diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CustomerAddressCreateDataValidator.php b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CustomerAddressCreateDataValidator.php
new file mode 100644
index 000000000000..65672bcd3503
--- /dev/null
+++ b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CustomerAddressCreateDataValidator.php
@@ -0,0 +1,56 @@
+getAllowedAddressAttributes = $getAllowedAddressAttributes;
+ }
+
+ /**
+ * Validate customer address create data
+ *
+ * @param array $addressData
+ * @return void
+ * @throws GraphQlInputException
+ */
+ public function validate(array $addressData): void
+ {
+ $attributes = $this->getAllowedAddressAttributes->execute();
+ $errorInput = [];
+
+ foreach ($attributes as $attributeName => $attributeInfo) {
+ if ($attributeInfo->getIsRequired()
+ && (!isset($addressData[$attributeName]) || empty($addressData[$attributeName]))
+ ) {
+ $errorInput[] = $attributeName;
+ }
+ }
+
+ if ($errorInput) {
+ throw new GraphQlInputException(
+ __('Required parameters are missing: %1', [implode(', ', $errorInput)])
+ );
+ }
+ }
+}
diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CustomerAddressDataProvider.php b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CustomerAddressDataProvider.php
new file mode 100644
index 000000000000..9640953032ac
--- /dev/null
+++ b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CustomerAddressDataProvider.php
@@ -0,0 +1,127 @@
+serviceOutputProcessor = $serviceOutputProcessor;
+ $this->jsonSerializer = $jsonSerializer;
+ $this->customerResourceModel = $customerResourceModel;
+ $this->customerFactory = $customerFactory;
+ }
+
+ /**
+ * Curate shipping and billing default options
+ *
+ * @param array $address
+ * @param AddressInterface $addressObject
+ * @return array
+ */
+ private function curateAddressDefaultValues(array $address, AddressInterface $addressObject) : array
+ {
+ $customerModel = $this->customerFactory->create();
+ $this->customerResourceModel->load($customerModel, $addressObject->getCustomerId());
+ $address[CustomerInterface::DEFAULT_BILLING] =
+ ($customerModel->getDefaultBillingAddress()
+ && $addressObject->getId() == $customerModel->getDefaultBillingAddress()->getId());
+ $address[CustomerInterface::DEFAULT_SHIPPING] =
+ ($customerModel->getDefaultShippingAddress()
+ && $addressObject->getId() == $customerModel->getDefaultShippingAddress()->getId());
+ return $address;
+ }
+
+ /**
+ * Transform single customer address data from object to in array format
+ *
+ * @param AddressInterface $addressObject
+ * @return array
+ */
+ public function getAddressData(AddressInterface $addressObject): array
+ {
+ $address = $this->serviceOutputProcessor->process(
+ $addressObject,
+ AddressRepositoryInterface::class,
+ 'getById'
+ );
+ $address = $this->curateAddressDefaultValues($address, $addressObject);
+
+ if (isset($address[CustomAttributesDataInterface::EXTENSION_ATTRIBUTES_KEY])) {
+ $address = array_merge($address, $address[CustomAttributesDataInterface::EXTENSION_ATTRIBUTES_KEY]);
+ }
+ $customAttributes = [];
+ if (isset($address[CustomAttributesDataInterface::CUSTOM_ATTRIBUTES])) {
+ foreach ($address[CustomAttributesDataInterface::CUSTOM_ATTRIBUTES] as $attribute) {
+ $isArray = false;
+ if (is_array($attribute['value'])) {
+ $isArray = true;
+ foreach ($attribute['value'] as $attributeValue) {
+ if (is_array($attributeValue)) {
+ $customAttributes[$attribute['attribute_code']] = $this->jsonSerializer->serialize(
+ $attribute['value']
+ );
+ continue;
+ }
+ $customAttributes[$attribute['attribute_code']] = implode(',', $attribute['value']);
+ continue;
+ }
+ }
+ if ($isArray) {
+ continue;
+ }
+ $customAttributes[$attribute['attribute_code']] = $attribute['value'];
+ }
+ }
+ $address = array_merge($address, $customAttributes);
+
+ return $address;
+ }
+}
diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CustomerAddressUpdateDataValidator.php b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CustomerAddressUpdateDataValidator.php
new file mode 100644
index 000000000000..13716b491fdd
--- /dev/null
+++ b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CustomerAddressUpdateDataValidator.php
@@ -0,0 +1,56 @@
+getAllowedAddressAttributes = $getAllowedAddressAttributes;
+ }
+
+ /**
+ * Validate customer address update data
+ *
+ * @param array $addressData
+ * @return void
+ * @throws GraphQlInputException
+ */
+ public function validate(array $addressData): void
+ {
+ $attributes = $this->getAllowedAddressAttributes->execute();
+ $errorInput = [];
+
+ foreach ($attributes as $attributeName => $attributeInfo) {
+ if ($attributeInfo->getIsRequired()
+ && (isset($addressData[$attributeName]) && empty($addressData[$attributeName]))
+ ) {
+ $errorInput[] = $attributeName;
+ }
+ }
+
+ if ($errorInput) {
+ throw new GraphQlInputException(
+ __('Required parameters are missing: %1', [implode(', ', $errorInput)])
+ );
+ }
+ }
+}
diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/GetAllowedAddressAttributes.php b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/GetAllowedAddressAttributes.php
new file mode 100644
index 000000000000..87be76073238
--- /dev/null
+++ b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/GetAllowedAddressAttributes.php
@@ -0,0 +1,49 @@
+eavConfig = $eavConfig;
+ }
+
+ /**
+ * Get allowed address attributes
+ *
+ * @return AbstractAttribute[]
+ */
+ public function execute(): array
+ {
+ $attributes = $this->eavConfig->getEntityAttributes(
+ AddressMetadataManagementInterface::ENTITY_TYPE_ADDRESS
+ );
+ foreach ($attributes as $attributeCode => $attribute) {
+ if (false === $attribute->getIsVisibleOnFront()) {
+ unset($attributes[$attributeCode]);
+ }
+ }
+ return $attributes;
+ }
+}
diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/GetCustomerAddressForUser.php b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/GetCustomerAddressForUser.php
new file mode 100644
index 000000000000..f7323402a6c6
--- /dev/null
+++ b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/GetCustomerAddressForUser.php
@@ -0,0 +1,61 @@
+addressRepository = $addressRepository;
+ }
+
+ /**
+ * Get customer address for user
+ *
+ * @param int $addressId
+ * @param int $userId
+ * @return AddressInterface
+ * @throws GraphQlAuthorizationException
+ * @throws GraphQlNoSuchEntityException
+ */
+ public function execute(int $addressId, int $userId): AddressInterface
+ {
+ try {
+ /** @var AddressInterface $address */
+ $address = $this->addressRepository->getById($addressId);
+ } catch (NoSuchEntityException $e) {
+ throw new GraphQlNoSuchEntityException(
+ __('Address id %1 does not exist.', [$addressId])
+ );
+ }
+
+ if ($address->getCustomerId() != $userId) {
+ throw new GraphQlAuthorizationException(
+ __('Current customer does not have permission to address id %1', [$addressId])
+ );
+ }
+ return $address;
+ }
+}
diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/CustomerDataProvider.php b/app/code/Magento/CustomerGraphQl/Model/Customer/CustomerDataProvider.php
index 646d426050b7..c8382593eab2 100644
--- a/app/code/Magento/CustomerGraphQl/Model/Customer/CustomerDataProvider.php
+++ b/app/code/Magento/CustomerGraphQl/Model/Customer/CustomerDataProvider.php
@@ -70,6 +70,25 @@ public function getCustomerById(int $customerId): array
return $this->processCustomer($customer);
}
+ /**
+ * Curate default shipping and default billing keys
+ *
+ * @param array $arrayAddress
+ * @return array
+ */
+ private function curateAddressData(array $arrayAddress) : array
+ {
+ foreach ($arrayAddress as $key => $address) {
+ if (!isset($address['default_shipping'])) {
+ $arrayAddress[$key]['default_shipping'] = false;
+ }
+ if (!isset($address['default_billing'])) {
+ $arrayAddress[$key]['default_billing'] = false;
+ }
+ }
+ return $arrayAddress;
+ }
+
/**
* Transform single customer data from object to in array format
*
@@ -83,6 +102,7 @@ private function processCustomer(CustomerInterface $customer): array
CustomerRepositoryInterface::class,
'get'
);
+ $customerData['addresses'] = $this->curateAddressData($customerData['addresses']);
if (isset($customerData['extension_attributes'])) {
$customerData = array_merge($customerData, $customerData['extension_attributes']);
}
diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/UpdateAccountInformation.php b/app/code/Magento/CustomerGraphQl/Model/Customer/UpdateCustomerData.php
similarity index 97%
rename from app/code/Magento/CustomerGraphQl/Model/Customer/UpdateAccountInformation.php
rename to app/code/Magento/CustomerGraphQl/Model/Customer/UpdateCustomerData.php
index 36365f1910f2..18510b872e64 100644
--- a/app/code/Magento/CustomerGraphQl/Model/Customer/UpdateAccountInformation.php
+++ b/app/code/Magento/CustomerGraphQl/Model/Customer/UpdateCustomerData.php
@@ -15,9 +15,9 @@
use Magento\Store\Model\StoreManagerInterface;
/**
- * Update account information
+ * Update customer data
*/
-class UpdateAccountInformation
+class UpdateCustomerData
{
/**
* @var CustomerRepositoryInterface
diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomerAddress.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomerAddress.php
new file mode 100644
index 000000000000..823444e5a2d7
--- /dev/null
+++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomerAddress.php
@@ -0,0 +1,124 @@
+checkCustomerAccount = $checkCustomerAccount;
+ $this->addressRepository = $addressRepository;
+ $this->addressInterfaceFactory = $addressInterfaceFactory;
+ $this->customerAddressDataProvider = $customerAddressDataProvider;
+ $this->dataObjectHelper = $dataObjectHelper;
+ $this->customerAddressCreateDataValidator = $customerAddressCreateDataValidator;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function resolve(
+ Field $field,
+ $context,
+ ResolveInfo $info,
+ array $value = null,
+ array $args = null
+ ) {
+ $currentUserId = $context->getUserId();
+ $currentUserType = $context->getUserType();
+
+ $this->checkCustomerAccount->execute($currentUserId, $currentUserType);
+ $this->customerAddressCreateDataValidator->validate($args['input']);
+
+ $address = $this->createCustomerAddress((int)$currentUserId, $args['input']);
+ return $this->customerAddressDataProvider->getAddressData($address);
+ }
+
+ /**
+ * Create customer address
+ *
+ * @param int $customerId
+ * @param array $addressData
+ * @return AddressInterface
+ * @throws GraphQlInputException
+ */
+ private function createCustomerAddress(int $customerId, array $addressData) : AddressInterface
+ {
+ /** @var AddressInterface $address */
+ $address = $this->addressInterfaceFactory->create();
+ $this->dataObjectHelper->populateWithArray($address, $addressData, AddressInterface::class);
+ $address->setCustomerId($customerId);
+
+ try {
+ $address = $this->addressRepository->save($address);
+ } catch (InputException $e) {
+ throw new GraphQlInputException(__($e->getMessage()), $e);
+ }
+ return $address;
+ }
+}
diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/DeleteCustomerAddress.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/DeleteCustomerAddress.php
new file mode 100644
index 000000000000..084857c84d5a
--- /dev/null
+++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/DeleteCustomerAddress.php
@@ -0,0 +1,96 @@
+checkCustomerAccount = $checkCustomerAccount;
+ $this->addressRepository = $addressRepository;
+ $this->getCustomerAddressForUser = $getCustomerAddressForUser;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function resolve(
+ Field $field,
+ $context,
+ ResolveInfo $info,
+ array $value = null,
+ array $args = null
+ ) {
+ $currentUserId = $context->getUserId();
+ $currentUserType = $context->getUserType();
+
+ $this->checkCustomerAccount->execute($currentUserId, $currentUserType);
+
+ return $this->deleteCustomerAddress((int)$currentUserId, (int)$args['id']);
+ }
+
+ /**
+ * Delete customer address
+ *
+ * @param int $customerId
+ * @param int $addressId
+ * @return bool
+ * @throws GraphQlAuthorizationException
+ * @throws GraphQlNoSuchEntityException
+ */
+ private function deleteCustomerAddress($customerId, $addressId)
+ {
+ $address = $this->getCustomerAddressForUser->execute($addressId, $customerId);
+ if ($address->isDefaultBilling()) {
+ throw new GraphQlAuthorizationException(
+ __('Customer Address %1 is set as default billing address and can not be deleted', [$addressId])
+ );
+ }
+ if ($address->isDefaultShipping()) {
+ throw new GraphQlAuthorizationException(
+ __('Customer Address %1 is set as default shipping address and can not be deleted', [$addressId])
+ );
+ }
+ return $this->addressRepository->delete($address);
+ }
+}
diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomer.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomer.php
index 5dc857f3f178..50760d2e2e31 100644
--- a/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomer.php
+++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomer.php
@@ -9,7 +9,7 @@
use Magento\CustomerGraphQl\Model\Customer\ChangeSubscriptionStatus;
use Magento\CustomerGraphQl\Model\Customer\CheckCustomerAccount;
-use Magento\CustomerGraphQl\Model\Customer\UpdateAccountInformation;
+use Magento\CustomerGraphQl\Model\Customer\UpdateCustomerData;
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\CustomerGraphQl\Model\Customer\CustomerDataProvider;
@@ -27,9 +27,9 @@ class UpdateCustomer implements ResolverInterface
private $checkCustomerAccount;
/**
- * @var UpdateAccountInformation
+ * @var UpdateCustomerData
*/
- private $updateAccountInformation;
+ private $updateCustomerData;
/**
* @var ChangeSubscriptionStatus
@@ -43,18 +43,18 @@ class UpdateCustomer implements ResolverInterface
/**
* @param CheckCustomerAccount $checkCustomerAccount
- * @param UpdateAccountInformation $updateAccountInformation
+ * @param UpdateCustomerData $updateCustomerData
* @param ChangeSubscriptionStatus $changeSubscriptionStatus
* @param CustomerDataProvider $customerDataProvider
*/
public function __construct(
CheckCustomerAccount $checkCustomerAccount,
- UpdateAccountInformation $updateAccountInformation,
+ UpdateCustomerData $updateCustomerData,
ChangeSubscriptionStatus $changeSubscriptionStatus,
CustomerDataProvider $customerDataProvider
) {
$this->checkCustomerAccount = $checkCustomerAccount;
- $this->updateAccountInformation = $updateAccountInformation;
+ $this->updateCustomerData = $updateCustomerData;
$this->changeSubscriptionStatus = $changeSubscriptionStatus;
$this->customerDataProvider = $customerDataProvider;
}
@@ -79,7 +79,7 @@ public function resolve(
$this->checkCustomerAccount->execute($currentUserId, $currentUserType);
$currentUserId = (int)$currentUserId;
- $this->updateAccountInformation->execute($currentUserId, $args['input']);
+ $this->updateCustomerData->execute($currentUserId, $args['input']);
if (isset($args['input']['is_subscribed'])) {
$this->changeSubscriptionStatus->execute($currentUserId, (bool)$args['input']['is_subscribed']);
diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomerAddress.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomerAddress.php
new file mode 100644
index 000000000000..7bae40e4cc5d
--- /dev/null
+++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomerAddress.php
@@ -0,0 +1,114 @@
+checkCustomerAccount = $checkCustomerAccount;
+ $this->addressRepository = $addressRepository;
+ $this->customerAddressDataProvider = $customerAddressDataProvider;
+ $this->dataObjectHelper = $dataObjectHelper;
+ $this->customerAddressUpdateDataValidator = $customerAddressUpdateDataValidator;
+ $this->getCustomerAddressForUser = $getCustomerAddressForUser;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function resolve(
+ Field $field,
+ $context,
+ ResolveInfo $info,
+ array $value = null,
+ array $args = null
+ ) {
+ $currentUserId = $context->getUserId();
+ $currentUserType = $context->getUserType();
+
+ $this->checkCustomerAccount->execute($currentUserId, $currentUserType);
+ $this->customerAddressUpdateDataValidator->validate($args['input']);
+
+ $address = $this->updateCustomerAddress((int)$currentUserId, (int)$args['id'], $args['input']);
+ return $this->customerAddressDataProvider->getAddressData($address);
+ }
+
+ /**
+ * Update customer address
+ *
+ * @param int $customerId
+ * @param int $addressId
+ * @param array $addressData
+ * @return AddressInterface
+ */
+ private function updateCustomerAddress(int $customerId, int $addressId, array $addressData)
+ {
+ $address = $this->getCustomerAddressForUser->execute($addressId, $customerId);
+ $this->dataObjectHelper->populateWithArray($address, $addressData, AddressInterface::class);
+ return $this->addressRepository->save($address);
+ }
+}
diff --git a/app/code/Magento/CustomerGraphQl/composer.json b/app/code/Magento/CustomerGraphQl/composer.json
index 488013c1ee58..722aba3aa065 100644
--- a/app/code/Magento/CustomerGraphQl/composer.json
+++ b/app/code/Magento/CustomerGraphQl/composer.json
@@ -6,6 +6,7 @@
"php": "~7.1.3||~7.2.0",
"magento/module-customer": "*",
"magento/module-authorization": "*",
+ "magento/module-eav": "*",
"magento/module-newsletter": "*",
"magento/module-integration": "*",
"magento/module-store": "*",
diff --git a/app/code/Magento/CustomerGraphQl/etc/module.xml b/app/code/Magento/CustomerGraphQl/etc/module.xml
index bde93c627650..eeed4862bbbf 100644
--- a/app/code/Magento/CustomerGraphQl/etc/module.xml
+++ b/app/code/Magento/CustomerGraphQl/etc/module.xml
@@ -6,11 +6,5 @@
*/
-->
-
-
-
-
-
-
-
+
diff --git a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls
index ca4ac6cc245a..c92753b96225 100644
--- a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls
+++ b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls
@@ -10,6 +10,40 @@ type Mutation {
changeCustomerPassword(currentPassword: String!, newPassword: String!): Customer @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\ChangePassword") @doc(description:"Changes the password for the logged-in customer")
updateCustomer (input: UpdateCustomerInput!): UpdateCustomerOutput @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\UpdateCustomer") @doc(description:"Update the customer's personal information")
revokeCustomerToken: RevokeCustomerTokenOutput @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\RevokeCustomerToken") @doc(description:"Revoke the customer token")
+ createCustomerAddress(input: CustomerAddressInput!): CustomerAddress @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\CreateCustomerAddress") @doc(description: "Create customer address")
+ updateCustomerAddress(id: Int!, input: CustomerAddressInput): CustomerAddress @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\UpdateCustomerAddress") @doc(description: "Update customer address")
+ deleteCustomerAddress(id: Int!): Boolean @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\DeleteCustomerAddress") @doc(description: "Delete customer address")
+}
+
+input CustomerAddressInput {
+ firstname: String @doc(description: "The first name of the person associated with the shipping/billing address")
+ lastname: String @doc(description: "The family name of the person associated with the shipping/billing address")
+ company: String @doc(description: "The customer's company")
+ telephone: String @doc(description: "The telephone number")
+ street: [String] @doc(description: "An array of strings that define the street number and name")
+ 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")
+ 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")
+ middlename: String @doc(description: "The middle name of the person associated with the shipping/billing address")
+ prefix: String @doc(description: "An honorific, such as Dr., Mr., or Mrs.")
+ suffix: String @doc(description: "A value such as Sr., Jr., or III")
+ vat_id: String @doc(description: "The customer's Tax/VAT number (for corporate customers)")
+ custom_attributes: [CustomerAddressAttributeInput] @doc(description: "Address custom attributes")
+}
+
+input CustomerAddressRegionInput @doc(description: "CustomerAddressRegionInput defines the customer's state or province") {
+ region_code: String @doc(description: "The address region code")
+ region: String @doc(description: "The state or province name")
+ region_id: Int @doc(description: "Uniquely identifies the region")
+}
+
+input CustomerAddressAttributeInput {
+ attribute_code: String! @doc(description: "Attribute code")
+ value: String! @doc(description: "Attribute value")
}
type CustomerToken {
@@ -70,6 +104,8 @@ type CustomerAddress @doc(description: "CustomerAddress contains detailed inform
vat_id: String @doc(description: "The customer's Tax/VAT number (for corporate customers)")
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")
+ custom_attributes: [CustomerAddressAttribute] @doc(description: "Address custom attributes")
+ extension_attributes: [CustomerAddressAttribute] @doc(description: "Address extension attributes")
}
type CustomerAddressRegion @doc(description: "CustomerAddressRegion defines the customer's state or province") {
@@ -77,3 +113,256 @@ type CustomerAddressRegion @doc(description: "CustomerAddressRegion defines the
region: String @doc(description: "The state or province name")
region_id: Int @doc(description: "Uniquely identifies the region")
}
+
+type CustomerAddressAttribute {
+ attribute_code: String @doc(description: "Attribute code")
+ value: String @doc(description: "Attribute value")
+}
+
+enum CountryCodeEnum @doc(description: "The list of countries codes") {
+ AF @doc(description: "Afghanistan")
+ AX @doc(description: "Åland Islands")
+ AL @doc(description: "Albania")
+ DZ @doc(description: "Algeria")
+ AS @doc(description: "American Samoa")
+ AD @doc(description: "Andorra")
+ AO @doc(description: "Angola")
+ AI @doc(description: "Anguilla")
+ AQ @doc(description: "Antarctica")
+ AG @doc(description: "Antigua & Barbuda")
+ AR @doc(description: "Argentina")
+ AM @doc(description: "Armenia")
+ AW @doc(description: "Aruba")
+ AU @doc(description: "Australia")
+ AT @doc(description: "Austria")
+ AZ @doc(description: "Azerbaijan")
+ BS @doc(description: "Bahamas")
+ BH @doc(description: "Bahrain")
+ BD @doc(description: "Bangladesh")
+ BB @doc(description: "Barbados")
+ BY @doc(description: "Belarus")
+ BE @doc(description: "Belgium")
+ BZ @doc(description: "Belize")
+ BJ @doc(description: "Benin")
+ BM @doc(description: "Bermuda")
+ BT @doc(description: "Bhutan")
+ BO @doc(description: "Bolivia")
+ BA @doc(description: "Bosnia & Herzegovina")
+ BW @doc(description: "Botswana")
+ BV @doc(description: "Bouvet Island")
+ BR @doc(description: "Brazil")
+ IO @doc(description: "British Indian Ocean Territory")
+ VG @doc(description: "British Virgin Islands")
+ BN @doc(description: "Brunei")
+ BG @doc(description: "Bulgaria")
+ BF @doc(description: "Burkina Faso")
+ BI @doc(description: "Burundi")
+ KH @doc(description: "Cambodia")
+ CM @doc(description: "Cameroon")
+ CA @doc(description: "Canada")
+ CV @doc(description: "Cape Verde")
+ KY @doc(description: "Cayman Islands")
+ CF @doc(description: "Central African Republic")
+ TD @doc(description: "Chad")
+ CL @doc(description: "Chile")
+ CN @doc(description: "China")
+ CX @doc(description: "Christmas Island")
+ CC @doc(description: "Cocos (Keeling) Islands")
+ CO @doc(description: "Colombia")
+ KM @doc(description: "Comoros")
+ CG @doc(description: "Congo -Brazzaville")
+ CD @doc(description: "Congo - Kinshasa")
+ CK @doc(description: "Cook Islands")
+ CR @doc(description: "Costa Rica")
+ CI @doc(description: "Côte d’Ivoire")
+ HR @doc(description: "Croatia")
+ CU @doc(description: "Cuba")
+ CY @doc(description: "Cyprus")
+ CZ @doc(description: "Czech Republic")
+ DK @doc(description: "Denmark")
+ DJ @doc(description: "Djibouti")
+ DM @doc(description: "Dominica")
+ DO @doc(description: "Dominican Republic")
+ EC @doc(description: "Ecuador")
+ EG @doc(description: "Egypt")
+ SV @doc(description: "El Salvador")
+ GQ @doc(description: "Equatorial Guinea")
+ ER @doc(description: "Eritrea")
+ EE @doc(description: "Estonia")
+ ET @doc(description: "Ethiopia")
+ FK @doc(description: "Falkland Islands")
+ FO @doc(description: "Faroe Islands")
+ FJ @doc(description: "Fiji")
+ FI @doc(description: "Finland")
+ FR @doc(description: "France")
+ GF @doc(description: "French Guiana")
+ PF @doc(description: "French Polynesia")
+ TF @doc(description: "French Southern Territories")
+ GA @doc(description: "Gabon")
+ GM @doc(description: "Gambia")
+ GE @doc(description: "Georgia")
+ DE @doc(description: "Germany")
+ GH @doc(description: "Ghana")
+ GI @doc(description: "Gibraltar")
+ GR @doc(description: "Greece")
+ GL @doc(description: "Greenland")
+ GD @doc(description: "Grenada")
+ GP @doc(description: "Guadeloupe")
+ GU @doc(description: "Guam")
+ GT @doc(description: "Guatemala")
+ GG @doc(description: "Guernsey")
+ GN @doc(description: "Guinea")
+ GW @doc(description: "Guinea-Bissau")
+ GY @doc(description: "Guyana")
+ HT @doc(description: "Haiti")
+ HM @doc(description: "Heard & McDonald Islands")
+ HN @doc(description: "Honduras")
+ HK @doc(description: "Hong Kong SAR China")
+ HU @doc(description: "Hungary")
+ IS @doc(description: "Iceland")
+ IN @doc(description: "India")
+ ID @doc(description: "Indonesia")
+ IR @doc(description: "Iran")
+ IQ @doc(description: "Iraq")
+ IE @doc(description: "Ireland")
+ IM @doc(description: "Isle of Man")
+ IL @doc(description: "Israel")
+ IT @doc(description: "Italy")
+ JM @doc(description: "Jamaica")
+ JP @doc(description: "Japan")
+ JE @doc(description: "Jersey")
+ JO @doc(description: "Jordan")
+ KZ @doc(description: "Kazakhstan")
+ KE @doc(description: "Kenya")
+ KI @doc(description: "Kiribati")
+ KW @doc(description: "Kuwait")
+ KG @doc(description: "Kyrgyzstan")
+ LA @doc(description: "Laos")
+ LV @doc(description: "Latvia")
+ LB @doc(description: "Lebanon")
+ LS @doc(description: "Lesotho")
+ LR @doc(description: "Liberia")
+ LY @doc(description: "Libya")
+ LI @doc(description: "Liechtenstein")
+ LT @doc(description: "Lithuania")
+ LU @doc(description: "Luxembourg")
+ MO @doc(description: "Macau SAR China")
+ MK @doc(description: "Macedonia")
+ MG @doc(description: "Madagascar")
+ MW @doc(description: "Malawi")
+ MY @doc(description: "Malaysia")
+ MV @doc(description: "Maldives")
+ ML @doc(description: "Mali")
+ MT @doc(description: "Malta")
+ MH @doc(description: "Marshall Islands")
+ MQ @doc(description: "Martinique")
+ MR @doc(description: "Mauritania")
+ MU @doc(description: "Mauritius")
+ YT @doc(description: "Mayotte")
+ MX @doc(description: "Mexico")
+ FM @doc(description: "Micronesia")
+ MD @doc(description: "Moldova")
+ MC @doc(description: "Monaco")
+ MN @doc(description: "Mongolia")
+ ME @doc(description: "Montenegro")
+ MS @doc(description: "Montserrat")
+ MA @doc(description: "Morocco")
+ MZ @doc(description: "Mozambique")
+ MM @doc(description: "Myanmar (Burma)")
+ NA @doc(description: "Namibia")
+ NR @doc(description: "Nauru")
+ NP @doc(description: "Nepal")
+ NL @doc(description: "Netherlands")
+ AN @doc(description: "Netherlands Antilles")
+ NC @doc(description: "New Caledonia")
+ NZ @doc(description: "New Zealand")
+ NI @doc(description: "Nicaragua")
+ NE @doc(description: "Niger")
+ NG @doc(description: "Nigeria")
+ NU @doc(description: "Niue")
+ NF @doc(description: "Norfolk Island")
+ MP @doc(description: "Northern Mariana Islands")
+ KP @doc(description: "North Korea")
+ NO @doc(description: "Norway")
+ OM @doc(description: "Oman")
+ PK @doc(description: "Pakistan")
+ PW @doc(description: "Palau")
+ PS @doc(description: "Palestinian Territories")
+ PA @doc(description: "Panama")
+ PG @doc(description: "Papua New Guinea")
+ PY @doc(description: "Paraguay")
+ PE @doc(description: "Peru")
+ PH @doc(description: "Philippines")
+ PN @doc(description: "Pitcairn Islands")
+ PL @doc(description: "Poland")
+ PT @doc(description: "Portugal")
+ QA @doc(description: "Qatar")
+ RE @doc(description: "Réunion")
+ RO @doc(description: "Romania")
+ RU @doc(description: "Russia")
+ RW @doc(description: "Rwanda")
+ WS @doc(description: "Samoa")
+ SM @doc(description: "San Marino")
+ ST @doc(description: "São Tomé & Príncipe")
+ SA @doc(description: "Saudi Arabia")
+ SN @doc(description: "Senegal")
+ RS @doc(description: "Serbia")
+ SC @doc(description: "Seychelles")
+ SL @doc(description: "Sierra Leone")
+ SG @doc(description: "Singapore")
+ SK @doc(description: "Slovakia")
+ SI @doc(description: "Slovenia")
+ SB @doc(description: "Solomon Islands")
+ SO @doc(description: "Somalia")
+ ZA @doc(description: "South Africa")
+ GS @doc(description: "South Georgia & South Sandwich Islands")
+ KR @doc(description: "South Korea")
+ ES @doc(description: "Spain")
+ LK @doc(description: "Sri Lanka")
+ BL @doc(description: "St. Barthélemy")
+ SH @doc(description: "St. Helena")
+ KN @doc(description: "St. Kitts & Nevis")
+ LC @doc(description: "St. Lucia")
+ MF @doc(description: "St. Martin")
+ PM @doc(description: "St. Pierre & Miquelon")
+ VC @doc(description: "St. Vincent & Grenadines")
+ SD @doc(description: "Sudan")
+ SR @doc(description: "Suriname")
+ SJ @doc(description: "Svalbard & Jan Mayen")
+ SZ @doc(description: "Swaziland")
+ SE @doc(description: "Sweden")
+ CH @doc(description: "Switzerland")
+ SY @doc(description: "Syria")
+ TW @doc(description: "Taiwan")
+ TJ @doc(description: "Tajikistan")
+ TZ @doc(description: "Tanzania")
+ TH @doc(description: "Thailand")
+ TL @doc(description: "Timor-Leste")
+ TG @doc(description: "Togo")
+ TK @doc(description: "Tokelau")
+ TO @doc(description: "Tonga")
+ TT @doc(description: "Trinidad & Tobago")
+ TN @doc(description: "Tunisia")
+ TR @doc(description: "Turkey")
+ TM @doc(description: "Turkmenistan")
+ TC @doc(description: "Turks & Caicos Islands")
+ TV @doc(description: "Tuvalu")
+ UG @doc(description: "Uganda")
+ UA @doc(description: "Ukraine")
+ AE @doc(description: "United Arab Emirates")
+ GB @doc(description: "United Kingdom")
+ US @doc(description: "United States")
+ UY @doc(description: "Uruguay")
+ UM @doc(description: "U.S. Outlying Islands")
+ VI @doc(description: "U.S. Virgin Islands")
+ UZ @doc(description: "Uzbekistan")
+ VU @doc(description: "Vanuatu")
+ VA @doc(description: "Vatican City")
+ VE @doc(description: "Venezuela")
+ VN @doc(description: "Vietnam")
+ WF @doc(description: "Wallis & Futuna")
+ EH @doc(description: "Western Sahara")
+ YE @doc(description: "Yemen")
+ ZM @doc(description: "Zambia")
+ ZW @doc(description: "Zimbabwe")
+}
\ No newline at end of file
diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php b/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php
index 9c50d4b85578..c3207bf478bb 100644
--- a/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php
+++ b/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php
@@ -8,7 +8,7 @@
namespace Magento\QuoteGraphQl\Model\Cart;
use Magento\Framework\Exception\NoSuchEntityException;
-use Magento\Framework\GraphQl\Exception\GraphQlAuthenticationException;
+use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException;
use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException;
use Magento\Quote\Api\CartRepositoryInterface;
use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface;
@@ -47,7 +47,7 @@ public function __construct(
* @param string $cartHash
* @param int|null $userId
* @return Quote
- * @throws GraphQlAuthenticationException
+ * @throws GraphQlAuthorizationException
* @throws GraphQlNoSuchEntityException
*/
public function execute(string $cartHash, ?int $userId): Quote
@@ -77,7 +77,7 @@ public function execute(string $cartHash, ?int $userId): Quote
}
if ($customerId !== $userId) {
- throw new GraphQlAuthenticationException(
+ throw new GraphQlAuthorizationException(
__(
'The current user cannot perform operations on cart "%masked_cart_id"',
['masked_cart_id' => $cartHash]
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerChangePasswordTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/ChangeCustomerPasswordTest.php
similarity index 98%
rename from dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerChangePasswordTest.php
rename to dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/ChangeCustomerPasswordTest.php
index f24518181521..f4e96e49a58e 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerChangePasswordTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/ChangeCustomerPasswordTest.php
@@ -14,7 +14,7 @@
use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\TestCase\GraphQlAbstract;
-class CustomerChangePasswordTest extends GraphQlAbstract
+class ChangeCustomerPasswordTest extends GraphQlAbstract
{
/**
* @var AccountManagementInterface
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerAddressTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerAddressTest.php
new file mode 100644
index 000000000000..602d969924fb
--- /dev/null
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerAddressTest.php
@@ -0,0 +1,247 @@
+customerTokenService = Bootstrap::getObjectManager()->get(CustomerTokenServiceInterface::class);
+ $this->addressRepository = Bootstrap::getObjectManager()->get(AddressRepositoryInterface::class);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer_without_addresses.php
+ * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+ */
+ public function testCreateCustomerAddress()
+ {
+ $customerId = 1;
+ $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
+ = <<graphQlQuery($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password));
+ $this->assertArrayHasKey('createCustomerAddress', $response);
+ $this->assertArrayHasKey('customer_id', $response['createCustomerAddress']);
+ $this->assertEquals($customerId, $response['createCustomerAddress']['customer_id']);
+ $this->assertArrayHasKey('id', $response['createCustomerAddress']);
+
+ $address = $this->addressRepository->getById($response['createCustomerAddress']['id']);
+ $this->assertEquals($address->getId(), $response['createCustomerAddress']['id']);
+ $this->assertCustomerAddressesFields($address, $response['createCustomerAddress']);
+ $this->assertCustomerAddressesFields($address, $newAddress);
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage The current customer isn't authorized.
+ */
+ public function testCreateCustomerAddressIfUserIsNotAuthorized()
+ {
+ $mutation
+ = <<graphQlQuery($mutation);
+ }
+
+ /**
+ * Verify customers with valid credentials create new address
+ * with missing required Firstname attribute
+ *
+ * @magentoApiDataFixture Magento/Customer/_files/customer_without_addresses.php
+ * @expectedException \Exception
+ * @expectedExceptionMessage Required parameters are missing: firstname
+ */
+ public function testCreateCustomerAddressWithMissingAttribute()
+ {
+ $mutation
+ = <<graphQlQuery($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password));
+ }
+
+ /**
+ * Verify the fields for Customer address
+ *
+ * @param AddressInterface $address
+ * @param array $actualResponse
+ */
+ private function assertCustomerAddressesFields(AddressInterface $address, array $actualResponse): void
+ {
+ /** @var $addresses */
+ $assertionMap = [
+ ['response_field' => 'country_id', '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()],
+ ['response_field' => 'fax', 'expected_value' => $address->getFax()],
+ ['response_field' => 'postcode', 'expected_value' => $address->getPostcode()],
+ ['response_field' => 'city', 'expected_value' => $address->getCity()],
+ ['response_field' => 'firstname', 'expected_value' => $address->getFirstname()],
+ ['response_field' => 'lastname', 'expected_value' => $address->getLastname()],
+ ['response_field' => 'middlename', 'expected_value' => $address->getMiddlename()],
+ ['response_field' => 'prefix', 'expected_value' => $address->getPrefix()],
+ ['response_field' => 'suffix', 'expected_value' => $address->getSuffix()],
+ ['response_field' => 'vat_id', 'expected_value' => $address->getVatId()],
+ ['response_field' => 'default_shipping', 'expected_value' => (bool)$address->isDefaultShipping()],
+ ['response_field' => 'default_billing', 'expected_value' => (bool)$address->isDefaultBilling()],
+ ];
+ $this->assertResponseFields($actualResponse, $assertionMap);
+ $this->assertTrue(is_array([$actualResponse['region']]), "region field must be of an array type.");
+ $assertionRegionMap = [
+ ['response_field' => 'region', 'expected_value' => $address->getRegion()->getRegion()],
+ ['response_field' => 'region_code', 'expected_value' => $address->getRegion()->getRegionCode()],
+ ['response_field' => 'region_id', 'expected_value' => $address->getRegion()->getRegionId()]
+ ];
+ $this->assertResponseFields($actualResponse['region'], $assertionRegionMap);
+ }
+
+ /**
+ * @param string $email
+ * @param string $password
+ * @return array
+ */
+ private function getCustomerAuthHeaders(string $email, string $password): array
+ {
+ $customerToken = $this->customerTokenService->createCustomerAccessToken($email, $password);
+ return ['Authorization' => 'Bearer ' . $customerToken];
+ }
+}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/DeleteCustomerAddressTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/DeleteCustomerAddressTest.php
new file mode 100644
index 000000000000..ba0232020298
--- /dev/null
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/DeleteCustomerAddressTest.php
@@ -0,0 +1,160 @@
+customerTokenService = Bootstrap::getObjectManager()->get(CustomerTokenServiceInterface::class);
+ $this->customerRepository = Bootstrap::getObjectManager()->get(CustomerRepositoryInterface::class);
+ $this->addressRepository = Bootstrap::getObjectManager()->get(AddressRepositoryInterface::class);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ * @magentoApiDataFixture Magento/Customer/_files/customer_two_addresses.php
+ */
+ public function testDeleteCustomerAddress()
+ {
+ $userName = 'customer@example.com';
+ $password = 'password';
+ $addressId = 2;
+
+ $mutation
+ = <<graphQlQuery($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password));
+ $this->assertArrayHasKey('deleteCustomerAddress', $response);
+ $this->assertEquals(true, $response['deleteCustomerAddress']);
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage The current customer isn't authorized.
+ */
+ public function testDeleteCustomerAddressIfUserIsNotAuthorized()
+ {
+ $addressId = 1;
+ $mutation
+ = <<graphQlQuery($mutation);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ * @magentoApiDataFixture Magento/Customer/_files/customer_two_addresses.php
+ *
+ * @expectedException \Exception
+ * @expectedExceptionMessage Customer Address 2 is set as default shipping address and can not be deleted
+ */
+ public function testDeleteDefaultShippingCustomerAddress()
+ {
+ $userName = 'customer@example.com';
+ $password = 'password';
+ $addressId = 2;
+
+ $address = $this->addressRepository->getById($addressId);
+ $address->setIsDefaultShipping(true);
+ $this->addressRepository->save($address);
+
+ $mutation
+ = <<graphQlQuery($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password));
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ * @magentoApiDataFixture Magento/Customer/_files/customer_two_addresses.php
+ *
+ * @expectedException \Exception
+ * @expectedExceptionMessage Customer Address 2 is set as default billing address and can not be deleted
+ */
+ public function testDeleteDefaultBillingCustomerAddress()
+ {
+ $userName = 'customer@example.com';
+ $password = 'password';
+ $addressId = 2;
+
+ $address = $this->addressRepository->getById($addressId);
+ $address->setIsDefaultBilling(true);
+ $this->addressRepository->save($address);
+
+ $mutation
+ = <<graphQlQuery($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password));
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ *
+ * @expectedException \Exception
+ * @expectedExceptionMessage Address id 9999 does not exist.
+ */
+ public function testDeleteNonExistCustomerAddress()
+ {
+ $userName = 'customer@example.com';
+ $password = 'password';
+ $mutation
+ = <<graphQlQuery($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password));
+ }
+
+ /**
+ * @param string $email
+ * @param string $password
+ * @return array
+ */
+ private function getCustomerAuthHeaders(string $email, string $password): array
+ {
+ $customerToken = $this->customerTokenService->createCustomerAccessToken($email, $password);
+ return ['Authorization' => 'Bearer ' . $customerToken];
+ }
+}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/AddressesTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/GetAddressesTest.php
similarity index 66%
rename from dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/AddressesTest.php
rename to dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/GetAddressesTest.php
index 9b7e3f28327d..53eb80335ff2 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/AddressesTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/GetAddressesTest.php
@@ -14,7 +14,7 @@
use Magento\TestFramework\TestCase\GraphQlAbstract;
use Magento\Integration\Api\CustomerTokenServiceInterface;
-class AddressesTest extends GraphQlAbstract
+class GetAddressesTest extends GraphQlAbstract
{
/**
* @magentoApiDataFixture Magento/Customer/_files/customer.php
@@ -25,20 +25,9 @@ public function testGetCustomerWithAddresses()
$query
= <<assertCustomerFields($customer, $response['customer']);
+ self::assertEquals($customer->getId(), $response['customer']['id']);
$this->assertCustomerAddressesFields($customer, $response);
}
- /**
- * Verify the all the whitelisted fields for a Customer Object
- *
- * @param CustomerInterface $customer
- * @param $actualResponse
- */
- public function assertCustomerFields($customer, $actualResponse)
- {
- // ['customer_object_field_name', 'expected_value']
- $assertionMap = [
- ['response_field' => 'id', 'expected_value' => $customer->getId()],
- ['response_field' => 'created_at', 'expected_value' => $customer->getCreatedAt()],
- ['response_field' => 'group_id', 'expected_value' => $customer->getGroupId()],
- ['response_field' => 'prefix', 'expected_value' => $customer->getPrefix()],
- ['response_field' => 'firstname', 'expected_value' => $customer->getFirstname()],
- ['response_field' => 'middlename', 'expected_value' => $customer->getMiddlename()],
- ['response_field' => 'lastname', 'expected_value' => $customer->getLastname()],
- ['response_field' => 'suffix', 'expected_value' => $customer->getSuffix()],
- ['response_field' => 'email', 'expected_value' => $customer->getEmail()],
- ['response_field' => 'default_shipping', 'expected_value' => (bool)$customer->getDefaultShipping()],
- ['response_field' => 'default_billing', 'expected_value' => (bool)$customer->getDefaultBilling()],
- ['response_field' => 'id', 'expected_value' => $customer->getId()]
- ];
-
- $this->assertResponseFields($actualResponse, $assertionMap);
- }
-
/**
* Verify the fields for CustomerAddress object
*
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/GetCustomerTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/GetCustomerTest.php
new file mode 100644
index 000000000000..928a263e8531
--- /dev/null
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/GetCustomerTest.php
@@ -0,0 +1,129 @@
+customerTokenService = Bootstrap::getObjectManager()->get(CustomerTokenServiceInterface::class);
+ $this->customerRegistry = Bootstrap::getObjectManager()->get(CustomerRegistry::class);
+ $this->customerAuthUpdate = Bootstrap::getObjectManager()->get(CustomerAuthUpdate::class);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ */
+ public function testGetCustomer()
+ {
+ $currentEmail = 'customer@example.com';
+ $currentPassword = 'password';
+
+ $query = <<graphQlQuery($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword));
+
+ $this->assertEquals('John', $response['customer']['firstname']);
+ $this->assertEquals('Smith', $response['customer']['lastname']);
+ $this->assertEquals($currentEmail, $response['customer']['email']);
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage The current customer isn't authorized.
+ */
+ public function testGetCustomerIfUserIsNotAuthorized()
+ {
+ $query = <<graphQlQuery($query);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ * @expectedException \Exception
+ * @expectedExceptionMessage The account is locked.
+ */
+ public function testGetCustomerIfAccountIsLocked()
+ {
+ $this->lockCustomer(1);
+
+ $currentEmail = 'customer@example.com';
+ $currentPassword = 'password';
+
+ $query = <<graphQlQuery($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword));
+ }
+
+ /**
+ * @param string $email
+ * @param string $password
+ * @return array
+ */
+ private function getCustomerAuthHeaders(string $email, string $password): array
+ {
+ $customerToken = $this->customerTokenService->createCustomerAccessToken($email, $password);
+ return ['Authorization' => 'Bearer ' . $customerToken];
+ }
+
+ /**
+ * @param int $customerId
+ * @return void
+ */
+ private function lockCustomer(int $customerId): void
+ {
+ $customerSecure = $this->customerRegistry->retrieveSecureData($customerId);
+ $customerSecure->setLockExpires('2030-12-31 00:00:00');
+ $this->customerAuthUpdate->saveAuth($customerId);
+ }
+}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerAddressTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerAddressTest.php
new file mode 100644
index 000000000000..519fe2b1405a
--- /dev/null
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerAddressTest.php
@@ -0,0 +1,240 @@
+customerTokenService = Bootstrap::getObjectManager()->get(CustomerTokenServiceInterface::class);
+ $this->customerRepository = Bootstrap::getObjectManager()->get(CustomerRepositoryInterface::class);
+ $this->addressRepository = Bootstrap::getObjectManager()->get(AddressRepositoryInterface::class);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ * @magentoApiDataFixture Magento/Customer/_files/customer_address.php
+ * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+ */
+ public function testUpdateCustomerAddress()
+ {
+ $userName = 'customer@example.com';
+ $password = 'password';
+ $customerId = 1;
+ $addressId = 1;
+
+ $updateAddress = [
+ 'region' => [
+ 'region' => 'Alaska',
+ 'region_id' => 2,
+ 'region_code' => 'AK'
+ ],
+ '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' => true
+ ];
+ $defaultShippingText = $updateAddress['default_shipping'] ? "true": "false";
+ $defaultBillingText = $updateAddress['default_billing'] ? "true": "false";
+
+ $mutation
+ = <<graphQlQuery($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password));
+ $this->assertArrayHasKey('updateCustomerAddress', $response);
+ $this->assertArrayHasKey('customer_id', $response['updateCustomerAddress']);
+ $this->assertEquals($customerId, $response['updateCustomerAddress']['customer_id']);
+ $this->assertArrayHasKey('id', $response['updateCustomerAddress']);
+
+ $address = $this->addressRepository->getById($addressId);
+ $this->assertEquals($address->getId(), $response['updateCustomerAddress']['id']);
+ $this->assertCustomerAddressesFields($address, $response['updateCustomerAddress']);
+ $this->assertCustomerAddressesFields($address, $updateAddress);
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage The current customer isn't authorized.
+ */
+ public function testUpdateCustomerAddressIfUserIsNotAuthorized()
+ {
+ $addressId = 1;
+ $mutation
+ = <<graphQlQuery($mutation);
+ }
+
+ /**
+ * Verify customers with credentials update address
+ * with missing required Firstname attribute
+ *
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ * @magentoApiDataFixture Magento/Customer/_files/customer_address.php
+ * @expectedException \Exception
+ * @expectedExceptionMessage Required parameters are missing: firstname
+ */
+ public function testUpdateCustomerAddressWithMissingAttribute()
+ {
+ $userName = 'customer@example.com';
+ $password = 'password';
+ $addressId = 1;
+
+ $mutation
+ = <<graphQlQuery($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password));
+ }
+
+ /**
+ * Verify the fields for Customer address
+ *
+ * @param AddressInterface $address
+ * @param array $actualResponse
+ */
+ private function assertCustomerAddressesFields(AddressInterface $address, $actualResponse): void
+ {
+ /** @var $addresses */
+ $assertionMap = [
+ ['response_field' => 'country_id', '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()],
+ ['response_field' => 'fax', 'expected_value' => $address->getFax()],
+ ['response_field' => 'postcode', 'expected_value' => $address->getPostcode()],
+ ['response_field' => 'city', 'expected_value' => $address->getCity()],
+ ['response_field' => 'firstname', 'expected_value' => $address->getFirstname()],
+ ['response_field' => 'lastname', 'expected_value' => $address->getLastname()],
+ ['response_field' => 'middlename', 'expected_value' => $address->getMiddlename()],
+ ['response_field' => 'prefix', 'expected_value' => $address->getPrefix()],
+ ['response_field' => 'suffix', 'expected_value' => $address->getSuffix()],
+ ['response_field' => 'vat_id', 'expected_value' => $address->getVatId()],
+ ['response_field' => 'default_shipping', 'expected_value' => (bool)$address->isDefaultShipping()],
+ ['response_field' => 'default_billing', 'expected_value' => (bool)$address->isDefaultBilling()],
+ ];
+ $this->assertResponseFields($actualResponse, $assertionMap);
+ $this->assertTrue(is_array([$actualResponse['region']]), "region field must be of an array type.");
+ // https://github.com/magento/graphql-ce/issues/270
+// $assertionRegionMap = [
+// ['response_field' => 'region', 'expected_value' => $address->getRegion()->getRegion()],
+// ['response_field' => 'region_code', 'expected_value' => $address->getRegion()->getRegionCode()],
+// ['response_field' => 'region_id', 'expected_value' => $address->getRegion()->getRegionId()]
+// ];
+// $this->assertResponseFields($actualResponse['region'], $assertionRegionMap);
+ }
+
+ /**
+ * @param string $email
+ * @param string $password
+ * @return array
+ */
+ private function getCustomerAuthHeaders(string $email, string $password): array
+ {
+ $customerToken = $this->customerTokenService->createCustomerAccessToken($email, $password);
+ return ['Authorization' => 'Bearer ' . $customerToken];
+ }
+}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/AccountInformationTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerTest.php
similarity index 73%
rename from dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/AccountInformationTest.php
rename to dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerTest.php
index 942e321a7871..c11c1385f741 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/AccountInformationTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerTest.php
@@ -7,26 +7,19 @@
namespace Magento\GraphQl\Customer;
-use Magento\Customer\Api\AccountManagementInterface;
-use Magento\Customer\Api\CustomerRepositoryInterface;
use Magento\Customer\Model\CustomerAuthUpdate;
use Magento\Customer\Model\CustomerRegistry;
use Magento\Integration\Api\CustomerTokenServiceInterface;
use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\TestCase\GraphQlAbstract;
-class AccountInformationTest extends GraphQlAbstract
+class UpdateCustomerTest extends GraphQlAbstract
{
/**
* @var CustomerTokenServiceInterface
*/
private $customerTokenService;
- /**
- * @var AccountManagementInterface
- */
- private $accountManagement;
-
/**
* @var CustomerRegistry
*/
@@ -37,92 +30,19 @@ class AccountInformationTest extends GraphQlAbstract
*/
private $customerAuthUpdate;
- /**
- * @var CustomerRepositoryInterface
- */
- private $customerRepository;
-
protected function setUp()
{
parent::setUp();
$this->customerTokenService = Bootstrap::getObjectManager()->get(CustomerTokenServiceInterface::class);
- $this->accountManagement = Bootstrap::getObjectManager()->get(AccountManagementInterface::class);
$this->customerRegistry = Bootstrap::getObjectManager()->get(CustomerRegistry::class);
$this->customerAuthUpdate = Bootstrap::getObjectManager()->get(CustomerAuthUpdate::class);
- $this->customerRepository = Bootstrap::getObjectManager()->get(CustomerRepositoryInterface::class);
- }
-
- /**
- * @magentoApiDataFixture Magento/Customer/_files/customer.php
- */
- public function testGetAccountInformation()
- {
- $currentEmail = 'customer@example.com';
- $currentPassword = 'password';
-
- $query = <<graphQlQuery($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword));
-
- $this->assertEquals('John', $response['customer']['firstname']);
- $this->assertEquals('Smith', $response['customer']['lastname']);
- $this->assertEquals($currentEmail, $response['customer']['email']);
- }
-
- /**
- * @expectedException \Exception
- * @expectedExceptionMessage The current customer isn't authorized.
- */
- public function testGetAccountInformationIfUserIsNotAuthorized()
- {
- $query = <<graphQlQuery($query);
- }
-
- /**
- * @magentoApiDataFixture Magento/Customer/_files/customer.php
- * @expectedException \Exception
- * @expectedExceptionMessage The account is locked.
- */
- public function testGetAccountInformationIfCustomerIsLocked()
- {
- $this->lockCustomer(1);
-
- $currentEmail = 'customer@example.com';
- $currentPassword = 'password';
-
- $query = <<graphQlQuery($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword));
}
/**
* @magentoApiDataFixture Magento/Customer/_files/customer.php
*/
- public function testUpdateAccountInformation()
+ public function testUpdateCustomer()
{
$currentEmail = 'customer@example.com';
$currentPassword = 'password';
@@ -161,7 +81,7 @@ public function testUpdateAccountInformation()
* @expectedException \Exception
* @expectedExceptionMessage "input" value should be specified
*/
- public function testUpdateAccountInformationIfInputDataIsEmpty()
+ public function testUpdateCustomerIfInputDataIsEmpty()
{
$currentEmail = 'customer@example.com';
$currentPassword = 'password';
@@ -186,7 +106,7 @@ public function testUpdateAccountInformationIfInputDataIsEmpty()
* @expectedException \Exception
* @expectedExceptionMessage The current customer isn't authorized.
*/
- public function testUpdateAccountInformationIfUserIsNotAuthorized()
+ public function testUpdateCustomerIfUserIsNotAuthorized()
{
$newFirstname = 'Richard';
@@ -211,7 +131,7 @@ public function testUpdateAccountInformationIfUserIsNotAuthorized()
* @expectedException \Exception
* @expectedExceptionMessage The account is locked.
*/
- public function testUpdateAccountInformationIfCustomerIsLocked()
+ public function testUpdateCustomerIfAccountIsLocked()
{
$this->lockCustomer(1);
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_two_addresses_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_two_addresses_rollback.php
new file mode 100644
index 000000000000..414d31ac9d0a
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_two_addresses_rollback.php
@@ -0,0 +1,32 @@
+get(Registry::class);
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+/** @var AddressRepositoryInterface $addressRepository */
+$addressRepository = Bootstrap::getObjectManager()->get(AddressRepositoryInterface::class);
+
+foreach ([1, 2] as $addressId) {
+ try {
+ $addressRepository->deleteById($addressId);
+ } catch (NoSuchEntityException $e) {
+ /**
+ * Tests which are wrapped with MySQL transaction clear all data by transaction rollback.
+ */
+ }
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_without_addresses.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_without_addresses.php
new file mode 100644
index 000000000000..0948e4617361
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_without_addresses.php
@@ -0,0 +1,31 @@
+get(CustomerRegistry::class);
+/** @var Customer $customer */
+$customer = Bootstrap::getObjectManager()->get(Customer::class);
+
+$customer->setWebsiteId(1)
+ ->setId(1)
+ ->setEmail('customer@example.com')
+ ->setPassword('password')
+ ->setGroupId(1)
+ ->setStoreId(1)
+ ->setIsActive(1)
+ ->setPrefix('Mr.')
+ ->setFirstname('John')
+ ->setMiddlename('A')
+ ->setLastname('Smith')
+ ->setSuffix('Esq.')
+ ->setGender(0)
+ ->save();
+$customerRegistry->remove($customer->getId());
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_without_addresses_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_without_addresses_rollback.php
new file mode 100644
index 000000000000..d5f9308ee77b
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_without_addresses_rollback.php
@@ -0,0 +1,34 @@
+get(CustomerRegistry::class);
+/** @var CustomerRepositoryInterface $customerRepository */
+$customerRepository = Bootstrap::getObjectManager()->get(CustomerRepositoryInterface::class);
+
+/** @var Registry $registry */
+$registry = Bootstrap::getObjectManager()->get(Registry::class);
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+try {
+ $customerRepository->deleteById(1);
+} catch (NoSuchEntityException $e) {
+ /**
+ * Tests which are wrapped with MySQL transaction clear all data by transaction rollback.
+ */
+}
+$customerRegistry->remove(1);
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);