diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CreateCustomerAddress.php b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CreateCustomerAddress.php index 388b6dc2ea943..9637b3e555b8b 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CreateCustomerAddress.php +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CreateCustomerAddress.php @@ -67,6 +67,10 @@ public function __construct( */ public function execute(int $customerId, array $data): AddressInterface { + // It is needed because AddressInterface has country_id field. + if (isset($data['country_code'])) { + $data['country_id'] = $data['country_code']; + } $this->validateData($data); /** @var AddressInterface $address */ diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/ExtractCustomerAddressData.php b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/ExtractCustomerAddressData.php index 8741bff7aa88d..7992ca8342921 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/ExtractCustomerAddressData.php +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/ExtractCustomerAddressData.php @@ -127,6 +127,10 @@ public function execute(AddressInterface $address): array $addressData['customer_id'] = null; + if (isset($addressData['country_id'])) { + $addressData['country_code'] = $addressData['country_id']; + } + return $addressData; } } diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/UpdateCustomerAddress.php b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/UpdateCustomerAddress.php index 65745a20bc8eb..26e53c7c3a0a8 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/UpdateCustomerAddress.php +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/UpdateCustomerAddress.php @@ -66,6 +66,9 @@ public function __construct( */ public function execute(AddressInterface $address, array $data): void { + if (isset($data['country_code'])) { + $data['country_id'] = $data['country_code']; + } $this->validateData($data); $filteredData = array_diff_key($data, array_flip($this->restrictedKeys)); diff --git a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls index 793ff0954ee94..86ab39bbee25c 100644 --- a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls @@ -28,7 +28,8 @@ input CustomerAddressInput { city: String @doc(description: "The city or town") region: CustomerAddressRegionInput @doc(description: "An object containing the region name, region code, and region ID") postcode: String @doc(description: "The customer's ZIP or postal code") - country_id: CountryCodeEnum @doc(description: "The customer's country") + country_id: CountryCodeEnum @doc(description: "Deprecated: use `country_code` instead.") + country_code: CountryCodeEnum @doc(description: "The customer's country") default_shipping: Boolean @doc(description: "Indicates whether the address is the default shipping address") default_billing: Boolean @doc(description: "Indicates whether the address is the default billing address") fax: String @doc(description: "The fax number") @@ -102,7 +103,8 @@ type CustomerAddress @doc(description: "CustomerAddress contains detailed inform customer_id: Int @doc(description: "The customer ID") @deprecated(reason: "customer_id is not needed as part of CustomerAddress, address ID (id) is unique identifier for the addresses.") region: CustomerAddressRegion @doc(description: "An object containing the region name, region code, and region ID") region_id: Int @deprecated(reason: "Region ID is excessive on storefront and region code should suffice for all scenarios") - country_id: String @doc(description: "The customer's country") + country_id: String @doc(description: "The customer's country") @deprecated(reason: "Use `country_code` instead.") + country_code: CountryCodeEnum @doc(description: "The customer's country") street: [String] @doc(description: "An array of strings that define the street number and name") company: String @doc(description: "The customer's company") telephone: String @doc(description: "The telephone number") diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/AssignBillingAddressToCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/AssignBillingAddressToCart.php index dd6478b4873c6..95c46cdcca5dc 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/AssignBillingAddressToCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/AssignBillingAddressToCart.php @@ -7,7 +7,7 @@ namespace Magento\QuoteGraphQl\Model\Cart; -use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; @@ -52,7 +52,7 @@ public function execute( $this->billingAddressManagement->assign($cart->getId(), $billingAddress, $useForShipping); } catch (NoSuchEntityException $e) { throw new GraphQlNoSuchEntityException(__($e->getMessage()), $e); - } catch (LocalizedException $e) { + } catch (InputException $e) { throw new GraphQlInputException(__($e->getMessage()), $e); } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/AssignShippingAddressToCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/AssignShippingAddressToCart.php index 527999b245a4c..4dbcfad31e84c 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/AssignShippingAddressToCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/AssignShippingAddressToCart.php @@ -7,7 +7,7 @@ namespace Magento\QuoteGraphQl\Model\Cart; -use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; @@ -50,7 +50,7 @@ public function execute( $this->shippingAddressManagement->assign($cart->getId(), $shippingAddress); } catch (NoSuchEntityException $e) { throw new GraphQlNoSuchEntityException(__($e->getMessage()), $e); - } catch (LocalizedException $e) { + } catch (InputException $e) { throw new GraphQlInputException(__($e->getMessage()), $e); } } diff --git a/app/code/Magento/SalesGraphQl/etc/schema.graphqls b/app/code/Magento/SalesGraphQl/etc/schema.graphqls index a7c30f582e752..a687ee59031ea 100644 --- a/app/code/Magento/SalesGraphQl/etc/schema.graphqls +++ b/app/code/Magento/SalesGraphQl/etc/schema.graphqls @@ -7,7 +7,7 @@ type Query { type CustomerOrder @doc(description: "Order mapping fields") { id: Int - increment_id: String @deprecated(reason: "Use the order_number instaed.") + increment_id: String @deprecated(reason: "Use the order_number instead.") order_number: String! @doc(description: "The order number") created_at: String grand_total: Float diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerAddressTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerAddressTest.php index 15da8443b1787..408a254f65f2e 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerAddressTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerAddressTest.php @@ -49,7 +49,7 @@ public function testCreateCustomerAddress() 'region_id' => 4, 'region_code' => 'AZ' ], - 'country_id' => 'US', + 'country_code' => 'US', 'street' => ['Line 1 Street', 'Line 2'], 'company' => 'Company name', 'telephone' => '123456789', @@ -75,7 +75,7 @@ public function testCreateCustomerAddress() region_id: {$newAddress['region']['region_id']} region_code: "{$newAddress['region']['region_code']}" } - country_id: {$newAddress['country_id']} + country_code: {$newAddress['country_code']} street: ["{$newAddress['street'][0]}","{$newAddress['street'][1]}"] company: "{$newAddress['company']}" telephone: "{$newAddress['telephone']}" @@ -98,7 +98,7 @@ public function testCreateCustomerAddress() region_id region_code } - country_id + country_code street company telephone @@ -133,6 +133,75 @@ public function testCreateCustomerAddress() $this->assertCustomerAddressesFields($address, $newAddress); } + /** + * Test case for deprecated `country_id` field. + * + * @magentoApiDataFixture Magento/Customer/_files/customer_without_addresses.php + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testCreateCustomerAddressWithCountryId() + { + $newAddress = [ + 'region' => [ + 'region' => 'Arizona', + 'region_id' => 4, + 'region_code' => 'AZ' + ], + 'country_id' => 'US', + 'street' => ['Line 1 Street', 'Line 2'], + 'company' => 'Company name', + 'telephone' => '123456789', + 'fax' => '123123123', + 'postcode' => '7777', + 'city' => 'City Name', + 'firstname' => 'Adam', + 'lastname' => 'Phillis', + 'middlename' => 'A', + 'prefix' => 'Mr.', + 'suffix' => 'Jr.', + 'vat_id' => '1', + 'default_shipping' => true, + 'default_billing' => false + ]; + + $mutation + = <<graphQlMutation($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); + $this->assertArrayHasKey('createCustomerAddress', $response); + $this->assertEquals($newAddress['country_id'], $response['createCustomerAddress']['country_id']); + } + /** * @expectedException Exception * @expectedExceptionMessage The current customer isn't authorized. @@ -153,7 +222,7 @@ public function testCreateCustomerAddressIfUserIsNotAuthorized() region: { region_id: 1 } - country_id: US + country_code: US postcode: "9999" default_shipping: true default_billing: false @@ -182,7 +251,7 @@ public function testCreateCustomerAddressWithMissingAttribute() region: { region_id: 1 } - country_id: US + country_code: US street: ["Line 1 Street","Line 2"] company: "Company name" telephone: "123456789" @@ -235,7 +304,7 @@ public function testCreateCustomerAddressWithRedundantStreetLine() 'region_id' => 4, 'region_code' => 'AZ' ], - 'country_id' => 'US', + 'country_code' => 'US', 'street' => ['Line 1 Street', 'Line 2', 'Line 3'], 'company' => 'Company name', 'telephone' => '123456789', @@ -261,7 +330,7 @@ public function testCreateCustomerAddressWithRedundantStreetLine() region_id: {$newAddress['region']['region_id']} region_code: "{$newAddress['region']['region_code']}" } - country_id: {$newAddress['country_id']} + country_code: {$newAddress['country_code']} street: ["{$newAddress['street'][0]}","{$newAddress['street'][1]}","{$newAddress['street'][2]}"] company: "{$newAddress['company']}" telephone: "{$newAddress['telephone']}" @@ -334,12 +403,15 @@ public function invalidInputDataProvider() * * @param AddressInterface $address * @param array $actualResponse + * @param string $countryFieldName */ - private function assertCustomerAddressesFields(AddressInterface $address, array $actualResponse): void - { + private function assertCustomerAddressesFields( + AddressInterface $address, + array $actualResponse + ): void { /** @var $addresses */ $assertionMap = [ - ['response_field' => 'country_id', 'expected_value' => $address->getCountryId()], + ['response_field' => 'country_code', 'expected_value' => $address->getCountryId()], ['response_field' => 'street', 'expected_value' => $address->getStreet()], ['response_field' => 'company', 'expected_value' => $address->getCompany()], ['response_field' => 'telephone', 'expected_value' => $address->getTelephone()], diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerAddressTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerAddressTest.php index 625d027f58d24..e214d770920d0 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerAddressTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerAddressTest.php @@ -76,6 +76,56 @@ public function testUpdateCustomerAddress() $this->assertCustomerAddressesFields($address, $updateAddress); } + /** + * Test case for deprecated `country_id` field. + * + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Customer/_files/customer_address.php + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testUpdateCustomerAddressWithCountryId() + { + $userName = 'customer@example.com'; + $password = 'password'; + $addressId = 1; + + $updateAddress = $this->getAddressData(); + + $mutation = $mutation + = <<graphQlMutation($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); + $this->assertArrayHasKey('updateCustomerAddress', $response); + $this->assertEquals($updateAddress['country_code'], $response['updateCustomerAddress']['country_id']); + } + /** * @expectedException Exception * @expectedExceptionMessage The current customer isn't authorized. @@ -131,12 +181,15 @@ public function testUpdateCustomerAddressWithMissingAttribute() * * @param AddressInterface $address * @param array $actualResponse + * @param string $countryFieldName */ - private function assertCustomerAddressesFields(AddressInterface $address, $actualResponse): void - { + private function assertCustomerAddressesFields( + AddressInterface $address, + $actualResponse + ): void { /** @var $addresses */ $assertionMap = [ - ['response_field' => 'country_id', 'expected_value' => $address->getCountryId()], + ['response_field' => 'country_code', 'expected_value' => $address->getCountryId()], ['response_field' => 'street', 'expected_value' => $address->getStreet()], ['response_field' => 'company', 'expected_value' => $address->getCompany()], ['response_field' => 'telephone', 'expected_value' => $address->getTelephone()], @@ -187,7 +240,7 @@ public function testUpdateCustomerAddressWithMissingId() region_id: {$updateAddress['region']['region_id']} region_code: "{$updateAddress['region']['region_code']}" } - country_id: {$updateAddress['country_id']} + country_code: {$updateAddress['country_code']} street: ["{$updateAddress['street'][0]}","{$updateAddress['street'][1]}"] company: "{$updateAddress['company']}" telephone: "{$updateAddress['telephone']}" @@ -243,7 +296,7 @@ public function testUpdateCustomerAddressWithInvalidIdType() region_id: {$updateAddress['region']['region_id']} region_code: "{$updateAddress['region']['region_code']}" } - country_id: {$updateAddress['country_id']} + country_code: {$updateAddress['country_code']} street: ["{$updateAddress['street'][0]}","{$updateAddress['street'][1]}"] company: "{$updateAddress['company']}" telephone: "{$updateAddress['telephone']}" @@ -382,11 +435,11 @@ private function getAddressData(): array { return [ 'region' => [ - 'region' => 'Alaska', - 'region_id' => 2, - 'region_code' => 'AK' + 'region' => 'Alberta', + 'region_id' => 66, + 'region_code' => 'AB' ], - 'country_id' => 'US', + 'country_code' => 'CA', 'street' => ['Line 1 Street', 'Line 2'], 'company' => 'Company Name', 'telephone' => '123456789', @@ -423,7 +476,7 @@ private function getMutation(int $addressId): string region_id: {$updateAddress['region']['region_id']} region_code: "{$updateAddress['region']['region_code']}" } - country_id: {$updateAddress['country_id']} + country_code: {$updateAddress['country_code']} street: ["{$updateAddress['street'][0]}","{$updateAddress['street'][1]}"] company: "{$updateAddress['company']}" telephone: "{$updateAddress['telephone']}" @@ -446,7 +499,7 @@ private function getMutation(int $addressId): string region_id region_code } - country_id + country_code street company telephone diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php index dd8be1788c570..d4f23854378fa 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php @@ -879,6 +879,59 @@ public function testSetNewBillingAddressWithNotSaveInAddressBook() } } + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + */ + public function testWithInvalidBillingAddressInput() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + + $query = <<expectExceptionMessage('The address failed to save. Verify the address and try again.'); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + } + /** * Verify the all the whitelisted fields for a New Address Object * diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php index 42b662d264a91..8b1b678b0b3a4 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php @@ -269,7 +269,6 @@ public function testVerifyShippingAddressType() /** * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @magentoApiDataFixture Magento/Customer/_files/customer_address.php * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php @@ -710,6 +709,49 @@ public function testSetShippingAddressWithLowerCaseCountry() $this->assertEquals('CA', $address['region']['code']); } + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + */ + public function testWithInvalidShippingAddressesInput() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + + $query = <<expectExceptionMessage('The address failed to save. Verify the address and try again.'); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + } + /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php diff --git a/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlInputException.php b/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlInputException.php index 429b7c04b7475..28b91c753c7e7 100644 --- a/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlInputException.php +++ b/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlInputException.php @@ -7,13 +7,15 @@ namespace Magento\Framework\GraphQl\Exception; -use Magento\Framework\Exception\InputException; +use Magento\Framework\Exception\AggregateExceptionInterface; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Phrase; +use GraphQL\Error\ClientAware; /** * Exception for GraphQL to be thrown when user supplies invalid input */ -class GraphQlInputException extends InputException implements \GraphQL\Error\ClientAware +class GraphQlInputException extends LocalizedException implements AggregateExceptionInterface, ClientAware { const EXCEPTION_CATEGORY = 'graphql-input'; @@ -22,6 +24,13 @@ class GraphQlInputException extends InputException implements \GraphQL\Error\Cli */ private $isSafe; + /** + * The array of errors that have been added via the addError() method + * + * @var \Magento\Framework\Exception\LocalizedException[] + */ + private $errors = []; + /** * Initialize object * @@ -51,4 +60,26 @@ public function getCategory() : string { return self::EXCEPTION_CATEGORY; } + + /** + * Add child error if used as aggregate exception + * + * @param LocalizedException $exception + * @return $this + */ + public function addError(LocalizedException $exception): self + { + $this->errors[] = $exception; + return $this; + } + + /** + * Get child errors if used as aggregate exception + * + * @return LocalizedException[] + */ + public function getErrors(): array + { + return $this->errors; + } } diff --git a/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandler.php b/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandler.php index 2661034116f9d..b3d78790892f1 100644 --- a/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandler.php +++ b/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandler.php @@ -7,7 +7,7 @@ namespace Magento\Framework\GraphQl\Query; -use GraphQL\Error\ClientAware; +use Magento\Framework\Exception\AggregateExceptionInterface; use Psr\Log\LoggerInterface; /** @@ -36,13 +36,20 @@ public function __construct( */ public function handle(array $errors, callable $formatter): array { - return array_map( - function (ClientAware $error) use ($formatter) { - $this->logger->error($error); - - return $formatter($error); - }, - $errors - ); + $formattedErrors = []; + foreach ($errors as $error) { + $this->logger->error($error); + $previousError = $error->getPrevious(); + if ($previousError instanceof AggregateExceptionInterface && !empty($previousError->getErrors())) { + $aggregatedErrors = $previousError->getErrors(); + foreach ($aggregatedErrors as $aggregatedError) { + $this->logger->error($aggregatedError); + $formattedErrors[] = $formatter($aggregatedError); + } + } else { + $formattedErrors[] = $formatter($error); + } + } + return $formattedErrors; } }