-
Notifications
You must be signed in to change notification settings - Fork 22
Add the built-in WP REST API validation to CountryCodeTrait
to avoid errors when clearing all audience countries in the onboarding flow
#2003
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
d45a0b5
6ffdce2
2babb97
e0c5b51
fbef461
6dba607
bdfb365
4eeffe8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ | |
use Automattic\WooCommerce\GoogleListingsAndAds\Google\GoogleHelperAwareTrait; | ||
use Automattic\WooCommerce\GoogleListingsAndAds\HelperTraits\ISO3166Awareness; | ||
use Automattic\WooCommerce\GoogleListingsAndAds\Vendor\League\ISO3166\Exception\OutOfBoundsException; | ||
use WP_REST_Request as Request; | ||
use Exception; | ||
use Throwable; | ||
|
||
|
@@ -35,24 +36,29 @@ protected function validate_country_code( string $country ): void { | |
} | ||
|
||
/** | ||
* Validate that a country or a list of countries is valid and supported. | ||
* Validate that a country or a list of countries is valid and supported, | ||
* and also validate the data by the built-in validation of WP REST API with parameter’s schema. | ||
* | ||
* @param mixed $countries An individual string or an array of strings. | ||
* @param bool $check_supported_country Whether to check the country is supported. | ||
* @param bool $check_supported_country Whether to check the country is supported. | ||
* @param mixed $countries An individual string or an array of strings. | ||
* @param Request $request The request to validate. | ||
* @param string $param The parameter name, used in error messages. | ||
* | ||
* @return mixed | ||
* @throws Exception When the country is not supported. | ||
* @throws OutOfBoundsException When the country code cannot be found. | ||
*/ | ||
protected function validate_country_codes( $countries, bool $check_supported_country ) { | ||
protected function validate_country_codes( bool $check_supported_country, $countries, $request, $param ) { | ||
$valid = rest_validate_request_arg( $countries, $request, $param ); | ||
|
||
if ( true !== $valid ) { | ||
return $valid; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd suggest renaming this as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Renamed in bdfb365. |
||
|
||
try { | ||
// This is used for individual strings and an array of strings. | ||
$countries = (array) $countries; | ||
|
||
if ( empty( $countries ) ) { | ||
throw new Exception( __( 'No countries provided.', 'google-listings-and-ads' ) ); | ||
} | ||
|
||
foreach ( $countries as $country ) { | ||
$this->validate_country_code( $country ); | ||
if ( $check_supported_country ) { | ||
|
@@ -91,24 +97,26 @@ protected function get_country_code_sanitize_callback(): callable { | |
} | ||
|
||
/** | ||
* Get a callable function for validating that a provided country code is recognized. | ||
* Get a callable function for validating that a provided country code is recognized | ||
* and fulfilled the given parameter's schema. | ||
* | ||
* @return callable | ||
*/ | ||
protected function get_country_code_validate_callback(): callable { | ||
return function( $value ) { | ||
return $this->validate_country_codes( $value, false ); | ||
return function( ...$args ) { | ||
return $this->validate_country_codes( false, ...$args ); | ||
}; | ||
} | ||
|
||
/** | ||
* Get a callable function for validating that a provided country code is recognized and supported. | ||
* Get a callable function for validating that a provided country code is recognized, supported, | ||
* and fulfilled the given parameter's schema.. | ||
* | ||
* @return callable | ||
*/ | ||
protected function get_supported_country_code_validate_callback(): callable { | ||
return function( $value ) { | ||
return $this->validate_country_codes( $value, true ); | ||
return function( ...$args ) { | ||
return $this->validate_country_codes( true, ...$args ); | ||
}; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
<?php | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe We can add some tests for:
|
||
|
||
namespace Automattic\WooCommerce\GoogleListingsAndAds\Tests\Unit\API\Site\Controllers\MerchantCenter; | ||
|
||
use Automattic\WooCommerce\GoogleListingsAndAds\API\Site\Controllers\MerchantCenter\ShippingTimeBatchController; | ||
use Automattic\WooCommerce\GoogleListingsAndAds\API\TransportMethods; | ||
use Automattic\WooCommerce\GoogleListingsAndAds\Proxies\RESTServer; | ||
use Automattic\WooCommerce\GoogleListingsAndAds\Tests\Framework\RESTControllerUnitTest; | ||
use Automattic\WooCommerce\GoogleListingsAndAds\Vendor\League\Container\Container; | ||
use Automattic\WooCommerce\GoogleListingsAndAds\Vendor\League\ISO3166\ISO3166DataProvider; | ||
use PHPUnit\Framework\MockObject\MockObject; | ||
use WP_REST_Response as Response; | ||
|
||
/** | ||
* Class ShippingTimeBatchControllerTest | ||
* | ||
* @package Automattic\WooCommerce\GoogleListingsAndAds\Tests\Unit\API\Site\Controllers\MerchantCenter | ||
*/ | ||
class ShippingTimeBatchControllerTest extends RESTControllerUnitTest { | ||
|
||
/** @var MockObject|Container $container */ | ||
protected $container; | ||
|
||
/** @var MockObject|ISO3166DataProvider $iso_provider */ | ||
protected $iso_provider; | ||
|
||
/** @var ShippingTimeBatchController $controller */ | ||
protected $controller; | ||
|
||
protected const ROUTE_SHIPPING_TIME_BATCH = '/wc/gla/mc/shipping/times/batch'; | ||
|
||
/** | ||
* Runs before each test is executed. | ||
*/ | ||
public function setUp(): void { | ||
parent::setUp(); | ||
|
||
$this->container = $this->createMock( Container::class ); | ||
$this->iso_provider = $this->createMock( ISO3166DataProvider::class ); | ||
|
||
$this->container->method( 'get' )->willReturn( $this->server ); | ||
|
||
$this->server->register_route( | ||
'/wc/gla', | ||
'/mc/shipping/times', | ||
[ | ||
'methods' => TransportMethods::CREATABLE, | ||
'callback' => function( $request ) { | ||
return new Response( $request->get_param( 'country_code' ), 201 ); | ||
}, | ||
] | ||
); | ||
|
||
$this->controller = new ShippingTimeBatchController( $this->container ); | ||
|
||
$this->controller->set_iso3166_provider( $this->iso_provider ); | ||
$this->controller->register(); | ||
} | ||
|
||
/** | ||
* Test a successful dispatch request for the batch creation of shipping times. | ||
*/ | ||
public function test_create_shipping_time_batch() { | ||
$payload = [ | ||
'country_codes' => [ 'US', 'GB' ], | ||
'time' => 5, | ||
]; | ||
|
||
$response = $this->do_request( self::ROUTE_SHIPPING_TIME_BATCH, 'POST', $payload ); | ||
|
||
$expected = [ | ||
'errors' => [], | ||
'success' => $payload['country_codes'], | ||
]; | ||
|
||
$this->assertEquals( $expected, $response->get_data() ); | ||
$this->assertEquals( 201, $response->get_status() ); | ||
} | ||
|
||
/** | ||
* Test a failed batch creation of shipping times with empty country codes. | ||
*/ | ||
public function test_create_shipping_time_batch_empty_country_codes() { | ||
$payload = [ | ||
'country_codes' => [], | ||
]; | ||
|
||
$response = $this->do_request( self::ROUTE_SHIPPING_TIME_BATCH, 'POST', $payload ); | ||
|
||
$this->assertEquals( 'rest_invalid_param', $response->get_data()['code'] ); | ||
$this->assertEquals( 'Invalid parameter(s): country_codes', $response->get_data()['message'] ); | ||
$this->assertEquals( 400, $response->get_status() ); | ||
} | ||
|
||
/** | ||
* Test a failed batch creation of shipping times with duplicate country codes. | ||
*/ | ||
public function test_create_shipping_time_batch_duplicate_country_codes() { | ||
$payload = [ | ||
'country_codes' => [ 'US', 'GB', 'US' ], | ||
]; | ||
|
||
$response = $this->do_request( self::ROUTE_SHIPPING_TIME_BATCH, 'POST', $payload ); | ||
|
||
$this->assertEquals( 'rest_invalid_param', $response->get_data()['code'] ); | ||
$this->assertEquals( 'Invalid parameter(s): country_codes', $response->get_data()['message'] ); | ||
$this->assertEquals( 400, $response->get_status() ); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
<?php | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
namespace Automattic\WooCommerce\GoogleListingsAndAds\Tests\Unit\API\Site\Controllers\MerchantCenter; | ||
|
||
use Automattic\WooCommerce\GoogleListingsAndAds\API\Site\Controllers\MerchantCenter\TargetAudienceController; | ||
use Automattic\WooCommerce\GoogleListingsAndAds\Google\GoogleHelper; | ||
use Automattic\WooCommerce\GoogleListingsAndAds\Options\OptionsInterface; | ||
use Automattic\WooCommerce\GoogleListingsAndAds\Proxies\WC; | ||
use Automattic\WooCommerce\GoogleListingsAndAds\Proxies\WP; | ||
use Automattic\WooCommerce\GoogleListingsAndAds\Shipping\ShippingZone; | ||
use Automattic\WooCommerce\GoogleListingsAndAds\Tests\Framework\RESTControllerUnitTest; | ||
use Automattic\WooCommerce\GoogleListingsAndAds\Vendor\League\ISO3166\ISO3166DataProvider; | ||
use PHPUnit\Framework\MockObject\MockObject; | ||
|
||
/** | ||
* Class TargetAudienceControllerTest | ||
* | ||
* @package Automattic\WooCommerce\GoogleListingsAndAds\Tests\Unit\API\Site\Controllers\MerchantCenter | ||
*/ | ||
class TargetAudienceControllerTest extends RESTControllerUnitTest { | ||
|
||
/** @var WP $wp */ | ||
protected $wp; | ||
|
||
/** @var WC $wc */ | ||
protected $wc; | ||
|
||
/** @var MockObject|ShippingZone $shipping_zone */ | ||
protected $shipping_zone; | ||
|
||
/** @var MockObject|ISO3166DataProvider $iso_provider */ | ||
protected $iso_provider; | ||
|
||
/** @var MockObject|GoogleHelper $google_helper */ | ||
protected $google_helper; | ||
|
||
/** @var MockObject|OptionsInterface $options */ | ||
protected $options; | ||
|
||
/** @var TargetAudienceController $controller */ | ||
protected $controller; | ||
|
||
protected const ROUTE_TARGET_AUDIENCE = '/wc/gla/mc/target_audience'; | ||
|
||
/** | ||
* Runs before each test is executed. | ||
*/ | ||
public function setUp(): void { | ||
parent::setUp(); | ||
|
||
$this->wp = $this->createMock( WP::class ); | ||
$this->wc = $this->createMock( WC::class ); | ||
$this->shipping_zone = $this->createMock( ShippingZone::class ); | ||
$this->iso_provider = $this->createMock( ISO3166DataProvider::class ); | ||
$this->google_helper = $this->createMock( GoogleHelper::class ); | ||
$this->options = $this->createMock( OptionsInterface::class ); | ||
|
||
$this->controller = new TargetAudienceController( $this->server, $this->wp, $this->wc, $this->shipping_zone, $this->google_helper ); | ||
|
||
$this->controller->set_iso3166_provider( $this->iso_provider ); | ||
$this->controller->set_options_object( $this->options ); | ||
$this->controller->register(); | ||
|
||
$this->google_helper->method( 'is_country_supported' )->willReturn( true ); | ||
} | ||
|
||
/** | ||
* Test a successful update of target audience. | ||
*/ | ||
public function test_update_target_audience() { | ||
$payload = [ | ||
'location' => 'selected', | ||
'countries' => [ 'US', 'GB' ], | ||
]; | ||
|
||
$this->options->expects( $this->once() ) | ||
->method( 'update' ) | ||
->with( OptionsInterface::TARGET_AUDIENCE, $payload ); | ||
|
||
$response = $this->do_request( self::ROUTE_TARGET_AUDIENCE, 'POST', $payload ); | ||
|
||
$this->assertEquals( 'success', $response->get_data()['status'] ); | ||
$this->assertEquals( 201, $response->get_status() ); | ||
} | ||
|
||
/** | ||
* Test a successful update of target audience with empty country codes. | ||
*/ | ||
public function test_update_target_audience_empty_countries() { | ||
$payload = [ | ||
'location' => 'all', | ||
'countries' => [], | ||
]; | ||
|
||
$this->options->expects( $this->once() ) | ||
->method( 'update' ) | ||
->with( OptionsInterface::TARGET_AUDIENCE, $payload ); | ||
|
||
$response = $this->do_request( self::ROUTE_TARGET_AUDIENCE, 'POST', $payload ); | ||
|
||
$this->assertEquals( 'success', $response->get_data()['status'] ); | ||
$this->assertEquals( 201, $response->get_status() ); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can add somewhere a short explanation of your findings of why
rest_validate_request_arg
is not being called automatically.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added in 4eeffe8.