From 09dc09c6052cdf27131086d63695ff3cb14bd962 Mon Sep 17 00:00:00 2001 From: Lyzun Oleksandr Date: Tue, 21 Aug 2018 19:44:43 +0200 Subject: [PATCH 01/15] Prototype of GraphQL mutations for customer update --- .../Model/Resolver/CustomerUpdate.php | 150 ++++++++++++++++++ .../CustomerGraphQl/etc/schema.graphqls | 11 ++ 2 files changed, 161 insertions(+) create mode 100644 app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php new file mode 100644 index 0000000000000..96fe852f700cf --- /dev/null +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php @@ -0,0 +1,150 @@ +customerResolver = $customerResolver; + $this->valueFactory = $valueFactory; + $this->customerRepository = $customerRepository; + $this->subscriberFactory = $subscriberFactory; + $this->storeResolver = $storeResolver; + $this->encryptor = $encryptor; + $this->customerRegistry = $customerRegistry; + } + + /** + * {@inheritdoc} + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) : Value { + + /** @var ContextInterface $context */ + if ((!$context->getUserId()) || $context->getUserType() == UserContextInterface::USER_TYPE_GUEST) { + throw new GraphQlAuthorizationException( + __( + 'Current customer does not have access to the resource "%1"', + [\Magento\Customer\Model\Customer::ENTITY] + ) + ); + } + + $customer = $this->customerRepository->getById($context->getUserId()); + + if (isset($args['email']) && $customer->getEmail() !== $args['email']) { + $customerSecure = $this->customerRegistry->retrieveSecureData($context->getUserId()); + $hash = $customerSecure->getPasswordHash(); + if (!$this->encryptor->validateHash($args['password'], $hash)) { + throw new GraphQlAuthorizationException(__('Invalid login or password.')); + } + $customer->setEmail($args['email']); + } + + if (isset($args['firstname'])) { + $customer->setFirstname($args['firstname']); + } + if (isset($args['lastname'])){ + $customer->setLastname($args['lastname']); + } + + $customer->setStoreId($this->storeResolver->getCurrentStoreId()); + $this->customerRepository->save($customer); + + if (isset($args['is_subscribed'])) { + + $checkSubscriber = $this->subscriberFactory->create()->loadByCustomerId($context->getUserId()); + + if ($args['is_subscribed'] === true && !$checkSubscriber->isSubscribed()) { + $this->subscriberFactory->create()->subscribeCustomerById($context->getUserId()); + } elseif ($args['is_subscribed'] === false && $checkSubscriber->isSubscribed()) { + $this->subscriberFactory->create()->unsubscribeCustomerById($context->getUserId()); + } + } + + + $data = $args; + $result = function () use ($data) { + return !empty($data) ? $data : []; + }; + + return $this->valueFactory->create($result); + + + } + +} diff --git a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls index 687826cff001d..f74b4f82f77f8 100644 --- a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls @@ -51,3 +51,14 @@ type CustomerAddressRegion @doc(description: "CustomerAddressRegion defines the region_id: Int @doc(description: "Uniquely identifies the region") } +type Mutation { + customerUpdate (firstname: String, lastname: String, email: String, password: String, is_subscribed: Boolean): customerItem @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\CustomerUpdate") @doc(description:"Update customer personal information") +} + +type customerItem { + firstname: String + lastname: String + email: String + password: String + is_subscribed: Boolean +} From ef7f5792bd5e05b27e7648f5d67cde10fb47110a Mon Sep 17 00:00:00 2001 From: Lyzun Oleksandr Date: Tue, 28 Aug 2018 08:35:23 +0200 Subject: [PATCH 02/15] Code alignments, formatting --- .../Model/Resolver/CustomerUpdate.php | 14 +------------- .../Magento/CustomerGraphQl/etc/schema.graphqls | 1 - 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php index 96fe852f700cf..5bce9dfa1d581 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php @@ -20,7 +20,6 @@ use Magento\Framework\Encryption\EncryptorInterface as Encryptor; use Magento\Customer\Model\CustomerRegistry; - /** * Customers field resolver, used for GraphQL request processing. */ @@ -46,11 +45,6 @@ class CustomerUpdate implements ResolverInterface */ protected $subscriberFactory; - /** - * @var AuthenticationInterface - */ - private $authentication; - /** * @var CustomerRegistry */ @@ -118,7 +112,7 @@ public function resolve( if (isset($args['firstname'])) { $customer->setFirstname($args['firstname']); } - if (isset($args['lastname'])){ + if (isset($args['lastname'])) { $customer->setLastname($args['lastname']); } @@ -126,9 +120,7 @@ public function resolve( $this->customerRepository->save($customer); if (isset($args['is_subscribed'])) { - $checkSubscriber = $this->subscriberFactory->create()->loadByCustomerId($context->getUserId()); - if ($args['is_subscribed'] === true && !$checkSubscriber->isSubscribed()) { $this->subscriberFactory->create()->subscribeCustomerById($context->getUserId()); } elseif ($args['is_subscribed'] === false && $checkSubscriber->isSubscribed()) { @@ -136,15 +128,11 @@ public function resolve( } } - $data = $args; $result = function () use ($data) { return !empty($data) ? $data : []; }; return $this->valueFactory->create($result); - - } - } diff --git a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls index f74b4f82f77f8..6683a511010b1 100644 --- a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls @@ -59,6 +59,5 @@ type customerItem { firstname: String lastname: String email: String - password: String is_subscribed: Boolean } From 89638111af1c607ef16a6e8e433f242ad7fb0262 Mon Sep 17 00:00:00 2001 From: Lyzun Oleksandr Date: Mon, 3 Sep 2018 16:28:08 +0200 Subject: [PATCH 03/15] Code refactoring --- .../Customer/CustomerDataProvider.php | 132 +++++++++++++++++- .../Model/Resolver/CustomerUpdate.php | 51 +------ 2 files changed, 132 insertions(+), 51 deletions(-) diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataProvider.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataProvider.php index 1a942a2ab149a..6b3c02ea98d41 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataProvider.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataProvider.php @@ -13,6 +13,11 @@ use Magento\Framework\Serialize\SerializerInterface; use Magento\Framework\Webapi\ServiceOutputProcessor; use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Newsletter\Model\SubscriberFactory; +use Magento\Customer\Model\CustomerRegistry; +use Magento\Framework\Encryption\EncryptorInterface as Encryptor; +use Magento\Store\Api\StoreResolverInterface; +use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; /** * Customer field data provider, used for GraphQL request processing. @@ -29,11 +34,31 @@ class CustomerDataProvider */ private $serviceOutputProcessor; + /** + * @var StoreResolverInterface + */ + private $storeResolver; + + /** + * @var \Magento\Newsletter\Model\SubscriberFactory + */ + protected $subscriberFactory; + + /** + * @var CustomerRegistry + */ + protected $customerRegistry; + /** * @var SerializerInterface */ private $jsonSerializer; + /** + * @var Encryptor + */ + protected $encryptor; + /** * @param CustomerRepositoryInterface $customerRepository * @param ServiceOutputProcessor $serviceOutputProcessor @@ -42,11 +67,32 @@ class CustomerDataProvider public function __construct( CustomerRepositoryInterface $customerRepository, ServiceOutputProcessor $serviceOutputProcessor, - SerializerInterface $jsonSerializer + SerializerInterface $jsonSerializer, + SubscriberFactory $subscriberFactory, + CustomerRegistry $customerRegistry, + Encryptor $encryptor, + StoreResolverInterface $storeResolver ) { $this->customerRepository = $customerRepository; $this->serviceOutputProcessor = $serviceOutputProcessor; $this->jsonSerializer = $jsonSerializer; + $this->subscriberFactory = $subscriberFactory; + $this->customerRegistry = $customerRegistry; + $this->encryptor = $encryptor; + $this->storeResolver = $storeResolver; + } + + /** + * Load customer object + * + * @param int $customerId + * @return CustomerInterface + * @throws LocalizedException + * @throws NoSuchEntityException + */ + public function loadCustomerById(int $customerId): CustomerInterface + { + return $this->customerRepository->getById($customerId); } /** @@ -56,7 +102,7 @@ public function __construct( * @return array * @throws NoSuchEntityException|LocalizedException */ - public function getCustomerById(int $customerId) : array + public function getCustomerById(int $customerId): array { try { $customerObject = $this->customerRepository->getById($customerId); @@ -73,7 +119,7 @@ public function getCustomerById(int $customerId) : array * @param CustomerInterface $customerObject * @return array */ - private function processCustomer(CustomerInterface $customerObject) : array + private function processCustomer(CustomerInterface $customerObject): array { $customer = $this->serviceOutputProcessor->process( $customerObject, @@ -110,4 +156,84 @@ private function processCustomer(CustomerInterface $customerObject) : array return $customer; } + + /** + * Check if customer is subscribed to Newsletter + * + * @param int $customerId + * @return bool + */ + public function isSubscribed(int $customerId): bool + { + $checkSubscriber = $this->subscriberFactory->create()->loadByCustomerId($customerId); + return $checkSubscriber->isSubscribed(); + } + + /** + * Manage customer subscription. Subscribe OR unsubscribe if required + * + * @param int $customerId + * @param $newSubscriptionStatus + * @return bool + */ + public function manageSubscription(int $customerId, bool $newSubscriptionStatus): bool + { + $checkSubscriber = $this->subscriberFactory->create()->loadByCustomerId($customerId); + $isSubscribed = $this->isSubscribed($customerId); + + if ($newSubscriptionStatus === true && !$isSubscribed) { + $this->subscriberFactory->create()->subscribeCustomerById($customerId); + } elseif ($newSubscriptionStatus === false && $checkSubscriber->isSubscribed()) { + $this->subscriberFactory->create()->unsubscribeCustomerById($customerId); + } + return true; + } + + /** + * @param int $customerId + * @param array $customerData + * @return CustomerInterface + * @throws LocalizedException + * @throws NoSuchEntityException + * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Exception\State\InputMismatchException + */ + public function updateAccountInformation(int $customerId, array $customerData): CustomerInterface + { + + $customer = $this->loadCustomerById($customerId); + + if (isset($customerData['email']) + && $customer->getEmail() !== $customerData['email'] + && isset($customerData['password'])) { + if ($this->isPasswordCorrect($customerData['password'], $customerId)) { + $customer->setEmail($customerData['email']); + } else { + throw new GraphQlAuthorizationException(__('Invalid current user password.')); + } + } + + if (isset($customerData['firstname'])) { + $customer->setFirstname($customerData['firstname']); + } + if (isset($customerData['lastname'])) { + $customer->setLastname($customerData['lastname']); + } + + $customer->setStoreId($this->storeResolver->getCurrentStoreId()); + $this->customerRepository->save($customer); + + return $customer; + } + + private function isPasswordCorrect(string $password, int $customerId) + { + + $customerSecure = $this->customerRegistry->retrieveSecureData($customerId); + $hash = $customerSecure->getPasswordHash(); + if (!$this->encryptor->validateHash($password, $hash)) { + return false; + } + return true; + } } diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php index 5bce9dfa1d581..51f62a7a8cafd 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php @@ -16,9 +16,6 @@ use Magento\Framework\GraphQl\Query\Resolver\Value; use Magento\Framework\GraphQl\Query\Resolver\ValueFactory; use Magento\Framework\GraphQl\Query\ResolverInterface; -use Magento\Store\Api\StoreResolverInterface; -use Magento\Framework\Encryption\EncryptorInterface as Encryptor; -use Magento\Customer\Model\CustomerRegistry; /** * Customers field resolver, used for GraphQL request processing. @@ -35,11 +32,6 @@ class CustomerUpdate implements ResolverInterface */ private $valueFactory; - /** - * @var StoreResolverInterface - */ - private $storeResolver; - /** * @var \Magento\Newsletter\Model\SubscriberFactory */ @@ -50,11 +42,6 @@ class CustomerUpdate implements ResolverInterface */ protected $customerRegistry; - /** - * @var Encryptor - */ - protected $encryptor; - /** * @param CustomerDataProvider $customerResolver * @param ValueFactory $valueFactory @@ -62,19 +49,11 @@ class CustomerUpdate implements ResolverInterface public function __construct( CustomerDataProvider $customerResolver, ValueFactory $valueFactory, - \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository, - \Magento\Newsletter\Model\SubscriberFactory $subscriberFactory, - StoreResolverInterface $storeResolver, - Encryptor $encryptor, - CustomerRegistry $customerRegistry + \Magento\Newsletter\Model\SubscriberFactory $subscriberFactory ) { $this->customerResolver = $customerResolver; $this->valueFactory = $valueFactory; - $this->customerRepository = $customerRepository; $this->subscriberFactory = $subscriberFactory; - $this->storeResolver = $storeResolver; - $this->encryptor = $encryptor; - $this->customerRegistry = $customerRegistry; } /** @@ -98,34 +77,10 @@ public function resolve( ); } - $customer = $this->customerRepository->getById($context->getUserId()); - - if (isset($args['email']) && $customer->getEmail() !== $args['email']) { - $customerSecure = $this->customerRegistry->retrieveSecureData($context->getUserId()); - $hash = $customerSecure->getPasswordHash(); - if (!$this->encryptor->validateHash($args['password'], $hash)) { - throw new GraphQlAuthorizationException(__('Invalid login or password.')); - } - $customer->setEmail($args['email']); - } - - if (isset($args['firstname'])) { - $customer->setFirstname($args['firstname']); - } - if (isset($args['lastname'])) { - $customer->setLastname($args['lastname']); - } - - $customer->setStoreId($this->storeResolver->getCurrentStoreId()); - $this->customerRepository->save($customer); + $this->customerResolver->updateAccountInformation($context->getUserId(), $args); if (isset($args['is_subscribed'])) { - $checkSubscriber = $this->subscriberFactory->create()->loadByCustomerId($context->getUserId()); - if ($args['is_subscribed'] === true && !$checkSubscriber->isSubscribed()) { - $this->subscriberFactory->create()->subscribeCustomerById($context->getUserId()); - } elseif ($args['is_subscribed'] === false && $checkSubscriber->isSubscribed()) { - $this->subscriberFactory->create()->unsubscribeCustomerById($context->getUserId()); - } + $this->customerResolver->manageSubscription($context->getUserId(), $args['is_subscribed']); } $data = $args; From d0dd90f99bc641b5f2c6766a36a25f4cfd6ecf19 Mon Sep 17 00:00:00 2001 From: Lyzun Oleksandr Date: Thu, 13 Sep 2018 14:54:43 +0200 Subject: [PATCH 04/15] Refactored Account Mutation --- .../Customer/CustomerDataProvider.php | 108 +----------- .../Resolver/Customer/CustomerDataUpdater.php | 116 +++++++++++++ .../Model/Resolver/CustomerUpdate.php | 15 +- .../Magento/CustomerGraphQl/composer.json | 1 + .../Customer/CustomerAccountEditTest.php | 157 ++++++++++++++++++ 5 files changed, 291 insertions(+), 106 deletions(-) create mode 100644 app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataUpdater.php create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAccountEditTest.php diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataProvider.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataProvider.php index 6b3c02ea98d41..17f40575ae3eb 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataProvider.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataProvider.php @@ -13,11 +13,8 @@ use Magento\Framework\Serialize\SerializerInterface; use Magento\Framework\Webapi\ServiceOutputProcessor; use Magento\Customer\Api\Data\CustomerInterface; -use Magento\Newsletter\Model\SubscriberFactory; use Magento\Customer\Model\CustomerRegistry; use Magento\Framework\Encryption\EncryptorInterface as Encryptor; -use Magento\Store\Api\StoreResolverInterface; -use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; /** * Customer field data provider, used for GraphQL request processing. @@ -34,16 +31,6 @@ class CustomerDataProvider */ private $serviceOutputProcessor; - /** - * @var StoreResolverInterface - */ - private $storeResolver; - - /** - * @var \Magento\Newsletter\Model\SubscriberFactory - */ - protected $subscriberFactory; - /** * @var CustomerRegistry */ @@ -68,31 +55,14 @@ public function __construct( CustomerRepositoryInterface $customerRepository, ServiceOutputProcessor $serviceOutputProcessor, SerializerInterface $jsonSerializer, - SubscriberFactory $subscriberFactory, CustomerRegistry $customerRegistry, - Encryptor $encryptor, - StoreResolverInterface $storeResolver + Encryptor $encryptor ) { $this->customerRepository = $customerRepository; $this->serviceOutputProcessor = $serviceOutputProcessor; $this->jsonSerializer = $jsonSerializer; - $this->subscriberFactory = $subscriberFactory; $this->customerRegistry = $customerRegistry; $this->encryptor = $encryptor; - $this->storeResolver = $storeResolver; - } - - /** - * Load customer object - * - * @param int $customerId - * @return CustomerInterface - * @throws LocalizedException - * @throws NoSuchEntityException - */ - public function loadCustomerById(int $customerId): CustomerInterface - { - return $this->customerRepository->getById($customerId); } /** @@ -158,82 +128,14 @@ private function processCustomer(CustomerInterface $customerObject): array } /** - * Check if customer is subscribed to Newsletter - * - * @param int $customerId - * @return bool - */ - public function isSubscribed(int $customerId): bool - { - $checkSubscriber = $this->subscriberFactory->create()->loadByCustomerId($customerId); - return $checkSubscriber->isSubscribed(); - } - - /** - * Manage customer subscription. Subscribe OR unsubscribe if required - * + * @param string $password * @param int $customerId - * @param $newSubscriptionStatus * @return bool - */ - public function manageSubscription(int $customerId, bool $newSubscriptionStatus): bool - { - $checkSubscriber = $this->subscriberFactory->create()->loadByCustomerId($customerId); - $isSubscribed = $this->isSubscribed($customerId); - - if ($newSubscriptionStatus === true && !$isSubscribed) { - $this->subscriberFactory->create()->subscribeCustomerById($customerId); - } elseif ($newSubscriptionStatus === false && $checkSubscriber->isSubscribed()) { - $this->subscriberFactory->create()->unsubscribeCustomerById($customerId); - } - return true; - } - - /** - * @param int $customerId - * @param array $customerData - * @return CustomerInterface - * @throws LocalizedException * @throws NoSuchEntityException - * @throws \Magento\Framework\Exception\InputException - * @throws \Magento\Framework\Exception\State\InputMismatchException */ - public function updateAccountInformation(int $customerId, array $customerData): CustomerInterface + public function isPasswordCorrect(string $password, int $customerId) { - - $customer = $this->loadCustomerById($customerId); - - if (isset($customerData['email']) - && $customer->getEmail() !== $customerData['email'] - && isset($customerData['password'])) { - if ($this->isPasswordCorrect($customerData['password'], $customerId)) { - $customer->setEmail($customerData['email']); - } else { - throw new GraphQlAuthorizationException(__('Invalid current user password.')); - } - } - - if (isset($customerData['firstname'])) { - $customer->setFirstname($customerData['firstname']); - } - if (isset($customerData['lastname'])) { - $customer->setLastname($customerData['lastname']); - } - - $customer->setStoreId($this->storeResolver->getCurrentStoreId()); - $this->customerRepository->save($customer); - - return $customer; - } - - private function isPasswordCorrect(string $password, int $customerId) - { - - $customerSecure = $this->customerRegistry->retrieveSecureData($customerId); - $hash = $customerSecure->getPasswordHash(); - if (!$this->encryptor->validateHash($password, $hash)) { - return false; - } - return true; + $hash = $this->customerRegistry->retrieveSecureData($customerId)->getPasswordHash(); + return $this->encryptor->validateHash($password, $hash); } } diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataUpdater.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataUpdater.php new file mode 100644 index 0000000000000..3fec957c163f8 --- /dev/null +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataUpdater.php @@ -0,0 +1,116 @@ +customerRepository = $customerRepository; + $this->storeResolver = $storeResolver; + $this->customerDataProvider = $customerDataProvider; + $this->subscriberFactory = $subscriberFactory; + } + + /** + * Manage customer subscription. Subscribe OR unsubscribe if required. Return new subscription status + * + * @param int $customerId + * @param $newSubscriptionStatus + * @return bool + */ + public function manageSubscription(int $customerId, bool $newSubscriptionStatus): bool + { + $subscriber = $this->subscriberFactory->create()->loadByCustomerId($customerId); + if ($newSubscriptionStatus === true && !$subscriber->isSubscribed()) { + $this->subscriberFactory->create()->subscribeCustomerById($customerId); + } elseif ($newSubscriptionStatus === false && $subscriber->isSubscribed()) { + $this->subscriberFactory->create()->unsubscribeCustomerById($customerId); + } + /** Load subscribed again to get his new status after update subscription */ + $subscriber = $this->subscriberFactory->create()->loadByCustomerId($customerId); + return $subscriber->isSubscribed(); + } + + /** + * @param int $customerId + * @param array $customerData + * @return CustomerInterface + * @throws GraphQlAuthorizationException + * @throws LocalizedException + * @throws NoSuchEntityException + * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Exception\State\InputMismatchException + */ + public function updateAccountInformation(int $customerId, array $customerData): CustomerInterface + { + $customer = $this->customerRepository->getById($customerId); + + if (isset($customerData['email']) + && $customer->getEmail() !== $customerData['email'] + && isset($customerData['password'])) { + if ($this->customerDataProvider->isPasswordCorrect($customerData['password'], $customerId)) { + $customer->setEmail($customerData['email']); + } else { + throw new GraphQlAuthorizationException(__('Invalid current user password.')); + } + } + + if (isset($customerData['firstname'])) { + $customer->setFirstname($customerData['firstname']); + } + if (isset($customerData['lastname'])) { + $customer->setLastname($customerData['lastname']); + } + + $customer->setStoreId($this->storeResolver->getCurrentStoreId()); + $this->customerRepository->save($customer); + + return $customer; + } +} diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php index 51f62a7a8cafd..6c3112632dbe2 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php @@ -10,6 +10,7 @@ use Magento\Authorization\Model\UserContextInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\CustomerGraphQl\Model\Resolver\Customer\CustomerDataProvider; +use Magento\CustomerGraphQl\Model\Resolver\Customer\CustomerDataUpdater; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; @@ -27,6 +28,11 @@ class CustomerUpdate implements ResolverInterface */ private $customerResolver; + /** + * @var CustomerDataUpdater + */ + private $customerUpdater; + /** * @var ValueFactory */ @@ -48,12 +54,14 @@ class CustomerUpdate implements ResolverInterface */ public function __construct( CustomerDataProvider $customerResolver, + CustomerDataUpdater $customerUpdater, ValueFactory $valueFactory, \Magento\Newsletter\Model\SubscriberFactory $subscriberFactory ) { $this->customerResolver = $customerResolver; $this->valueFactory = $valueFactory; $this->subscriberFactory = $subscriberFactory; + $this->customerUpdater = $customerUpdater; } /** @@ -77,13 +85,14 @@ public function resolve( ); } - $this->customerResolver->updateAccountInformation($context->getUserId(), $args); + $customerId = $context->getUserId(); + $this->customerUpdater->updateAccountInformation($customerId, $args); + $data = $this->customerResolver->getCustomerById($customerId); if (isset($args['is_subscribed'])) { - $this->customerResolver->manageSubscription($context->getUserId(), $args['is_subscribed']); + $data['is_subscribed'] = $this->customerUpdater->manageSubscription($customerId, $args['is_subscribed']); } - $data = $args; $result = function () use ($data) { return !empty($data) ? $data : []; }; diff --git a/app/code/Magento/CustomerGraphQl/composer.json b/app/code/Magento/CustomerGraphQl/composer.json index 290d925215ec2..ea9d0613f9737 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/newsletter": "*", "magento/framework": "*" }, "suggest": { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAccountEditTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAccountEditTest.php new file mode 100644 index 0000000000000..7bf46f08c853a --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAccountEditTest.php @@ -0,0 +1,157 @@ +getChangeAccountInformationQuery($customerNewFirstname, $customerNewLastname); + + $headerMap = $this->getCustomerAuthHeaders($customerEmail, $customerPassword); + $response = $this->graphQlQuery($query, [], '', $headerMap); + + $this->assertEquals($customerNewFirstname, $response['customerUpdate']['firstname']); + $this->assertEquals($customerNewLastname, $response['customerUpdate']['lastname']); + } + + /** + * + * @magentoApiDataFixture Magento/Customer/_files/customer.php + */ + public function testCustomerChangeEmail() + { + $customerEmail = 'customer@example.com'; + $customerPassword = 'password'; + $newEmailAddress = 'customer2@example.com'; + + $headerMap = $this->getCustomerAuthHeaders($customerEmail, $customerPassword); + + $query = $this->getChangeEmailQuery($newEmailAddress, $customerPassword); + $response = $this->graphQlQuery($query, [], '', $headerMap); + $this->assertEquals($newEmailAddress, $response['customerUpdate']['email']); + + /** + * Roll back email address to default + */ + $query = $this->getChangeEmailQuery($customerEmail, $customerPassword); + $response = $this->graphQlQuery($query, [], '', $headerMap); + $this->assertEquals($customerEmail, $response['customerUpdate']['email']); + } + + /** + * + * @magentoApiDataFixture Magento/Customer/_files/customer.php + */ + public function testCustomerCheckSubscription() + { + $customerEmail = 'customer@example.com'; + $customerPassword = 'password'; + + $isSubscribed = "true"; + + $query = $this->getSubscriptionQuery($isSubscribed); + $headerMap = $this->getCustomerAuthHeaders($customerEmail, $customerPassword); + + $response = $this->graphQlQuery($query, [], '', $headerMap); + + $subscriberModel = ObjectManager::getInstance()->get(Subscriber::class); + $subscriber = $subscriberModel->loadByEmail($customerEmail); + + $this->assertEquals(true, $subscriber->isSubscribed()); + } + private function getSubscriptionQuery($isSubscribed) + { + $query = <<objectManager->create(CustomerTokenServiceInterface::class); + $customerToken = $customerTokenService->createCustomerAccessToken($customerEmail, $customerPassword); + return ['Authorization' => 'Bearer ' . $customerToken]; + } + protected function setUp() + { + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->accountManagement = $this->objectManager->get(AccountManagementInterface::class); + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->searchCriteriaBuilder = $objectManager->create( + \Magento\Framework\Api\SearchCriteriaBuilder::class + ); + $this->filterBuilder = $objectManager->get( + \Magento\Framework\Api\FilterBuilder::class + ); + } +} \ No newline at end of file From b691d847f95bd91f2d98f87fc798f849a1285927 Mon Sep 17 00:00:00 2001 From: Lyzun Oleksandr Date: Fri, 14 Sep 2018 10:32:05 +0200 Subject: [PATCH 05/15] Code formatting --- .../Model/Resolver/Customer/CustomerDataProvider.php | 4 ++++ .../Model/Resolver/Customer/CustomerDataUpdater.php | 7 ++++++- .../CustomerGraphQl/Model/Resolver/CustomerUpdate.php | 4 ++++ .../Magento/GraphQl/Customer/CustomerAccountEditTest.php | 5 +++-- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataProvider.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataProvider.php index 17f40575ae3eb..39b23d1b23b24 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataProvider.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataProvider.php @@ -47,9 +47,13 @@ class CustomerDataProvider protected $encryptor; /** + * CustomerDataProvider constructor. + * * @param CustomerRepositoryInterface $customerRepository * @param ServiceOutputProcessor $serviceOutputProcessor * @param SerializerInterface $jsonSerializer + * @param CustomerRegistry $customerRegistry + * @param Encryptor $encryptor */ public function __construct( CustomerRepositoryInterface $customerRepository, diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataUpdater.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataUpdater.php index 3fec957c163f8..0e472bcf3945a 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataUpdater.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataUpdater.php @@ -42,8 +42,11 @@ class CustomerDataUpdater /** * CustomerDataUpdater constructor. + * * @param CustomerRepositoryInterface $customerRepository * @param StoreResolverInterface $storeResolver + * @param CustomerDataProvider $customerDataProvider + * @param SubscriberFactory $subscriberFactory */ public function __construct( CustomerRepositoryInterface $customerRepository, @@ -61,7 +64,7 @@ public function __construct( * Manage customer subscription. Subscribe OR unsubscribe if required. Return new subscription status * * @param int $customerId - * @param $newSubscriptionStatus + * @param bool $newSubscriptionStatus * @return bool */ public function manageSubscription(int $customerId, bool $newSubscriptionStatus): bool @@ -78,6 +81,8 @@ public function manageSubscription(int $customerId, bool $newSubscriptionStatus) } /** + * Update account information related to + * * @param int $customerId * @param array $customerData * @return CustomerInterface diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php index 6c3112632dbe2..b570767acaef3 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php @@ -49,8 +49,12 @@ class CustomerUpdate implements ResolverInterface protected $customerRegistry; /** + * CustomerUpdate constructor. + * * @param CustomerDataProvider $customerResolver + * @param CustomerDataUpdater $customerUpdater * @param ValueFactory $valueFactory + * @param \Magento\Newsletter\Model\SubscriberFactory $subscriberFactory */ public function __construct( CustomerDataProvider $customerResolver, diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAccountEditTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAccountEditTest.php index 7bf46f08c853a..17899cc7a7ccd 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAccountEditTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAccountEditTest.php @@ -5,6 +5,7 @@ */ declare(strict_types=1); namespace Magento\GraphQl\Customer; + use Magento\Customer\Api\AccountManagementInterface; use Magento\Integration\Api\CustomerTokenServiceInterface; use Magento\TestFramework\ObjectManager; @@ -88,7 +89,7 @@ public function testCustomerCheckSubscription() $query = $this->getSubscriptionQuery($isSubscribed); $headerMap = $this->getCustomerAuthHeaders($customerEmail, $customerPassword); - $response = $this->graphQlQuery($query, [], '', $headerMap); + $this->graphQlQuery($query, [], '', $headerMap); $subscriberModel = ObjectManager::getInstance()->get(Subscriber::class); $subscriber = $subscriberModel->loadByEmail($customerEmail); @@ -154,4 +155,4 @@ protected function setUp() \Magento\Framework\Api\FilterBuilder::class ); } -} \ No newline at end of file +} From d75986720e8700bc9e2411387f4f1eb1cce7b69a Mon Sep 17 00:00:00 2001 From: Lyzun Oleksandr Date: Fri, 14 Sep 2018 14:06:40 +0200 Subject: [PATCH 06/15] Code clean --- .../Model/Resolver/Customer/CustomerDataProvider.php | 3 +++ .../Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataProvider.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataProvider.php index 39b23d1b23b24..4ca61e069df43 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataProvider.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataProvider.php @@ -132,6 +132,8 @@ private function processCustomer(CustomerInterface $customerObject): array } /** + * Checking if password for customer is correct + * * @param string $password * @param int $customerId * @return bool @@ -143,3 +145,4 @@ public function isPasswordCorrect(string $password, int $customerId) return $this->encryptor->validateHash($password, $hash); } } + diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php index b570767acaef3..8c1fb92339019 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php @@ -19,7 +19,7 @@ use Magento\Framework\GraphQl\Query\ResolverInterface; /** - * Customers field resolver, used for GraphQL request processing. + * Customers Update resolver */ class CustomerUpdate implements ResolverInterface { From cc8d3289924fb4c16768a9ab215c72271a8e3e0c Mon Sep 17 00:00:00 2001 From: Lyzun Oleksandr Date: Wed, 3 Oct 2018 01:29:33 +0300 Subject: [PATCH 07/15] Fix tests --- .../Customer/CustomerDataProvider.php | 1 - .../Model/Resolver/CustomerUpdate.php | 2 +- composer.lock | 146 +++++++++--------- 3 files changed, 74 insertions(+), 75 deletions(-) diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataProvider.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataProvider.php index 4ca61e069df43..5badb7d0564d3 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataProvider.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataProvider.php @@ -145,4 +145,3 @@ public function isPasswordCorrect(string $password, int $customerId) return $this->encryptor->validateHash($password, $hash); } } - diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php index 8c1fb92339019..a7cf60ba8029f 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php @@ -69,7 +69,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function resolve( Field $field, diff --git a/composer.lock b/composer.lock index 2550f70f0be81..859d1bfc2185d 100644 --- a/composer.lock +++ b/composer.lock @@ -1104,16 +1104,16 @@ }, { "name": "paragonie/sodium_compat", - "version": "v1.6.4", + "version": "v1.7.0", "source": { "type": "git", "url": "https://github.com/paragonie/sodium_compat.git", - "reference": "3f2fd07977541b4d630ea0365ad0eceddee5179c" + "reference": "7b73005be3c224f12c47bd75a23ce24b762e47e8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/3f2fd07977541b4d630ea0365ad0eceddee5179c", - "reference": "3f2fd07977541b4d630ea0365ad0eceddee5179c", + "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/7b73005be3c224f12c47bd75a23ce24b762e47e8", + "reference": "7b73005be3c224f12c47bd75a23ce24b762e47e8", "shasum": "" }, "require": { @@ -1182,7 +1182,7 @@ "secret-key cryptography", "side-channel resistant" ], - "time": "2018-08-29T22:02:48+00:00" + "time": "2018-09-22T03:59:58+00:00" }, { "name": "pelago/emogrifier", @@ -1834,16 +1834,16 @@ }, { "name": "symfony/console", - "version": "v4.1.4", + "version": "v4.1.5", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "ca80b8ced97cf07390078b29773dc384c39eee1f" + "reference": "d3dbe91fd5b8b11ecb73508c844bc6a490de15b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/ca80b8ced97cf07390078b29773dc384c39eee1f", - "reference": "ca80b8ced97cf07390078b29773dc384c39eee1f", + "url": "https://api.github.com/repos/symfony/console/zipball/d3dbe91fd5b8b11ecb73508c844bc6a490de15b4", + "reference": "d3dbe91fd5b8b11ecb73508c844bc6a490de15b4", "shasum": "" }, "require": { @@ -1898,11 +1898,11 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2018-07-26T11:24:31+00:00" + "time": "2018-09-30T03:38:13+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v4.1.4", + "version": "v4.1.5", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", @@ -1965,16 +1965,16 @@ }, { "name": "symfony/filesystem", - "version": "v4.1.4", + "version": "v4.1.5", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "c0f5f62db218fa72195b8b8700e4b9b9cf52eb5e" + "reference": "a10ae719b02c47ecba5c684ca2b505f3a49bf397" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/c0f5f62db218fa72195b8b8700e4b9b9cf52eb5e", - "reference": "c0f5f62db218fa72195b8b8700e4b9b9cf52eb5e", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/a10ae719b02c47ecba5c684ca2b505f3a49bf397", + "reference": "a10ae719b02c47ecba5c684ca2b505f3a49bf397", "shasum": "" }, "require": { @@ -2011,20 +2011,20 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2018-08-18T16:52:46+00:00" + "time": "2018-09-30T03:38:13+00:00" }, { "name": "symfony/finder", - "version": "v4.1.4", + "version": "v4.1.5", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "e162f1df3102d0b7472805a5a9d5db9fcf0a8068" + "reference": "f0b042d445c155501793e7b8007457f9f5bb1c8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/e162f1df3102d0b7472805a5a9d5db9fcf0a8068", - "reference": "e162f1df3102d0b7472805a5a9d5db9fcf0a8068", + "url": "https://api.github.com/repos/symfony/finder/zipball/f0b042d445c155501793e7b8007457f9f5bb1c8c", + "reference": "f0b042d445c155501793e7b8007457f9f5bb1c8c", "shasum": "" }, "require": { @@ -2060,7 +2060,7 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2018-07-26T11:24:31+00:00" + "time": "2018-09-21T12:49:42+00:00" }, { "name": "symfony/polyfill-ctype", @@ -2181,16 +2181,16 @@ }, { "name": "symfony/process", - "version": "v4.1.4", + "version": "v4.1.5", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "86cdb930a6a855b0ab35fb60c1504cb36184f843" + "reference": "c64647828bc7733ba9427f1eeb1b542588635427" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/86cdb930a6a855b0ab35fb60c1504cb36184f843", - "reference": "86cdb930a6a855b0ab35fb60c1504cb36184f843", + "url": "https://api.github.com/repos/symfony/process/zipball/c64647828bc7733ba9427f1eeb1b542588635427", + "reference": "c64647828bc7733ba9427f1eeb1b542588635427", "shasum": "" }, "require": { @@ -2226,20 +2226,20 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2018-08-03T11:13:38+00:00" + "time": "2018-09-08T13:24:10+00:00" }, { "name": "tedivm/jshrink", - "version": "v1.3.0", + "version": "v1.3.1", "source": { "type": "git", "url": "https://github.com/tedious/JShrink.git", - "reference": "68ce379b213741e86f02bf6053b0d26b9f833448" + "reference": "21254058dc3ce6aba6bef458cff4bfa25cf8b198" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tedious/JShrink/zipball/68ce379b213741e86f02bf6053b0d26b9f833448", - "reference": "68ce379b213741e86f02bf6053b0d26b9f833448", + "url": "https://api.github.com/repos/tedious/JShrink/zipball/21254058dc3ce6aba6bef458cff4bfa25cf8b198", + "reference": "21254058dc3ce6aba6bef458cff4bfa25cf8b198", "shasum": "" }, "require": { @@ -2272,7 +2272,7 @@ "javascript", "minifier" ], - "time": "2017-12-08T00:59:56+00:00" + "time": "2018-09-16T00:02:51+00:00" }, { "name": "true/punycode", @@ -4804,16 +4804,16 @@ }, { "name": "consolidation/annotated-command", - "version": "2.8.5", + "version": "2.9.1", "source": { "type": "git", "url": "https://github.com/consolidation/annotated-command.git", - "reference": "1e8ff512072422b850b44aa721b5b303e4a5ebb3" + "reference": "4bdbb8fa149e1cc1511bd77b0bc4729fd66bccac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/1e8ff512072422b850b44aa721b5b303e4a5ebb3", - "reference": "1e8ff512072422b850b44aa721b5b303e4a5ebb3", + "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/4bdbb8fa149e1cc1511bd77b0bc4729fd66bccac", + "reference": "4bdbb8fa149e1cc1511bd77b0bc4729fd66bccac", "shasum": "" }, "require": { @@ -4852,7 +4852,7 @@ } ], "description": "Initialize Symfony Console commands from annotated command class methods.", - "time": "2018-08-18T23:51:49+00:00" + "time": "2018-09-19T17:47:18+00:00" }, { "name": "consolidation/config", @@ -8228,7 +8228,7 @@ }, { "name": "symfony/browser-kit", - "version": "v4.1.4", + "version": "v4.1.5", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", @@ -8285,16 +8285,16 @@ }, { "name": "symfony/config", - "version": "v4.1.4", + "version": "v4.1.5", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "76015a3cc372b14d00040ff58e18e29f69eba717" + "reference": "b3d4d7b567d7a49e6dfafb6d4760abc921177c96" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/76015a3cc372b14d00040ff58e18e29f69eba717", - "reference": "76015a3cc372b14d00040ff58e18e29f69eba717", + "url": "https://api.github.com/repos/symfony/config/zipball/b3d4d7b567d7a49e6dfafb6d4760abc921177c96", + "reference": "b3d4d7b567d7a49e6dfafb6d4760abc921177c96", "shasum": "" }, "require": { @@ -8344,20 +8344,20 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2018-08-08T06:37:38+00:00" + "time": "2018-09-08T13:24:10+00:00" }, { "name": "symfony/css-selector", - "version": "v4.1.4", + "version": "v4.1.5", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "2a4df7618f869b456f9096781e78c57b509d76c7" + "reference": "9ac515bde3c725ca46efa918d37e37c7cece6353" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/2a4df7618f869b456f9096781e78c57b509d76c7", - "reference": "2a4df7618f869b456f9096781e78c57b509d76c7", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/9ac515bde3c725ca46efa918d37e37c7cece6353", + "reference": "9ac515bde3c725ca46efa918d37e37c7cece6353", "shasum": "" }, "require": { @@ -8397,20 +8397,20 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2018-07-26T09:10:45+00:00" + "time": "2018-09-08T13:24:10+00:00" }, { "name": "symfony/dependency-injection", - "version": "v4.1.4", + "version": "v4.1.5", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "bae4983003c9d451e278504d7d9b9d7fc1846873" + "reference": "985ebee0d4cadaadef4d81aaccf0018443cf2560" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/bae4983003c9d451e278504d7d9b9d7fc1846873", - "reference": "bae4983003c9d451e278504d7d9b9d7fc1846873", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/985ebee0d4cadaadef4d81aaccf0018443cf2560", + "reference": "985ebee0d4cadaadef4d81aaccf0018443cf2560", "shasum": "" }, "require": { @@ -8468,20 +8468,20 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2018-08-08T11:48:58+00:00" + "time": "2018-09-21T12:49:42+00:00" }, { "name": "symfony/dom-crawler", - "version": "v4.1.4", + "version": "v4.1.5", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "1c4519d257e652404c3aa550207ccd8ada66b38e" + "reference": "8ffa4c496c782e5591182318a6199b7507431651" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/1c4519d257e652404c3aa550207ccd8ada66b38e", - "reference": "1c4519d257e652404c3aa550207ccd8ada66b38e", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/8ffa4c496c782e5591182318a6199b7507431651", + "reference": "8ffa4c496c782e5591182318a6199b7507431651", "shasum": "" }, "require": { @@ -8525,20 +8525,20 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2018-07-26T11:00:49+00:00" + "time": "2018-09-21T12:49:42+00:00" }, { "name": "symfony/http-foundation", - "version": "v4.1.4", + "version": "v4.1.5", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "3a5c91e133b220bb882b3cd773ba91bf39989345" + "reference": "2ce66353d0a6ea96bc54bc9ecf8bcea4eaf5896c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/3a5c91e133b220bb882b3cd773ba91bf39989345", - "reference": "3a5c91e133b220bb882b3cd773ba91bf39989345", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/2ce66353d0a6ea96bc54bc9ecf8bcea4eaf5896c", + "reference": "2ce66353d0a6ea96bc54bc9ecf8bcea4eaf5896c", "shasum": "" }, "require": { @@ -8579,20 +8579,20 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2018-08-27T17:47:02+00:00" + "time": "2018-09-30T03:47:35+00:00" }, { "name": "symfony/options-resolver", - "version": "v4.1.4", + "version": "v4.1.5", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "1913f1962477cdbb13df951f8147d5da1fe2412c" + "reference": "40f0e40d37c1c8a762334618dea597d64bbb75ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/1913f1962477cdbb13df951f8147d5da1fe2412c", - "reference": "1913f1962477cdbb13df951f8147d5da1fe2412c", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/40f0e40d37c1c8a762334618dea597d64bbb75ff", + "reference": "40f0e40d37c1c8a762334618dea597d64bbb75ff", "shasum": "" }, "require": { @@ -8633,7 +8633,7 @@ "configuration", "options" ], - "time": "2018-07-26T08:55:25+00:00" + "time": "2018-09-18T12:45:12+00:00" }, { "name": "symfony/polyfill-php70", @@ -8751,7 +8751,7 @@ }, { "name": "symfony/stopwatch", - "version": "v4.1.4", + "version": "v4.1.5", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", @@ -8800,16 +8800,16 @@ }, { "name": "symfony/yaml", - "version": "v3.4.15", + "version": "v3.4.16", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "c2f4812ead9f847cb69e90917ca7502e6892d6b8" + "reference": "61973ecda60e9f3561e929e19c07d4878b960fc1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/c2f4812ead9f847cb69e90917ca7502e6892d6b8", - "reference": "c2f4812ead9f847cb69e90917ca7502e6892d6b8", + "url": "https://api.github.com/repos/symfony/yaml/zipball/61973ecda60e9f3561e929e19c07d4878b960fc1", + "reference": "61973ecda60e9f3561e929e19c07d4878b960fc1", "shasum": "" }, "require": { @@ -8855,7 +8855,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2018-08-10T07:34:36+00:00" + "time": "2018-09-24T08:15:45+00:00" }, { "name": "theseer/fdomdocument", From 2057ddf9a3d8d84ced9390418ee3a74ecdc8691d Mon Sep 17 00:00:00 2001 From: Valeriy Nayda Date: Mon, 8 Oct 2018 19:44:08 +0300 Subject: [PATCH 08/15] GraphQL-55: [Mutations] My Account > Change account information --- .../Customer/ChangeSubscriptionStatus.php | 48 +++ .../Model/Customer/CheckCustomerAccount.php | 103 ++++++ .../Model/Customer/CheckCustomerPassword.php | 50 +++ .../Customer/CustomerDataProvider.php | 77 ++-- .../Customer/UpdateAccountInformation.php | 94 +++++ .../Model/Resolver/ChangePassword.php | 94 +++++ .../Model/Resolver/Customer.php | 44 ++- .../Customer/Account/ChangePassword.php | 88 ----- .../Resolver/Customer/CustomerDataUpdater.php | 121 ------ .../Model/Resolver/CustomerUpdate.php | 106 ------ .../Account => }/GenerateCustomerToken.php | 21 +- .../Model/Resolver/IsSubscribed.php | 63 ++++ .../Model/Resolver/UpdateCustomer.php | 93 +++++ .../CustomerGraphQl/etc/schema.graphqls | 30 +- .../Customer/AccountInformationTest.php | 343 ++++++++++++++++++ .../Customer/CustomerAccountEditTest.php | 158 -------- .../Customer/SubscriptionStatusTest.php | 134 +++++++ .../_files/two_customers_rollback.php | 31 ++ .../GraphQlAlreadyExistsException.php | 56 +++ .../GraphQlAuthenticationException.php | 56 +++ 20 files changed, 1236 insertions(+), 574 deletions(-) create mode 100644 app/code/Magento/CustomerGraphQl/Model/Customer/ChangeSubscriptionStatus.php create mode 100644 app/code/Magento/CustomerGraphQl/Model/Customer/CheckCustomerAccount.php create mode 100644 app/code/Magento/CustomerGraphQl/Model/Customer/CheckCustomerPassword.php rename app/code/Magento/CustomerGraphQl/Model/{Resolver => }/Customer/CustomerDataProvider.php (57%) create mode 100644 app/code/Magento/CustomerGraphQl/Model/Customer/UpdateAccountInformation.php create mode 100644 app/code/Magento/CustomerGraphQl/Model/Resolver/ChangePassword.php delete mode 100644 app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/Account/ChangePassword.php delete mode 100644 app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataUpdater.php delete mode 100644 app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php rename app/code/Magento/CustomerGraphQl/Model/Resolver/{Customer/Account => }/GenerateCustomerToken.php (74%) create mode 100644 app/code/Magento/CustomerGraphQl/Model/Resolver/IsSubscribed.php create mode 100644 app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomer.php create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/AccountInformationTest.php delete mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAccountEditTest.php create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/SubscriptionStatusTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Customer/_files/two_customers_rollback.php create mode 100644 lib/internal/Magento/Framework/GraphQl/Exception/GraphQlAlreadyExistsException.php create mode 100644 lib/internal/Magento/Framework/GraphQl/Exception/GraphQlAuthenticationException.php diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/ChangeSubscriptionStatus.php b/app/code/Magento/CustomerGraphQl/Model/Customer/ChangeSubscriptionStatus.php new file mode 100644 index 0000000000000..1acb418e7bba6 --- /dev/null +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/ChangeSubscriptionStatus.php @@ -0,0 +1,48 @@ +subscriberFactory = $subscriberFactory; + } + + /** + * Change subscription status. Subscribe OR unsubscribe if required + * + * @param int $customerId + * @param bool $subscriptionStatus + * @return void + */ + public function execute(int $customerId, bool $subscriptionStatus): void + { + $subscriber = $this->subscriberFactory->create()->loadByCustomerId($customerId); + + if ($subscriptionStatus === true && !$subscriber->isSubscribed()) { + $this->subscriberFactory->create()->subscribeCustomerById($customerId); + } elseif ($subscriptionStatus === false && $subscriber->isSubscribed()) { + $this->subscriberFactory->create()->unsubscribeCustomerById($customerId); + } + } +} diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/CheckCustomerAccount.php b/app/code/Magento/CustomerGraphQl/Model/Customer/CheckCustomerAccount.php new file mode 100644 index 0000000000000..030fc47d19e81 --- /dev/null +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/CheckCustomerAccount.php @@ -0,0 +1,103 @@ +authentication = $authentication; + $this->customerRepository = $customerRepository; + $this->accountManagement = $accountManagement; + } + + /** + * Check customer account + * + * @param int|null $customerId + * @param int|null $customerType + * @return void + * @throws GraphQlAuthorizationException + * @throws GraphQlNoSuchEntityException + * @throws GraphQlAuthenticationException + */ + public function execute(?int $customerId, ?int $customerType): void + { + if (true === $this->isCustomerGuest($customerId, $customerType)) { + throw new GraphQlAuthorizationException(__('The current customer isn\'t authorized.')); + } + + try { + $this->customerRepository->getById($customerId); + } catch (NoSuchEntityException $e) { + throw new GraphQlNoSuchEntityException( + __('Customer with id "%customer_id" does not exist.', ['customer_id' => $customerId]), + $e + ); + } + + if (true === $this->authentication->isLocked($customerId)) { + throw new GraphQlAuthenticationException(__('The account is locked.')); + } + + $confirmationStatus = $this->accountManagement->getConfirmationStatus($customerId); + if ($confirmationStatus === AccountManagementInterface::ACCOUNT_CONFIRMATION_REQUIRED) { + throw new GraphQlAuthenticationException(__("This account isn't confirmed. Verify and try again.")); + } + } + + /** + * Checking if current customer is guest + * + * @param int|null $customerId + * @param int|null $customerType + * @return bool + */ + private function isCustomerGuest(?int $customerId, ?int $customerType): bool + { + if (null === $customerId || null === $customerType) { + return true; + } + return 0 === (int)$customerId || (int)$customerType === UserContextInterface::USER_TYPE_GUEST; + } +} diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/CheckCustomerPassword.php b/app/code/Magento/CustomerGraphQl/Model/Customer/CheckCustomerPassword.php new file mode 100644 index 0000000000000..f3c03e5fc18aa --- /dev/null +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/CheckCustomerPassword.php @@ -0,0 +1,50 @@ +authentication = $authentication; + } + + /** + * Check customer password + * + * @param string $password + * @param int $customerId + * @throws GraphQlAuthenticationException + */ + public function execute(string $password, int $customerId) + { + try { + $this->authentication->authenticate($customerId, $password); + } catch (InvalidEmailOrPasswordException $e) { + throw new GraphQlAuthenticationException( + __('The password doesn\'t match this account. Verify the password and try again.') + ); + } + } +} diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataProvider.php b/app/code/Magento/CustomerGraphQl/Model/Customer/CustomerDataProvider.php similarity index 57% rename from app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataProvider.php rename to app/code/Magento/CustomerGraphQl/Model/Customer/CustomerDataProvider.php index 5badb7d0564d3..646d426050b70 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataProvider.php +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/CustomerDataProvider.php @@ -5,16 +5,15 @@ */ declare(strict_types=1); -namespace Magento\CustomerGraphQl\Model\Resolver\Customer; +namespace Magento\CustomerGraphQl\Model\Customer; use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Framework\Serialize\SerializerInterface; use Magento\Framework\Webapi\ServiceOutputProcessor; use Magento\Customer\Api\Data\CustomerInterface; -use Magento\Customer\Model\CustomerRegistry; -use Magento\Framework\Encryption\EncryptorInterface as Encryptor; /** * Customer field data provider, used for GraphQL request processing. @@ -31,42 +30,24 @@ class CustomerDataProvider */ private $serviceOutputProcessor; - /** - * @var CustomerRegistry - */ - protected $customerRegistry; - /** * @var SerializerInterface */ - private $jsonSerializer; + private $serializer; /** - * @var Encryptor - */ - protected $encryptor; - - /** - * CustomerDataProvider constructor. - * * @param CustomerRepositoryInterface $customerRepository * @param ServiceOutputProcessor $serviceOutputProcessor - * @param SerializerInterface $jsonSerializer - * @param CustomerRegistry $customerRegistry - * @param Encryptor $encryptor + * @param SerializerInterface $serializer */ public function __construct( CustomerRepositoryInterface $customerRepository, ServiceOutputProcessor $serviceOutputProcessor, - SerializerInterface $jsonSerializer, - CustomerRegistry $customerRegistry, - Encryptor $encryptor + SerializerInterface $serializer ) { $this->customerRepository = $customerRepository; $this->serviceOutputProcessor = $serviceOutputProcessor; - $this->jsonSerializer = $jsonSerializer; - $this->customerRegistry = $customerRegistry; - $this->encryptor = $encryptor; + $this->serializer = $serializer; } /** @@ -79,39 +60,41 @@ public function __construct( public function getCustomerById(int $customerId): array { try { - $customerObject = $this->customerRepository->getById($customerId); + $customer = $this->customerRepository->getById($customerId); } catch (NoSuchEntityException $e) { - // No error should be thrown, null result should be returned - return []; + throw new GraphQlNoSuchEntityException( + __('Customer id "%customer_id" does not exist.', ['customer_id' => $customerId]), + $e + ); } - return $this->processCustomer($customerObject); + return $this->processCustomer($customer); } /** * Transform single customer data from object to in array format * - * @param CustomerInterface $customerObject + * @param CustomerInterface $customer * @return array */ - private function processCustomer(CustomerInterface $customerObject): array + private function processCustomer(CustomerInterface $customer): array { - $customer = $this->serviceOutputProcessor->process( - $customerObject, + $customerData = $this->serviceOutputProcessor->process( + $customer, CustomerRepositoryInterface::class, 'get' ); - if (isset($customer['extension_attributes'])) { - $customer = array_merge($customer, $customer['extension_attributes']); + if (isset($customerData['extension_attributes'])) { + $customerData = array_merge($customerData, $customerData['extension_attributes']); } $customAttributes = []; - if (isset($customer['custom_attributes'])) { - foreach ($customer['custom_attributes'] as $attribute) { + if (isset($customerData['custom_attributes'])) { + foreach ($customerData['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( + $customAttributes[$attribute['attribute_code']] = $this->serializer->serialize( $attribute['value'] ); continue; @@ -126,22 +109,8 @@ private function processCustomer(CustomerInterface $customerObject): array $customAttributes[$attribute['attribute_code']] = $attribute['value']; } } - $customer = array_merge($customer, $customAttributes); + $customerData = array_merge($customerData, $customAttributes); - return $customer; - } - - /** - * Checking if password for customer is correct - * - * @param string $password - * @param int $customerId - * @return bool - * @throws NoSuchEntityException - */ - public function isPasswordCorrect(string $password, int $customerId) - { - $hash = $this->customerRegistry->retrieveSecureData($customerId)->getPasswordHash(); - return $this->encryptor->validateHash($password, $hash); + return $customerData; } } diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/UpdateAccountInformation.php b/app/code/Magento/CustomerGraphQl/Model/Customer/UpdateAccountInformation.php new file mode 100644 index 0000000000000..d235d51b5e52d --- /dev/null +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/UpdateAccountInformation.php @@ -0,0 +1,94 @@ +customerRepository = $customerRepository; + $this->storeManager = $storeManager; + $this->checkCustomerPassword = $checkCustomerPassword; + } + + /** + * Update account information + * + * @param int $customerId + * @param array $data + * @return void + * @throws GraphQlNoSuchEntityException + * @throws GraphQlInputException + * @throws GraphQlAlreadyExistsException + */ + public function execute(int $customerId, array $data): void + { + $customer = $this->customerRepository->getById($customerId); + + if (isset($data['firstname'])) { + $customer->setFirstname($data['firstname']); + } + + if (isset($data['lastname'])) { + $customer->setLastname($data['lastname']); + } + + if (isset($data['email']) && $customer->getEmail() !== $data['email']) { + if (!isset($data['password']) || empty($data['password'])) { + throw new GraphQlInputException(__('For changing "email" you should provide current "password".')); + } + + $this->checkCustomerPassword->execute($data['password'], $customerId); + $customer->setEmail($data['email']); + } + + $customer->setStoreId($this->storeManager->getStore()->getId()); + + try { + $this->customerRepository->save($customer); + } catch (AlreadyExistsException $e) { + throw new GraphQlAlreadyExistsException( + __('A customer with the same email address already exists in an associated website.'), + $e + ); + } + } +} diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/ChangePassword.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/ChangePassword.php new file mode 100644 index 0000000000000..e94db23ac1c2a --- /dev/null +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/ChangePassword.php @@ -0,0 +1,94 @@ +checkCustomerAccount = $checkCustomerAccount; + $this->checkCustomerPassword = $checkCustomerPassword; + $this->accountManagement = $accountManagement; + $this->customerDataProvider = $customerDataProvider; + } + + /** + * @inheritdoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + if (!isset($args['currentPassword'])) { + throw new GraphQlInputException(__('"currentPassword" value should be specified')); + } + + if (!isset($args['newPassword'])) { + throw new GraphQlInputException(__('"newPassword" value should be specified')); + } + + /** @var ContextInterface $context */ + $currentUserId = $context->getUserId(); + $currentUserType = $context->getUserType(); + $currentUserId = (int)$currentUserId; + + $this->checkCustomerAccount->execute($currentUserId, $currentUserType); + $this->checkCustomerPassword->execute($args['currentPassword'], $currentUserId); + + $this->accountManagement->changePasswordById($currentUserId, $args['currentPassword'], $args['newPassword']); + + $data = $this->customerDataProvider->getCustomerById($currentUserId); + return $data; + } +} diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer.php index 98941af3b73d2..29cde9edc6db3 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer.php @@ -7,13 +7,10 @@ namespace Magento\CustomerGraphQl\Model\Resolver; -use Magento\Authorization\Model\UserContextInterface; +use Magento\CustomerGraphQl\Model\Customer\CheckCustomerAccount; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\CustomerGraphQl\Model\Resolver\Customer\CustomerDataProvider; -use Magento\Framework\Exception\NoSuchEntityException; +use Magento\CustomerGraphQl\Model\Customer\CustomerDataProvider; use Magento\Framework\GraphQl\Config\Element\Field; -use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; -use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; use Magento\Framework\GraphQl\Query\ResolverInterface; @@ -22,18 +19,26 @@ */ class Customer implements ResolverInterface { + /** + * @var CheckCustomerAccount + */ + private $checkCustomerAccount; + /** * @var CustomerDataProvider */ - private $customerResolver; + private $customerDataProvider; /** - * @param CustomerDataProvider $customerResolver + * @param CheckCustomerAccount $checkCustomerAccount + * @param CustomerDataProvider $customerDataProvider */ public function __construct( - CustomerDataProvider $customerResolver + CheckCustomerAccount $checkCustomerAccount, + CustomerDataProvider $customerDataProvider ) { - $this->customerResolver = $customerResolver; + $this->checkCustomerAccount = $checkCustomerAccount; + $this->customerDataProvider = $customerDataProvider; } /** @@ -47,20 +52,13 @@ public function resolve( array $args = null ) { /** @var ContextInterface $context */ - if ((!$context->getUserId()) || $context->getUserType() == UserContextInterface::USER_TYPE_GUEST) { - throw new GraphQlAuthorizationException( - __( - 'Current customer does not have access to the resource "%1"', - [\Magento\Customer\Model\Customer::ENTITY] - ) - ); - } + $currentUserId = $context->getUserId(); + $currentUserType = $context->getUserType(); + + $this->checkCustomerAccount->execute($currentUserId, $currentUserType); - try { - $data = $this->customerResolver->getCustomerById($context->getUserId()); - return !empty($data) ? $data : []; - } catch (NoSuchEntityException $exception) { - throw new GraphQlNoSuchEntityException(__('Customer id %1 does not exist.', [$context->getUserId()])); - } + $currentUserId = (int)$currentUserId; + $data = $this->customerDataProvider->getCustomerById($currentUserId); + return $data; } } diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/Account/ChangePassword.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/Account/ChangePassword.php deleted file mode 100644 index 84425df62a622..0000000000000 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/Account/ChangePassword.php +++ /dev/null @@ -1,88 +0,0 @@ -userContext = $userContext; - $this->accountManagement = $accountManagement; - $this->customerResolver = $customerResolver; - } - - /** - * @inheritdoc - */ - public function resolve( - Field $field, - $context, - ResolveInfo $info, - array $value = null, - array $args = null - ) { - if (!isset($args['currentPassword'])) { - throw new GraphQlInputException(__('"currentPassword" value should be specified')); - } - - if (!isset($args['newPassword'])) { - throw new GraphQlInputException(__('"newPassword" value should be specified')); - } - - $customerId = (int)$this->userContext->getUserId(); - if ($customerId === 0) { - throw new GraphQlAuthorizationException( - __( - 'Current customer does not have access to the resource "%1"', - [Customer::ENTITY] - ) - ); - } - - $this->accountManagement->changePasswordById($customerId, $args['currentPassword'], $args['newPassword']); - $data = $this->customerResolver->getCustomerById($customerId); - - return $data; - } -} diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataUpdater.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataUpdater.php deleted file mode 100644 index 0e472bcf3945a..0000000000000 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataUpdater.php +++ /dev/null @@ -1,121 +0,0 @@ -customerRepository = $customerRepository; - $this->storeResolver = $storeResolver; - $this->customerDataProvider = $customerDataProvider; - $this->subscriberFactory = $subscriberFactory; - } - - /** - * Manage customer subscription. Subscribe OR unsubscribe if required. Return new subscription status - * - * @param int $customerId - * @param bool $newSubscriptionStatus - * @return bool - */ - public function manageSubscription(int $customerId, bool $newSubscriptionStatus): bool - { - $subscriber = $this->subscriberFactory->create()->loadByCustomerId($customerId); - if ($newSubscriptionStatus === true && !$subscriber->isSubscribed()) { - $this->subscriberFactory->create()->subscribeCustomerById($customerId); - } elseif ($newSubscriptionStatus === false && $subscriber->isSubscribed()) { - $this->subscriberFactory->create()->unsubscribeCustomerById($customerId); - } - /** Load subscribed again to get his new status after update subscription */ - $subscriber = $this->subscriberFactory->create()->loadByCustomerId($customerId); - return $subscriber->isSubscribed(); - } - - /** - * Update account information related to - * - * @param int $customerId - * @param array $customerData - * @return CustomerInterface - * @throws GraphQlAuthorizationException - * @throws LocalizedException - * @throws NoSuchEntityException - * @throws \Magento\Framework\Exception\InputException - * @throws \Magento\Framework\Exception\State\InputMismatchException - */ - public function updateAccountInformation(int $customerId, array $customerData): CustomerInterface - { - $customer = $this->customerRepository->getById($customerId); - - if (isset($customerData['email']) - && $customer->getEmail() !== $customerData['email'] - && isset($customerData['password'])) { - if ($this->customerDataProvider->isPasswordCorrect($customerData['password'], $customerId)) { - $customer->setEmail($customerData['email']); - } else { - throw new GraphQlAuthorizationException(__('Invalid current user password.')); - } - } - - if (isset($customerData['firstname'])) { - $customer->setFirstname($customerData['firstname']); - } - if (isset($customerData['lastname'])) { - $customer->setLastname($customerData['lastname']); - } - - $customer->setStoreId($this->storeResolver->getCurrentStoreId()); - $this->customerRepository->save($customer); - - return $customer; - } -} diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php deleted file mode 100644 index a7cf60ba8029f..0000000000000 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerUpdate.php +++ /dev/null @@ -1,106 +0,0 @@ -customerResolver = $customerResolver; - $this->valueFactory = $valueFactory; - $this->subscriberFactory = $subscriberFactory; - $this->customerUpdater = $customerUpdater; - } - - /** - * @inheritdoc - */ - public function resolve( - Field $field, - $context, - ResolveInfo $info, - array $value = null, - array $args = null - ) : Value { - - /** @var ContextInterface $context */ - if ((!$context->getUserId()) || $context->getUserType() == UserContextInterface::USER_TYPE_GUEST) { - throw new GraphQlAuthorizationException( - __( - 'Current customer does not have access to the resource "%1"', - [\Magento\Customer\Model\Customer::ENTITY] - ) - ); - } - - $customerId = $context->getUserId(); - $this->customerUpdater->updateAccountInformation($customerId, $args); - $data = $this->customerResolver->getCustomerById($customerId); - - if (isset($args['is_subscribed'])) { - $data['is_subscribed'] = $this->customerUpdater->manageSubscription($customerId, $args['is_subscribed']); - } - - $result = function () use ($data) { - return !empty($data) ? $data : []; - }; - - return $this->valueFactory->create($result); - } -} diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/Account/GenerateCustomerToken.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/GenerateCustomerToken.php similarity index 74% rename from app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/Account/GenerateCustomerToken.php rename to app/code/Magento/CustomerGraphQl/Model/Resolver/GenerateCustomerToken.php index b756a96411a44..0405d63e49b81 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/Account/GenerateCustomerToken.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/GenerateCustomerToken.php @@ -5,11 +5,12 @@ */ declare(strict_types=1); -namespace Magento\CustomerGraphQl\Model\Resolver\Customer\Account; +namespace Magento\CustomerGraphQl\Model\Resolver; use Magento\Framework\Exception\AuthenticationException; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Integration\Api\CustomerTokenServiceInterface; @@ -43,19 +44,19 @@ public function resolve( array $value = null, array $args = null ) { + if (!isset($args['email'])) { + throw new GraphQlInputException(__('"email" value should be specified')); + } + + if (!isset($args['password'])) { + throw new GraphQlInputException(__('"password" value should be specified')); + } + try { - if (!isset($args['email'])) { - throw new GraphQlInputException(__('"email" value should be specified')); - } - if (!isset($args['password'])) { - throw new GraphQlInputException(__('"password" value should be specified')); - } $token = $this->customerTokenService->createCustomerAccessToken($args['email'], $args['password']); return ['token' => $token]; } catch (AuthenticationException $e) { - throw new GraphQlAuthorizationException( - __($e->getMessage()) - ); + throw new GraphQlAuthorizationException(__($e->getMessage()), $e); } } } diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/IsSubscribed.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/IsSubscribed.php new file mode 100644 index 0000000000000..bee116eec4afc --- /dev/null +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/IsSubscribed.php @@ -0,0 +1,63 @@ +checkCustomerAccount = $checkCustomerAccount; + $this->subscriberFactory = $subscriberFactory; + } + + /** + * @inheritdoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + /** @var ContextInterface $context */ + $currentUserId = $context->getUserId(); + $currentUserType = $context->getUserType(); + + $this->checkCustomerAccount->execute($currentUserId, $currentUserType); + + $status = $this->subscriberFactory->create()->loadByCustomerId($currentUserId)->isSubscribed(); + return (bool)$status; + } +} diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomer.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomer.php new file mode 100644 index 0000000000000..339f855b2f0c6 --- /dev/null +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomer.php @@ -0,0 +1,93 @@ +checkCustomerAccount = $checkCustomerAccount; + $this->updateAccountInformation = $updateAccountInformation; + $this->changeSubscriptionStatus = $changeSubscriptionStatus; + $this->customerDataProvider = $customerDataProvider; + } + + /** + * @inheritdoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + if (!isset($args['input']) || !is_array($args['input']) || empty($args['input'])) { + throw new GraphQlInputException(__('"input" value should be specified')); + } + + /** @var ContextInterface $context */ + $currentUserId = $context->getUserId(); + $currentUserType = $context->getUserType(); + + $this->checkCustomerAccount->execute($currentUserId, $currentUserType); + + $currentUserId = (int)$currentUserId; + $this->updateAccountInformation->execute($currentUserId, $args['input']); + + if (isset($args['input']['is_subscribed'])) { + $this->changeSubscriptionStatus->execute($currentUserId, (bool)$args['input']['is_subscribed']); + } + + $data = $this->customerDataProvider->getCustomerById($currentUserId); + return ['customer' => $data]; + } +} diff --git a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls index f146009312837..8e0c39372e14a 100644 --- a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls @@ -6,14 +6,27 @@ type Query { } type Mutation { - generateCustomerToken(email: String!, password: String!): CustomerToken @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\Customer\\Account\\GenerateCustomerToken") @doc(description:"Retrieve Customer token") - changeCustomerPassword(currentPassword: String!, newPassword: String!): Customer @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\Customer\\Account\\ChangePassword") @doc(description:"Changes password for logged in customer") + generateCustomerToken(email: String!, password: String!): CustomerToken @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\GenerateCustomerToken") @doc(description:"Retrieve Customer token") + changeCustomerPassword(currentPassword: String!, newPassword: String!): Customer @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\ChangePassword") @doc(description:"Changes password for logged in customer") + updateCustomer (input: UpdateCustomerInput): UpdateCustomerOutput @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\UpdateCustomer") @doc(description:"Update customer personal information") } type CustomerToken { token: String @doc(description: "The customer token") } +input UpdateCustomerInput { + firstname: String + lastname: String + email: String + password: String + is_subscribed: Boolean +} + +type UpdateCustomerOutput { + customer: Customer! +} + type Customer @doc(description: "Customer defines the customer name and address and other details") { created_at: String @doc(description: "Timestamp indicating when the account was created") group_id: Int @doc(description: "The group assigned to the user. Default values are 0 (Not logged in), 1 (General), 2 (Wholesale), and 3 (Retailer)") @@ -28,7 +41,7 @@ type Customer @doc(description: "Customer defines the customer name and address dob: String @doc(description: "The customer's date of birth") taxvat: String @doc(description: "The customer's Tax/VAT number (for corporate customers)") id: Int @doc(description: "The ID assigned to the customer") - is_subscribed: Boolean @doc(description: "Indicates whether the customer is subscribed to the company's newsletter") + is_subscribed: Boolean @doc(description: "Indicates whether the customer is subscribed to the company's newsletter") @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\IsSubscribed") addresses: [CustomerAddress] @doc(description: "An array containing the customer's shipping and billing addresses") } @@ -59,14 +72,3 @@ 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 Mutation { - customerUpdate (firstname: String, lastname: String, email: String, password: String, is_subscribed: Boolean): customerItem @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\CustomerUpdate") @doc(description:"Update customer personal information") -} - -type customerItem { - firstname: String - lastname: String - email: String - is_subscribed: Boolean -} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/AccountInformationTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/AccountInformationTest.php new file mode 100644 index 0000000000000..2caee84ba6f00 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/AccountInformationTest.php @@ -0,0 +1,343 @@ +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() + { + $currentEmail = 'customer@example.com'; + $currentPassword = 'password'; + + $newFirstname = 'Richard'; + $newLastname = 'Rowe'; + $newEmail = 'customer_updated@example.com'; + + $query = <<graphQlQuery($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); + + $this->assertEquals($newFirstname, $response['updateCustomer']['customer']['firstname']); + $this->assertEquals($newLastname, $response['updateCustomer']['customer']['lastname']); + $this->assertEquals($newEmail, $response['updateCustomer']['customer']['email']); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @expectedException \Exception + * @expectedExceptionMessage "input" value should be specified + */ + public function testUpdateAccountInformationIfInputDataIsEmpty() + { + $currentEmail = 'customer@example.com'; + $currentPassword = 'password'; + + $query = <<graphQlQuery($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage The current customer isn't authorized. + */ + public function testUpdateAccountInformationIfUserIsNotAuthorized() + { + $newFirstname = 'Richard'; + + $query = <<graphQlQuery($query); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @expectedException \Exception + * @expectedExceptionMessage The account is locked. + */ + public function testUpdateAccountInformationIfCustomerIsLocked() + { + $this->lockCustomer(1); + + $currentEmail = 'customer@example.com'; + $currentPassword = 'password'; + $newFirstname = 'Richard'; + + $query = <<graphQlQuery($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @expectedException \Exception + * @expectedExceptionMessage For changing "email" you should provide current "password". + */ + public function testUpdateEmailIfPasswordIsMissed() + { + $currentEmail = 'customer@example.com'; + $currentPassword = 'password'; + $newEmail = 'customer_updated@example.com'; + + $query = <<graphQlQuery($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @expectedException \Exception + * @expectedExceptionMessage The password doesn't match this account. Verify the password and try again. + */ + public function testUpdateEmailIfPasswordIsInvalid() + { + $currentEmail = 'customer@example.com'; + $currentPassword = 'password'; + $invalidPassword = 'invalid_password'; + $newEmail = 'customer_updated@example.com'; + + $query = <<graphQlQuery($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/two_customers.php + * @expectedException \Exception + * @expectedExceptionMessage A customer with the same email address already exists in an associated website. + */ + public function testUpdateEmailIfEmailAlreadyExists() + { + $currentEmail = 'customer@example.com'; + $currentPassword = 'password'; + $existedEmail = 'customer_two@example.com'; + + $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/CustomerAccountEditTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAccountEditTest.php deleted file mode 100644 index 17899cc7a7ccd..0000000000000 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAccountEditTest.php +++ /dev/null @@ -1,158 +0,0 @@ -getChangeAccountInformationQuery($customerNewFirstname, $customerNewLastname); - - $headerMap = $this->getCustomerAuthHeaders($customerEmail, $customerPassword); - $response = $this->graphQlQuery($query, [], '', $headerMap); - - $this->assertEquals($customerNewFirstname, $response['customerUpdate']['firstname']); - $this->assertEquals($customerNewLastname, $response['customerUpdate']['lastname']); - } - - /** - * - * @magentoApiDataFixture Magento/Customer/_files/customer.php - */ - public function testCustomerChangeEmail() - { - $customerEmail = 'customer@example.com'; - $customerPassword = 'password'; - $newEmailAddress = 'customer2@example.com'; - - $headerMap = $this->getCustomerAuthHeaders($customerEmail, $customerPassword); - - $query = $this->getChangeEmailQuery($newEmailAddress, $customerPassword); - $response = $this->graphQlQuery($query, [], '', $headerMap); - $this->assertEquals($newEmailAddress, $response['customerUpdate']['email']); - - /** - * Roll back email address to default - */ - $query = $this->getChangeEmailQuery($customerEmail, $customerPassword); - $response = $this->graphQlQuery($query, [], '', $headerMap); - $this->assertEquals($customerEmail, $response['customerUpdate']['email']); - } - - /** - * - * @magentoApiDataFixture Magento/Customer/_files/customer.php - */ - public function testCustomerCheckSubscription() - { - $customerEmail = 'customer@example.com'; - $customerPassword = 'password'; - - $isSubscribed = "true"; - - $query = $this->getSubscriptionQuery($isSubscribed); - $headerMap = $this->getCustomerAuthHeaders($customerEmail, $customerPassword); - - $this->graphQlQuery($query, [], '', $headerMap); - - $subscriberModel = ObjectManager::getInstance()->get(Subscriber::class); - $subscriber = $subscriberModel->loadByEmail($customerEmail); - - $this->assertEquals(true, $subscriber->isSubscribed()); - } - private function getSubscriptionQuery($isSubscribed) - { - $query = <<objectManager->create(CustomerTokenServiceInterface::class); - $customerToken = $customerTokenService->createCustomerAccessToken($customerEmail, $customerPassword); - return ['Authorization' => 'Bearer ' . $customerToken]; - } - protected function setUp() - { - $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->accountManagement = $this->objectManager->get(AccountManagementInterface::class); - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->searchCriteriaBuilder = $objectManager->create( - \Magento\Framework\Api\SearchCriteriaBuilder::class - ); - $this->filterBuilder = $objectManager->get( - \Magento\Framework\Api\FilterBuilder::class - ); - } -} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/SubscriptionStatusTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/SubscriptionStatusTest.php new file mode 100644 index 0000000000000..191ea1ae6b877 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/SubscriptionStatusTest.php @@ -0,0 +1,134 @@ +customerTokenService = Bootstrap::getObjectManager()->get(CustomerTokenServiceInterface::class); + $this->subscriberFactory = Bootstrap::getObjectManager()->get(SubscriberFactory::class); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + */ + public function testGetSubscriptionStatusTest() + { + $currentEmail = 'customer@example.com'; + $currentPassword = 'password'; + + $query = <<graphQlQuery($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); + $this->assertFalse($response['customer']['is_subscribed']); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage The current customer isn't authorized. + */ + public function testGetSubscriptionStatusIfUserIsNotAuthorizedTest() + { + $query = <<graphQlQuery($query); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + */ + public function testChangeSubscriptionStatusTest() + { + $currentEmail = 'customer@example.com'; + $currentPassword = 'password'; + + $query = <<graphQlQuery($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); + $this->assertTrue($response['updateCustomer']['customer']['is_subscribed']); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage The current customer isn't authorized. + */ + public function testChangeSubscriptionStatuIfUserIsNotAuthorizedTest() + { + $query = <<graphQlQuery($query); + } + + /** + * @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]; + } + + protected function tearDown() + { + parent::tearDown(); + + $this->subscriberFactory->create()->loadByCustomerId(1)->delete(); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/two_customers_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/two_customers_rollback.php new file mode 100644 index 0000000000000..4d8b712129b4c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/two_customers_rollback.php @@ -0,0 +1,31 @@ +get(\Magento\Framework\Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var CustomerRepositoryInterface $customerRepository */ +$customerRepository = Bootstrap::getObjectManager()->get(CustomerRepositoryInterface::class); +try { + $customerRepository->deleteById(2); +} 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/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlAlreadyExistsException.php b/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlAlreadyExistsException.php new file mode 100644 index 0000000000000..d25707b2ca347 --- /dev/null +++ b/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlAlreadyExistsException.php @@ -0,0 +1,56 @@ +isSafe = $isSafe; + parent::__construct($phrase, $cause, $code); + } + + /** + * @inheritdoc + */ + public function isClientSafe(): bool + { + return $this->isSafe; + } + + /** + * @inheritdoc + */ + public function getCategory(): string + { + return self::EXCEPTION_CATEGORY; + } +} diff --git a/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlAuthenticationException.php b/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlAuthenticationException.php new file mode 100644 index 0000000000000..4df74bbf332cd --- /dev/null +++ b/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlAuthenticationException.php @@ -0,0 +1,56 @@ +isSafe = $isSafe; + parent::__construct($phrase, $cause, $code); + } + + /** + * @inheritdoc + */ + public function isClientSafe(): bool + { + return $this->isSafe; + } + + /** + * @inheritdoc + */ + public function getCategory(): string + { + return self::EXCEPTION_CATEGORY; + } +} From 20e33937055d3d288384108ac88e2deb3f742a2a Mon Sep 17 00:00:00 2001 From: Valeriy Nayda Date: Mon, 8 Oct 2018 19:47:28 +0300 Subject: [PATCH 09/15] GraphQL-55: [Mutations] My Account > Change account information --- .../CustomerGraphQl/Model/Resolver/GenerateCustomerToken.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/GenerateCustomerToken.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/GenerateCustomerToken.php index 0405d63e49b81..9719d048f606b 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/GenerateCustomerToken.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/GenerateCustomerToken.php @@ -9,7 +9,7 @@ use Magento\Framework\Exception\AuthenticationException; use Magento\Framework\GraphQl\Config\Element\Field; -use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; +use Magento\Framework\GraphQl\Exception\GraphQlAuthenticationException; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; @@ -56,7 +56,7 @@ public function resolve( $token = $this->customerTokenService->createCustomerAccessToken($args['email'], $args['password']); return ['token' => $token]; } catch (AuthenticationException $e) { - throw new GraphQlAuthorizationException(__($e->getMessage()), $e); + throw new GraphQlAuthenticationException(__($e->getMessage()), $e); } } } From 661988b9bb73568c9daf493ee67f068e68bd9243 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda Date: Tue, 9 Oct 2018 15:01:34 +0300 Subject: [PATCH 10/15] GraphQL-55: [Mutations] My Account > Change account information --- .../Model/Resolver/ChangePassword.php | 6 +- .../Model/Resolver/Customer.php | 2 - .../Model/Resolver/IsSubscribed.php | 2 - .../Model/Resolver/UpdateCustomer.php | 2 - .../IsCartMutationAllowedForCurrentUser.php | 67 -------------- .../Model/Cart/AddProductsToCart.php | 61 +------------ .../Model/Cart/GetCartForUser.php | 89 +++++++++++++++++++ .../Resolver/AddSimpleProductsToCart.php | 14 ++- .../Model/Resolver/ApplyCouponToCart.php | 49 +++------- .../Model/Resolver/CreateEmptyCart.php | 12 +-- .../Model/Resolver/RemoveCouponFromCart.php | 46 +++------- ...thenticationTest.php => AddressesTest.php} | 40 +-------- .../Customer/CustomerChangePasswordTest.php | 24 ++--- .../GraphQl/Query/ResolverInterface.php | 2 +- 14 files changed, 146 insertions(+), 270 deletions(-) delete mode 100644 app/code/Magento/QuoteGraphQl/Model/Authorization/IsCartMutationAllowedForCurrentUser.php create mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php rename dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/{CustomerAuthenticationTest.php => AddressesTest.php} (84%) diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/ChangePassword.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/ChangePassword.php index e94db23ac1c2a..f4f651f683b8e 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/ChangePassword.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/ChangePassword.php @@ -13,7 +13,6 @@ use Magento\CustomerGraphQl\Model\Customer\CheckCustomerAccount; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Exception\GraphQlInputException; -use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; @@ -44,8 +43,8 @@ class ChangePassword implements ResolverInterface /** * @param CheckCustomerAccount $checkCustomerAccount - * @param CheckCustomerAccount $checkCustomerPassword - * @param CheckCustomerPassword $accountManagement + * @param CheckCustomerPassword $checkCustomerPassword + * @param AccountManagementInterface $accountManagement * @param CustomerDataProvider $customerDataProvider */ public function __construct( @@ -78,7 +77,6 @@ public function resolve( throw new GraphQlInputException(__('"newPassword" value should be specified')); } - /** @var ContextInterface $context */ $currentUserId = $context->getUserId(); $currentUserType = $context->getUserType(); $currentUserId = (int)$currentUserId; diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer.php index 29cde9edc6db3..c3c78a1004da6 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer.php @@ -11,7 +11,6 @@ use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\CustomerGraphQl\Model\Customer\CustomerDataProvider; use Magento\Framework\GraphQl\Config\Element\Field; -use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; use Magento\Framework\GraphQl\Query\ResolverInterface; /** @@ -51,7 +50,6 @@ public function resolve( array $value = null, array $args = null ) { - /** @var ContextInterface $context */ $currentUserId = $context->getUserId(); $currentUserType = $context->getUserType(); diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/IsSubscribed.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/IsSubscribed.php index bee116eec4afc..ff958d3733788 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/IsSubscribed.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/IsSubscribed.php @@ -10,7 +10,6 @@ use Magento\CustomerGraphQl\Model\Customer\CheckCustomerAccount; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Framework\GraphQl\Config\Element\Field; -use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Newsletter\Model\SubscriberFactory; @@ -51,7 +50,6 @@ public function resolve( array $value = null, array $args = null ) { - /** @var ContextInterface $context */ $currentUserId = $context->getUserId(); $currentUserType = $context->getUserType(); diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomer.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomer.php index 339f855b2f0c6..5dc857f3f178c 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomer.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomer.php @@ -14,7 +14,6 @@ use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\CustomerGraphQl\Model\Customer\CustomerDataProvider; use Magento\Framework\GraphQl\Config\Element\Field; -use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; use Magento\Framework\GraphQl\Query\ResolverInterface; /** @@ -74,7 +73,6 @@ public function resolve( throw new GraphQlInputException(__('"input" value should be specified')); } - /** @var ContextInterface $context */ $currentUserId = $context->getUserId(); $currentUserType = $context->getUserType(); diff --git a/app/code/Magento/QuoteGraphQl/Model/Authorization/IsCartMutationAllowedForCurrentUser.php b/app/code/Magento/QuoteGraphQl/Model/Authorization/IsCartMutationAllowedForCurrentUser.php deleted file mode 100644 index 2dec8c278800b..0000000000000 --- a/app/code/Magento/QuoteGraphQl/Model/Authorization/IsCartMutationAllowedForCurrentUser.php +++ /dev/null @@ -1,67 +0,0 @@ -userContext = $userContext; - $this->cartRepository = $cartRepository; - } - - /** - * Check that the shopping cart operations are allowed for current user - * - * @param int $quoteId - * @return bool - * @throws GraphQlNoSuchEntityException - */ - public function execute(int $quoteId): bool - { - try { - $quote = $this->cartRepository->get($quoteId); - } catch (NoSuchEntityException $exception) { - throw new GraphQlNoSuchEntityException(__($exception->getMessage())); - } - - $customerId = $quote->getCustomerId(); - - /* Guest cart, allow operations */ - if (!$customerId) { - return true; - } - - /* If the quote belongs to the current customer allow operations */ - return $customerId == $this->userContext->getUserId(); - } -} diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php index daec9411307f8..96259f2264943 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php @@ -7,71 +7,47 @@ namespace Magento\QuoteGraphQl\Model\Cart; -use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; use Magento\Framework\GraphQl\Exception\GraphQlInputException; -use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Framework\Message\AbstractMessage; use Magento\Quote\Api\CartRepositoryInterface; -use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface; use Magento\Quote\Model\Quote; -use Magento\QuoteGraphQl\Model\Authorization\IsCartMutationAllowedForCurrentUser; /** * Add products to cart */ class AddProductsToCart { - /** - * @var MaskedQuoteIdToQuoteIdInterface - */ - private $maskedQuoteIdToQuoteId; - /** * @var CartRepositoryInterface */ private $cartRepository; - /** - * @var IsCartMutationAllowedForCurrentUser - */ - private $isCartMutationAllowedForCurrentUser; - /** * @var AddSimpleProductToCart */ private $addProductToCart; /** - * @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId * @param CartRepositoryInterface $cartRepository - * @param IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser * @param AddSimpleProductToCart $addProductToCart */ public function __construct( - MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId, CartRepositoryInterface $cartRepository, - IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser, AddSimpleProductToCart $addProductToCart ) { - $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; $this->cartRepository = $cartRepository; - $this->isCartMutationAllowedForCurrentUser = $isCartMutationAllowedForCurrentUser; $this->addProductToCart = $addProductToCart; } /** * Add products to cart * - * @param string $cartHash + * @param Quote $cart * @param array $cartItems - * @return Quote * @throws GraphQlInputException */ - public function execute(string $cartHash, array $cartItems): Quote + public function execute(Quote $cart, array $cartItems): void { - $cart = $this->getCart($cartHash); - foreach ($cartItems as $cartItemData) { $this->addProductToCart->execute($cart, $cartItemData); } @@ -83,39 +59,6 @@ public function execute(string $cartHash, array $cartItems): Quote } $this->cartRepository->save($cart); - return $cart; - } - - /** - * Get cart - * - * @param string $cartHash - * @return Quote - * @throws GraphQlNoSuchEntityException - * @throws GraphQlAuthorizationException - */ - private function getCart(string $cartHash): Quote - { - try { - $cartId = $this->maskedQuoteIdToQuoteId->execute($cartHash); - $cart = $this->cartRepository->get($cartId); - } catch (NoSuchEntityException $e) { - throw new GraphQlNoSuchEntityException( - __('Could not find a cart with ID "%masked_cart_id"', ['masked_cart_id' => $cartHash]) - ); - } - - if (false === $this->isCartMutationAllowedForCurrentUser->execute($cartId)) { - throw new GraphQlAuthorizationException( - __( - 'The current user cannot perform operations on cart "%masked_cart_id"', - ['masked_cart_id' => $cartHash] - ) - ); - } - - /** @var Quote $cart */ - return $cart; } /** diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php b/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php new file mode 100644 index 0000000000000..9c50d4b85578b --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php @@ -0,0 +1,89 @@ +maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; + $this->cartRepository = $cartRepository; + } + + /** + * Get cart for user + * + * @param string $cartHash + * @param int|null $userId + * @return Quote + * @throws GraphQlAuthenticationException + * @throws GraphQlNoSuchEntityException + */ + public function execute(string $cartHash, ?int $userId): Quote + { + try { + $cartId = $this->maskedQuoteIdToQuoteId->execute($cartHash); + } catch (NoSuchEntityException $exception) { + throw new GraphQlNoSuchEntityException( + __('Could not find a cart with ID "%masked_cart_id"', ['masked_cart_id' => $cartHash]) + ); + } + + try { + /** @var Quote $cart */ + $cart = $this->cartRepository->get($cartId); + } catch (NoSuchEntityException $e) { + throw new GraphQlNoSuchEntityException( + __('Could not find a cart with ID "%masked_cart_id"', ['masked_cart_id' => $cartHash]) + ); + } + + $customerId = (int)$cart->getCustomerId(); + + /* Guest cart, allow operations */ + if (!$customerId) { + return $cart; + } + + if ($customerId !== $userId) { + throw new GraphQlAuthenticationException( + __( + 'The current user cannot perform operations on cart "%masked_cart_id"', + ['masked_cart_id' => $cartHash] + ) + ); + } + return $cart; + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/AddSimpleProductsToCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/AddSimpleProductsToCart.php index 31a0b3d02e44a..f4335b262c854 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/AddSimpleProductsToCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/AddSimpleProductsToCart.php @@ -14,6 +14,7 @@ use Magento\Framework\Stdlib\ArrayManager; use Magento\QuoteGraphQl\Model\Cart\AddProductsToCart; use Magento\QuoteGraphQl\Model\Cart\ExtractDataFromCart; +use Magento\QuoteGraphQl\Model\Cart\GetCartForUser; /** * Add simple products to cart GraphQl resolver @@ -26,6 +27,11 @@ class AddSimpleProductsToCart implements ResolverInterface */ private $arrayManager; + /** + * @var GetCartForUser + */ + private $getCartForUser; + /** * @var AddProductsToCart */ @@ -38,15 +44,18 @@ class AddSimpleProductsToCart implements ResolverInterface /** * @param ArrayManager $arrayManager + * @param GetCartForUser $getCartForUser * @param AddProductsToCart $addProductsToCart * @param ExtractDataFromCart $extractDataFromCart */ public function __construct( ArrayManager $arrayManager, + GetCartForUser $getCartForUser, AddProductsToCart $addProductsToCart, ExtractDataFromCart $extractDataFromCart ) { $this->arrayManager = $arrayManager; + $this->getCartForUser = $getCartForUser; $this->addProductsToCart = $addProductsToCart; $this->extractDataFromCart = $extractDataFromCart; } @@ -67,7 +76,10 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value throw new GraphQlInputException(__('Missing key "cartItems" in cart data')); } - $cart = $this->addProductsToCart->execute((string)$cartHash, $cartItems); + $currentUserId = $context->getUserId(); + $cart = $this->getCartForUser->execute((string)$cartHash, $currentUserId); + + $this->addProductsToCart->execute($cart, $cartItems); $cartData = $this->extractDataFromCart->execute($cart); return [ diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ApplyCouponToCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ApplyCouponToCart.php index ac5cb38326bc0..88f26fe4ea818 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/ApplyCouponToCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/ApplyCouponToCart.php @@ -8,16 +8,15 @@ namespace Magento\QuoteGraphQl\Model\Resolver; use Magento\Framework\Exception\CouldNotSaveException; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\GraphQl\Config\Element\Field; -use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Quote\Api\CouponManagementInterface; -use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface; -use Magento\QuoteGraphQl\Model\Authorization\IsCartMutationAllowedForCurrentUser; +use Magento\QuoteGraphQl\Model\Cart\GetCartForUser; /** * @inheritdoc @@ -25,33 +24,25 @@ class ApplyCouponToCart implements ResolverInterface { /** - * @var CouponManagementInterface + * @var GetCartForUser */ - private $couponManagement; + private $getCartForUser; /** - * @var MaskedQuoteIdToQuoteIdInterface - */ - private $maskedQuoteIdToQuoteId; - - /** - * @var IsCartMutationAllowedForCurrentUser + * @var CouponManagementInterface */ - private $isCartMutationAllowedForCurrentUser; + private $couponManagement; /** + * @param GetCartForUser $getCartForUser * @param CouponManagementInterface $couponManagement - * @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToId - * @param IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser */ public function __construct( - CouponManagementInterface $couponManagement, - MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToId, - IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser + GetCartForUser $getCartForUser, + CouponManagementInterface $couponManagement ) { + $this->getCartForUser = $getCartForUser; $this->couponManagement = $couponManagement; - $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToId; - $this->isCartMutationAllowedForCurrentUser = $isCartMutationAllowedForCurrentUser; } /** @@ -69,22 +60,10 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value } $couponCode = $args['input']['coupon_code']; - try { - $cartId = $this->maskedQuoteIdToQuoteId->execute($maskedCartId); - } catch (NoSuchEntityException $exception) { - throw new GraphQlNoSuchEntityException( - __('Could not find a cart with ID "%masked_cart_id"', ['masked_cart_id' => $maskedCartId]) - ); - } - if (false === $this->isCartMutationAllowedForCurrentUser->execute($cartId)) { - throw new GraphQlAuthorizationException( - __( - 'The current user cannot perform operations on cart "%masked_cart_id"', - ['masked_cart_id' => $maskedCartId] - ) - ); - } + $currentUserId = $context->getUserId(); + $cart = $this->getCartForUser->execute($maskedCartId, $currentUserId); + $cartId = $cart->getId(); /* Check current cart does not have coupon code applied */ $appliedCouponCode = $this->couponManagement->get($cartId); @@ -99,7 +78,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value } catch (NoSuchEntityException $exception) { throw new GraphQlNoSuchEntityException(__($exception->getMessage())); } catch (CouldNotSaveException $exception) { - throw new GraphQlInputException(__($exception->getMessage())); + throw new LocalizedException(__($exception->getMessage())); } $data['cart']['applied_coupon'] = [ diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/CreateEmptyCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/CreateEmptyCart.php index 1ccc2fda58e56..06123abe615e6 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/CreateEmptyCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/CreateEmptyCart.php @@ -7,7 +7,6 @@ namespace Magento\QuoteGraphQl\Model\Resolver; -use Magento\Authorization\Model\UserContextInterface; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; @@ -25,6 +24,7 @@ class CreateEmptyCart implements ResolverInterface * @var CartManagementInterface */ private $cartManagement; + /** * @var GuestCartManagementInterface */ @@ -35,11 +35,6 @@ class CreateEmptyCart implements ResolverInterface */ private $quoteIdToMaskedId; - /** - * @var UserContextInterface - */ - private $userContext; - /** * @var QuoteIdMaskFactory */ @@ -48,20 +43,17 @@ class CreateEmptyCart implements ResolverInterface /** * @param CartManagementInterface $cartManagement * @param GuestCartManagementInterface $guestCartManagement - * @param UserContextInterface $userContext * @param QuoteIdToMaskedQuoteIdInterface $quoteIdToMaskedId * @param QuoteIdMaskFactory $quoteIdMaskFactory */ public function __construct( CartManagementInterface $cartManagement, GuestCartManagementInterface $guestCartManagement, - UserContextInterface $userContext, QuoteIdToMaskedQuoteIdInterface $quoteIdToMaskedId, QuoteIdMaskFactory $quoteIdMaskFactory ) { $this->cartManagement = $cartManagement; $this->guestCartManagement = $guestCartManagement; - $this->userContext = $userContext; $this->quoteIdToMaskedId = $quoteIdToMaskedId; $this->quoteIdMaskFactory = $quoteIdMaskFactory; } @@ -71,7 +63,7 @@ public function __construct( */ public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) { - $customerId = $this->userContext->getUserId(); + $customerId = $context->getUserId(); if (0 !== $customerId && null !== $customerId) { $quoteId = $this->cartManagement->createEmptyCartForCustomer($customerId); diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/RemoveCouponFromCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/RemoveCouponFromCart.php index 40175cc589954..c21d869ddac7d 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/RemoveCouponFromCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/RemoveCouponFromCart.php @@ -8,16 +8,15 @@ namespace Magento\QuoteGraphQl\Model\Resolver; use Magento\Framework\Exception\CouldNotDeleteException; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\GraphQl\Config\Element\Field; -use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Quote\Api\CouponManagementInterface; -use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface; -use Magento\QuoteGraphQl\Model\Authorization\IsCartMutationAllowedForCurrentUser; +use Magento\QuoteGraphQl\Model\Cart\GetCartForUser; /** * @inheritdoc @@ -25,9 +24,9 @@ class RemoveCouponFromCart implements ResolverInterface { /** - * @var MaskedQuoteIdToQuoteIdInterface + * @var GetCartForUser */ - private $maskedQuoteIdToId; + private $getCartForUser; /** * @var CouponManagementInterface @@ -35,23 +34,15 @@ class RemoveCouponFromCart implements ResolverInterface private $couponManagement; /** - * @var IsCartMutationAllowedForCurrentUser - */ - private $isCartMutationAllowedForCurrentUser; - - /** + * @param GetCartForUser $getCartForUser * @param CouponManagementInterface $couponManagement - * @param IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser - * @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToId */ public function __construct( - CouponManagementInterface $couponManagement, - IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser, - MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToId + GetCartForUser $getCartForUser, + CouponManagementInterface $couponManagement ) { + $this->getCartForUser = $getCartForUser; $this->couponManagement = $couponManagement; - $this->isCartMutationAllowedForCurrentUser = $isCartMutationAllowedForCurrentUser; - $this->maskedQuoteIdToId = $maskedQuoteIdToId; } /** @@ -64,29 +55,16 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value } $maskedCartId = $args['input']['cart_id']; - try { - $cartId = $this->maskedQuoteIdToId->execute($maskedCartId); - } catch (NoSuchEntityException $exception) { - throw new GraphQlNoSuchEntityException( - __('Could not find a cart with ID "%masked_cart_id"', ['masked_cart_id' => $maskedCartId]) - ); - } - - if (false === $this->isCartMutationAllowedForCurrentUser->execute($cartId)) { - throw new GraphQlAuthorizationException( - __( - 'The current user cannot perform operations on cart "%masked_cart_id"', - ['masked_cart_id' => $maskedCartId] - ) - ); - } + $currentUserId = $context->getUserId(); + $cart = $this->getCartForUser->execute($maskedCartId, $currentUserId); + $cartId = $cart->getId(); try { $this->couponManagement->remove($cartId); } catch (NoSuchEntityException $exception) { throw new GraphQlNoSuchEntityException(__($exception->getMessage())); } catch (CouldNotDeleteException $exception) { - throw new GraphQlInputException(__($exception->getMessage())); + throw new LocalizedException(__($exception->getMessage())); } $data['cart']['applied_coupon'] = [ diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAuthenticationTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/AddressesTest.php similarity index 84% rename from dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAuthenticationTest.php rename to dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/AddressesTest.php index 88ce7e91d94bc..9b7e3f28327da 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAuthenticationTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/AddressesTest.php @@ -14,16 +14,13 @@ use Magento\TestFramework\TestCase\GraphQlAbstract; use Magento\Integration\Api\CustomerTokenServiceInterface; -class CustomerAuthenticationTest extends GraphQlAbstract +class AddressesTest extends GraphQlAbstract { /** - * Verify customers with valid credentials with a customer bearer token - * * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/Customer/_files/customer_two_addresses.php - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - public function testRegisteredCustomerWithValidCredentials() + public function testGetCustomerWithAddresses() { $query = <<assertCustomerAddressesFields($customer, $response); } - /** - * Verify customer with valid credentials but without the bearer token - * - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public function testCustomerWithValidCredentialsWithoutToken() - { - $query - = <<expectException(\Exception::class); - $this->expectExceptionMessage('GraphQL response contains errors: Current customer' . ' ' . - 'does not have access to the resource "customer"'); - $this->graphQlQuery($query); - } - /** * Verify the all the whitelisted fields for a Customer Object * diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerChangePasswordTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerChangePasswordTest.php index ede719bb569ba..f245181815217 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerChangePasswordTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerChangePasswordTest.php @@ -41,7 +41,7 @@ protected function setUp() /** * @magentoApiDataFixture Magento/Customer/_files/customer.php */ - public function testCustomerChangeValidPassword() + public function testChangePassword() { $customerEmail = 'customer@example.com'; $oldCustomerPassword = 'password'; @@ -62,14 +62,13 @@ public function testCustomerChangeValidPassword() } } - public function testGuestUserCannotChangePassword() + /** + * @expectedException \Exception + * @expectedExceptionMessage The current customer isn't authorized. + */ + public function testChangePasswordIfUserIsNotAuthorizedTest() { $query = $this->getChangePassQuery('currentpassword', 'newpassword'); - $this->expectException(\Exception::class); - $this->expectExceptionMessage( - 'GraphQL response contains errors: Current customer' . ' ' . - 'does not have access to the resource "customer"' - ); $this->graphQlQuery($query); } @@ -94,10 +93,11 @@ public function testChangeWeakPassword() /** * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @expectedException \Exception + * @expectedExceptionMessage The password doesn't match this account. Verify the password and try again. */ - public function testCannotChangeWithIncorrectPassword() + public function testChangePasswordIfPasswordIsInvalid() { - $this->markTestIncomplete('https://github.com/magento/graphql-ce/issues/190'); $customerEmail = 'customer@example.com'; $oldCustomerPassword = 'password'; $newCustomerPassword = 'anotherPassword1'; @@ -105,13 +105,7 @@ public function testCannotChangeWithIncorrectPassword() $query = $this->getChangePassQuery($incorrectPassword, $newCustomerPassword); - // acquire authentication with correct password $headerMap = $this->getCustomerAuthHeaders($customerEmail, $oldCustomerPassword); - - $this->expectException(\Exception::class); - $this->expectExceptionMessageRegExp('/The password doesn\'t match this account. Verify the password.*/'); - - // but try to change with incorrect 'old' password $this->graphQlQuery($query, [], '', $headerMap); } diff --git a/lib/internal/Magento/Framework/GraphQl/Query/ResolverInterface.php b/lib/internal/Magento/Framework/GraphQl/Query/ResolverInterface.php index 295113a98e465..f0450dce7f5f0 100644 --- a/lib/internal/Magento/Framework/GraphQl/Query/ResolverInterface.php +++ b/lib/internal/Magento/Framework/GraphQl/Query/ResolverInterface.php @@ -20,7 +20,7 @@ interface ResolverInterface * Fetches the data from persistence models and format it according to the GraphQL schema. * * @param \Magento\Framework\GraphQl\Config\Element\Field $field - * @param $context + * @param \Magento\Framework\GraphQl\Query\Resolver\ContextInterface $context * @param ResolveInfo $info * @param array|null $value * @param array|null $args From a1c4d33aaea0b0b608ea75e9e127b2f0cf35dd34 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda Date: Tue, 9 Oct 2018 15:17:31 +0300 Subject: [PATCH 11/15] GraphQL-55: [Mutations] My Account > Change account information -- fixes after merge with mainline --- .../Model/Resolver/ChangePassword.php | 4 +-- .../Model/Resolver/IsSubscribed.php | 2 +- .../Account => }/RevokeCustomerToken.php | 29 +++++++------------ .../CustomerGraphQl/etc/schema.graphqls | 2 +- .../Customer/RevokeCustomerTokenTest.php | 13 +++------ 5 files changed, 19 insertions(+), 31 deletions(-) rename app/code/Magento/CustomerGraphQl/Model/Resolver/{Customer/Account => }/RevokeCustomerToken.php (59%) diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/ChangePassword.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/ChangePassword.php index f4f651f683b8e..98b0975b37169 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/ChangePassword.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/ChangePassword.php @@ -79,9 +79,9 @@ public function resolve( $currentUserId = $context->getUserId(); $currentUserType = $context->getUserType(); - $currentUserId = (int)$currentUserId; - $this->checkCustomerAccount->execute($currentUserId, $currentUserType); + + $currentUserId = (int)$currentUserId; $this->checkCustomerPassword->execute($args['currentPassword'], $currentUserId); $this->accountManagement->changePasswordById($currentUserId, $args['currentPassword'], $args['newPassword']); diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/IsSubscribed.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/IsSubscribed.php index ff958d3733788..5dbdd91a6fef9 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/IsSubscribed.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/IsSubscribed.php @@ -55,7 +55,7 @@ public function resolve( $this->checkCustomerAccount->execute($currentUserId, $currentUserType); - $status = $this->subscriberFactory->create()->loadByCustomerId($currentUserId)->isSubscribed(); + $status = $this->subscriberFactory->create()->loadByCustomerId((int)$currentUserId)->isSubscribed(); return (bool)$status; } } diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/Account/RevokeCustomerToken.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/RevokeCustomerToken.php similarity index 59% rename from app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/Account/RevokeCustomerToken.php rename to app/code/Magento/CustomerGraphQl/Model/Resolver/RevokeCustomerToken.php index 7eb219a01217e..d3b16c05a6492 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/Account/RevokeCustomerToken.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/RevokeCustomerToken.php @@ -5,11 +5,10 @@ */ declare(strict_types=1); -namespace Magento\CustomerGraphQl\Model\Resolver\Customer\Account; +namespace Magento\CustomerGraphQl\Model\Resolver; -use Magento\Authorization\Model\UserContextInterface; +use Magento\CustomerGraphQl\Model\Customer\CheckCustomerAccount; use Magento\Framework\GraphQl\Config\Element\Field; -use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Integration\Api\CustomerTokenServiceInterface; @@ -20,9 +19,9 @@ class RevokeCustomerToken implements ResolverInterface { /** - * @var UserContextInterface + * @var CheckCustomerAccount */ - private $userContext; + private $checkCustomerAccount; /** * @var CustomerTokenServiceInterface @@ -30,14 +29,14 @@ class RevokeCustomerToken implements ResolverInterface private $customerTokenService; /** - * @param UserContextInterface $userContext + * @param CheckCustomerAccount $checkCustomerAccount * @param CustomerTokenServiceInterface $customerTokenService */ public function __construct( - UserContextInterface $userContext, + CheckCustomerAccount $checkCustomerAccount, CustomerTokenServiceInterface $customerTokenService ) { - $this->userContext = $userContext; + $this->checkCustomerAccount = $checkCustomerAccount; $this->customerTokenService = $customerTokenService; } @@ -51,17 +50,11 @@ public function resolve( array $value = null, array $args = null ) { - $customerId = (int)$this->userContext->getUserId(); + $currentUserId = $context->getUserId(); + $currentUserType = $context->getUserType(); - if ($customerId === 0) { - throw new GraphQlAuthorizationException( - __( - 'Current customer does not have access to the resource "%1"', - [\Magento\Customer\Model\Customer::ENTITY] - ) - ); - } + $this->checkCustomerAccount->execute($currentUserId, $currentUserType); - return $this->customerTokenService->revokeCustomerAccessToken($customerId); + return $this->customerTokenService->revokeCustomerAccessToken((int)$currentUserId); } } diff --git a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls index 00db881481ebc..b8411f00c5cb1 100644 --- a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls @@ -9,7 +9,7 @@ type Mutation { generateCustomerToken(email: String!, password: String!): CustomerToken @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\GenerateCustomerToken") @doc(description:"Retrieve Customer token") changeCustomerPassword(currentPassword: String!, newPassword: String!): Customer @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\ChangePassword") @doc(description:"Changes password for logged in customer") updateCustomer (input: UpdateCustomerInput): UpdateCustomerOutput @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\UpdateCustomer") @doc(description:"Update customer personal information") - revokeCustomerToken: Boolean @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\Customer\\Account\\RevokeCustomerToken") @doc(description:"Revoke Customer token") + revokeCustomerToken: Boolean @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\RevokeCustomerToken") @doc(description:"Revoke Customer token") } type CustomerToken { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/RevokeCustomerTokenTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/RevokeCustomerTokenTest.php index dac48bf01e7db..415a81f8cf45a 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/RevokeCustomerTokenTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/RevokeCustomerTokenTest.php @@ -7,6 +7,7 @@ namespace Magento\GraphQl\Customer; +use Magento\Integration\Api\CustomerTokenServiceInterface; use Magento\TestFramework\ObjectManager; use Magento\TestFramework\TestCase\GraphQlAbstract; @@ -16,7 +17,6 @@ class RevokeCustomerTokenTest extends GraphQlAbstract { /** - * Verify customers with valid credentials * @magentoApiDataFixture Magento/Customer/_files/customer.php */ public function testRevokeCustomerTokenValidCredentials() @@ -30,8 +30,7 @@ public function testRevokeCustomerTokenValidCredentials() $userName = 'customer@example.com'; $password = 'password'; /** @var CustomerTokenServiceInterface $customerTokenService */ - $customerTokenService = ObjectManager::getInstance() - ->get(\Magento\Integration\Api\CustomerTokenServiceInterface::class); + $customerTokenService = ObjectManager::getInstance()->get(CustomerTokenServiceInterface::class); $customerToken = $customerTokenService->createCustomerAccessToken($userName, $password); $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; @@ -40,7 +39,8 @@ public function testRevokeCustomerTokenValidCredentials() } /** - * Verify guest customers + * @expectedException \Exception + * @expectedExceptionMessage The current customer isn't authorized. */ public function testRevokeCustomerTokenForGuestCustomer() { @@ -49,11 +49,6 @@ public function testRevokeCustomerTokenForGuestCustomer() revokeCustomerToken } QUERY; - $this->expectException(\Exception::class); - $this->expectExceptionMessage( - 'GraphQL response contains errors: Current customer' . ' ' . - 'does not have access to the resource "customer"' - ); $this->graphQlQuery($query, [], ''); } } From 0cb378b23ebfcafdca5fda737d3ea2e6ebfdb915 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda Date: Tue, 9 Oct 2018 15:19:11 +0300 Subject: [PATCH 12/15] GraphQL-55: [Mutations] My Account > Change account information -- revert composer lock --- composer.lock | 146 +++++++++++++++++++++++++------------------------- 1 file changed, 73 insertions(+), 73 deletions(-) diff --git a/composer.lock b/composer.lock index c518379858c60..1d101c8aaaf15 100644 --- a/composer.lock +++ b/composer.lock @@ -1104,16 +1104,16 @@ }, { "name": "paragonie/sodium_compat", - "version": "v1.7.0", + "version": "v1.6.4", "source": { "type": "git", "url": "https://github.com/paragonie/sodium_compat.git", - "reference": "7b73005be3c224f12c47bd75a23ce24b762e47e8" + "reference": "3f2fd07977541b4d630ea0365ad0eceddee5179c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/7b73005be3c224f12c47bd75a23ce24b762e47e8", - "reference": "7b73005be3c224f12c47bd75a23ce24b762e47e8", + "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/3f2fd07977541b4d630ea0365ad0eceddee5179c", + "reference": "3f2fd07977541b4d630ea0365ad0eceddee5179c", "shasum": "" }, "require": { @@ -1182,7 +1182,7 @@ "secret-key cryptography", "side-channel resistant" ], - "time": "2018-09-22T03:59:58+00:00" + "time": "2018-08-29T22:02:48+00:00" }, { "name": "pelago/emogrifier", @@ -1834,16 +1834,16 @@ }, { "name": "symfony/console", - "version": "v4.1.5", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "d3dbe91fd5b8b11ecb73508c844bc6a490de15b4" + "reference": "ca80b8ced97cf07390078b29773dc384c39eee1f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/d3dbe91fd5b8b11ecb73508c844bc6a490de15b4", - "reference": "d3dbe91fd5b8b11ecb73508c844bc6a490de15b4", + "url": "https://api.github.com/repos/symfony/console/zipball/ca80b8ced97cf07390078b29773dc384c39eee1f", + "reference": "ca80b8ced97cf07390078b29773dc384c39eee1f", "shasum": "" }, "require": { @@ -1898,11 +1898,11 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2018-09-30T03:38:13+00:00" + "time": "2018-07-26T11:24:31+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v4.1.5", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", @@ -1965,16 +1965,16 @@ }, { "name": "symfony/filesystem", - "version": "v4.1.5", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "a10ae719b02c47ecba5c684ca2b505f3a49bf397" + "reference": "c0f5f62db218fa72195b8b8700e4b9b9cf52eb5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/a10ae719b02c47ecba5c684ca2b505f3a49bf397", - "reference": "a10ae719b02c47ecba5c684ca2b505f3a49bf397", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/c0f5f62db218fa72195b8b8700e4b9b9cf52eb5e", + "reference": "c0f5f62db218fa72195b8b8700e4b9b9cf52eb5e", "shasum": "" }, "require": { @@ -2011,20 +2011,20 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2018-09-30T03:38:13+00:00" + "time": "2018-08-18T16:52:46+00:00" }, { "name": "symfony/finder", - "version": "v4.1.5", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "f0b042d445c155501793e7b8007457f9f5bb1c8c" + "reference": "e162f1df3102d0b7472805a5a9d5db9fcf0a8068" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/f0b042d445c155501793e7b8007457f9f5bb1c8c", - "reference": "f0b042d445c155501793e7b8007457f9f5bb1c8c", + "url": "https://api.github.com/repos/symfony/finder/zipball/e162f1df3102d0b7472805a5a9d5db9fcf0a8068", + "reference": "e162f1df3102d0b7472805a5a9d5db9fcf0a8068", "shasum": "" }, "require": { @@ -2060,7 +2060,7 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2018-09-21T12:49:42+00:00" + "time": "2018-07-26T11:24:31+00:00" }, { "name": "symfony/polyfill-ctype", @@ -2181,16 +2181,16 @@ }, { "name": "symfony/process", - "version": "v4.1.5", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "c64647828bc7733ba9427f1eeb1b542588635427" + "reference": "86cdb930a6a855b0ab35fb60c1504cb36184f843" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/c64647828bc7733ba9427f1eeb1b542588635427", - "reference": "c64647828bc7733ba9427f1eeb1b542588635427", + "url": "https://api.github.com/repos/symfony/process/zipball/86cdb930a6a855b0ab35fb60c1504cb36184f843", + "reference": "86cdb930a6a855b0ab35fb60c1504cb36184f843", "shasum": "" }, "require": { @@ -2226,20 +2226,20 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2018-09-08T13:24:10+00:00" + "time": "2018-08-03T11:13:38+00:00" }, { "name": "tedivm/jshrink", - "version": "v1.3.1", + "version": "v1.3.0", "source": { "type": "git", "url": "https://github.com/tedious/JShrink.git", - "reference": "21254058dc3ce6aba6bef458cff4bfa25cf8b198" + "reference": "68ce379b213741e86f02bf6053b0d26b9f833448" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tedious/JShrink/zipball/21254058dc3ce6aba6bef458cff4bfa25cf8b198", - "reference": "21254058dc3ce6aba6bef458cff4bfa25cf8b198", + "url": "https://api.github.com/repos/tedious/JShrink/zipball/68ce379b213741e86f02bf6053b0d26b9f833448", + "reference": "68ce379b213741e86f02bf6053b0d26b9f833448", "shasum": "" }, "require": { @@ -2272,7 +2272,7 @@ "javascript", "minifier" ], - "time": "2018-09-16T00:02:51+00:00" + "time": "2017-12-08T00:59:56+00:00" }, { "name": "true/punycode", @@ -4804,16 +4804,16 @@ }, { "name": "consolidation/annotated-command", - "version": "2.9.1", + "version": "2.8.5", "source": { "type": "git", "url": "https://github.com/consolidation/annotated-command.git", - "reference": "4bdbb8fa149e1cc1511bd77b0bc4729fd66bccac" + "reference": "1e8ff512072422b850b44aa721b5b303e4a5ebb3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/4bdbb8fa149e1cc1511bd77b0bc4729fd66bccac", - "reference": "4bdbb8fa149e1cc1511bd77b0bc4729fd66bccac", + "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/1e8ff512072422b850b44aa721b5b303e4a5ebb3", + "reference": "1e8ff512072422b850b44aa721b5b303e4a5ebb3", "shasum": "" }, "require": { @@ -4852,7 +4852,7 @@ } ], "description": "Initialize Symfony Console commands from annotated command class methods.", - "time": "2018-09-19T17:47:18+00:00" + "time": "2018-08-18T23:51:49+00:00" }, { "name": "consolidation/config", @@ -8228,7 +8228,7 @@ }, { "name": "symfony/browser-kit", - "version": "v4.1.5", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", @@ -8285,16 +8285,16 @@ }, { "name": "symfony/config", - "version": "v4.1.5", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "b3d4d7b567d7a49e6dfafb6d4760abc921177c96" + "reference": "76015a3cc372b14d00040ff58e18e29f69eba717" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/b3d4d7b567d7a49e6dfafb6d4760abc921177c96", - "reference": "b3d4d7b567d7a49e6dfafb6d4760abc921177c96", + "url": "https://api.github.com/repos/symfony/config/zipball/76015a3cc372b14d00040ff58e18e29f69eba717", + "reference": "76015a3cc372b14d00040ff58e18e29f69eba717", "shasum": "" }, "require": { @@ -8344,20 +8344,20 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2018-09-08T13:24:10+00:00" + "time": "2018-08-08T06:37:38+00:00" }, { "name": "symfony/css-selector", - "version": "v4.1.5", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "9ac515bde3c725ca46efa918d37e37c7cece6353" + "reference": "2a4df7618f869b456f9096781e78c57b509d76c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/9ac515bde3c725ca46efa918d37e37c7cece6353", - "reference": "9ac515bde3c725ca46efa918d37e37c7cece6353", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/2a4df7618f869b456f9096781e78c57b509d76c7", + "reference": "2a4df7618f869b456f9096781e78c57b509d76c7", "shasum": "" }, "require": { @@ -8397,20 +8397,20 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2018-09-08T13:24:10+00:00" + "time": "2018-07-26T09:10:45+00:00" }, { "name": "symfony/dependency-injection", - "version": "v4.1.5", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "985ebee0d4cadaadef4d81aaccf0018443cf2560" + "reference": "bae4983003c9d451e278504d7d9b9d7fc1846873" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/985ebee0d4cadaadef4d81aaccf0018443cf2560", - "reference": "985ebee0d4cadaadef4d81aaccf0018443cf2560", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/bae4983003c9d451e278504d7d9b9d7fc1846873", + "reference": "bae4983003c9d451e278504d7d9b9d7fc1846873", "shasum": "" }, "require": { @@ -8468,20 +8468,20 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2018-09-21T12:49:42+00:00" + "time": "2018-08-08T11:48:58+00:00" }, { "name": "symfony/dom-crawler", - "version": "v4.1.5", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "8ffa4c496c782e5591182318a6199b7507431651" + "reference": "1c4519d257e652404c3aa550207ccd8ada66b38e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/8ffa4c496c782e5591182318a6199b7507431651", - "reference": "8ffa4c496c782e5591182318a6199b7507431651", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/1c4519d257e652404c3aa550207ccd8ada66b38e", + "reference": "1c4519d257e652404c3aa550207ccd8ada66b38e", "shasum": "" }, "require": { @@ -8525,20 +8525,20 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2018-09-21T12:49:42+00:00" + "time": "2018-07-26T11:00:49+00:00" }, { "name": "symfony/http-foundation", - "version": "v4.1.5", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "2ce66353d0a6ea96bc54bc9ecf8bcea4eaf5896c" + "reference": "3a5c91e133b220bb882b3cd773ba91bf39989345" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/2ce66353d0a6ea96bc54bc9ecf8bcea4eaf5896c", - "reference": "2ce66353d0a6ea96bc54bc9ecf8bcea4eaf5896c", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/3a5c91e133b220bb882b3cd773ba91bf39989345", + "reference": "3a5c91e133b220bb882b3cd773ba91bf39989345", "shasum": "" }, "require": { @@ -8579,20 +8579,20 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2018-09-30T03:47:35+00:00" + "time": "2018-08-27T17:47:02+00:00" }, { "name": "symfony/options-resolver", - "version": "v4.1.5", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "40f0e40d37c1c8a762334618dea597d64bbb75ff" + "reference": "1913f1962477cdbb13df951f8147d5da1fe2412c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/40f0e40d37c1c8a762334618dea597d64bbb75ff", - "reference": "40f0e40d37c1c8a762334618dea597d64bbb75ff", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/1913f1962477cdbb13df951f8147d5da1fe2412c", + "reference": "1913f1962477cdbb13df951f8147d5da1fe2412c", "shasum": "" }, "require": { @@ -8633,7 +8633,7 @@ "configuration", "options" ], - "time": "2018-09-18T12:45:12+00:00" + "time": "2018-07-26T08:55:25+00:00" }, { "name": "symfony/polyfill-php70", @@ -8751,7 +8751,7 @@ }, { "name": "symfony/stopwatch", - "version": "v4.1.5", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", @@ -8800,16 +8800,16 @@ }, { "name": "symfony/yaml", - "version": "v3.4.16", + "version": "v3.4.15", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "61973ecda60e9f3561e929e19c07d4878b960fc1" + "reference": "c2f4812ead9f847cb69e90917ca7502e6892d6b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/61973ecda60e9f3561e929e19c07d4878b960fc1", - "reference": "61973ecda60e9f3561e929e19c07d4878b960fc1", + "url": "https://api.github.com/repos/symfony/yaml/zipball/c2f4812ead9f847cb69e90917ca7502e6892d6b8", + "reference": "c2f4812ead9f847cb69e90917ca7502e6892d6b8", "shasum": "" }, "require": { @@ -8855,7 +8855,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2018-09-24T08:15:45+00:00" + "time": "2018-08-10T07:34:36+00:00" }, { "name": "theseer/fdomdocument", From 6e6b5c3649fab672804844e8c24d4601aad518bc Mon Sep 17 00:00:00 2001 From: Valeriy Nayda Date: Tue, 9 Oct 2018 15:33:02 +0300 Subject: [PATCH 13/15] GraphQL-55: [Mutations] My Account > Change account information -- fix static tests --- .../Magento/CustomerGraphQl/Model/Resolver/IsSubscribed.php | 2 +- .../Magento/QuoteGraphQl/Model/Resolver/ApplyCouponToCart.php | 1 - .../Magento/Customer/_files/two_customers_rollback.php | 1 + 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/IsSubscribed.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/IsSubscribed.php index 5dbdd91a6fef9..c0bd864b3ee09 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/IsSubscribed.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/IsSubscribed.php @@ -14,7 +14,7 @@ use Magento\Newsletter\Model\SubscriberFactory; /** - * is_subscribed customer field resolver + * Customer is_subscribed field resolver */ class IsSubscribed implements ResolverInterface { diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ApplyCouponToCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ApplyCouponToCart.php index 88f26fe4ea818..ec59416d49371 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/ApplyCouponToCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/ApplyCouponToCart.php @@ -60,7 +60,6 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value } $couponCode = $args['input']['coupon_code']; - $currentUserId = $context->getUserId(); $cart = $this->getCartForUser->execute($maskedCartId, $currentUserId); $cartId = $cart->getId(); diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/two_customers_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/two_customers_rollback.php index 4d8b712129b4c..5beb9e654de63 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/_files/two_customers_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/two_customers_rollback.php @@ -5,6 +5,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); require 'customer_rollback.php'; From 6f73325a34cd0b9304c603ed54c3686587ad32d8 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda Date: Tue, 9 Oct 2018 15:37:21 +0300 Subject: [PATCH 14/15] GraphQL-55: [Mutations] My Account > Change account information -- fix static tests --- app/code/Magento/CustomerGraphQl/composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CustomerGraphQl/composer.json b/app/code/Magento/CustomerGraphQl/composer.json index c17e9a605db00..488013c1ee586 100644 --- a/app/code/Magento/CustomerGraphQl/composer.json +++ b/app/code/Magento/CustomerGraphQl/composer.json @@ -6,8 +6,9 @@ "php": "~7.1.3||~7.2.0", "magento/module-customer": "*", "magento/module-authorization": "*", - "magento/newsletter": "*", + "magento/module-newsletter": "*", "magento/module-integration": "*", + "magento/module-store": "*", "magento/framework": "*" }, "suggest": { From 7ca6097e4ce7e0c7328de1f4a0da8910c4e2bba4 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda Date: Tue, 9 Oct 2018 15:52:32 +0300 Subject: [PATCH 15/15] GraphQL-55: [Mutations] My Account > Change account information -- fix static tests --- app/code/Magento/QuoteGraphQl/composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/QuoteGraphQl/composer.json b/app/code/Magento/QuoteGraphQl/composer.json index bed9157c4de59..c9900dd5f3150 100644 --- a/app/code/Magento/QuoteGraphQl/composer.json +++ b/app/code/Magento/QuoteGraphQl/composer.json @@ -5,7 +5,6 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/framework": "*", - "magento/module-authorization": "*", "magento/module-quote": "*", "magento/module-catalog": "*", "magento/module-store": "*"