Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GraphQL mutations for Reset password for MyAccount #27876

Merged
merged 8 commits into from
May 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\CustomerGraphQl\Model\Resolver;

use Magento\Customer\Api\AccountManagementInterface;
use Magento\Customer\Api\CustomerRepositoryInterface;
use Magento\Customer\Model\AccountManagement;
use Magento\Customer\Model\AuthenticationInterface;
use Magento\Framework\Exception\LocalizedException;
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\Resolver\Value;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Framework\Validator\EmailAddress as EmailValidator;

/**
* Class Resolver for RequestPasswordResetEmail
*/
class RequestPasswordResetEmail implements ResolverInterface
{
/**
* @var AuthenticationInterface
*/
private $authentication;

/**
* @var CustomerRepositoryInterface
*/
private $customerRepository;

/**
* @var AccountManagementInterface
*/
private $customerAccountManagement;

/**
* @var EmailValidator
*/
private $emailValidator;

/**
* RequestPasswordResetEmail constructor.
*
* @param AuthenticationInterface $authentication
* @param CustomerRepositoryInterface $customerRepository
* @param AccountManagementInterface $customerAccountManagement
* @param EmailValidator $emailValidator
*/
public function __construct(
AuthenticationInterface $authentication,
CustomerRepositoryInterface $customerRepository,
AccountManagementInterface $customerAccountManagement,
EmailValidator $emailValidator
) {
$this->authentication = $authentication;
$this->customerRepository = $customerRepository;
$this->customerAccountManagement = $customerAccountManagement;
$this->emailValidator = $emailValidator;
}

/**
* Send password email request
*
* @param Field $field
* @param ContextInterface $context
* @param ResolveInfo $info
* @param array|null $value
* @param array|null $args
*
* @return bool|Value|mixed
*
* @throws GraphQlInputException
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function resolve(
Field $field,
$context,
ResolveInfo $info,
array $value = null,
array $args = null
) {
if (empty($args['email'])) {
throw new GraphQlInputException(__('You must specify an email address.'));
}

if (!$this->emailValidator->isValid($args['email'])) {
throw new GraphQlInputException(__('The email address has an invalid format.'));
}

try {
$customer = $this->customerRepository->get($args['email']);
} catch (LocalizedException $e) {
throw new GraphQlInputException(__('Cannot reset the customer\'s password'), $e);
}

if (true === $this->authentication->isLocked($customer->getId())) {
throw new GraphQlInputException(__('The account is locked'));
}

try {
return $this->customerAccountManagement->initiatePasswordReset(
$args['email'],
AccountManagement::EMAIL_RESET
);
} catch (LocalizedException $e) {
throw new GraphQlInputException(__('Cannot reset the customer\'s password'), $e);
}
}
}
124 changes: 124 additions & 0 deletions app/code/Magento/CustomerGraphQl/Model/Resolver/ResetPassword.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\CustomerGraphQl\Model\Resolver;

use Magento\Customer\Api\AccountManagementInterface;
use Magento\Customer\Api\CustomerRepositoryInterface;
use Magento\Customer\Model\AuthenticationInterface;
use Magento\Framework\Exception\LocalizedException;
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\Resolver\Value;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Framework\Validator\EmailAddress as EmailValidator;

/**
* Class Resolver for ResetPassword
*/
class ResetPassword implements ResolverInterface
{
/**
* @var AccountManagementInterface
*/
private $customerAccountManagement;

/**
* @var EmailValidator
*/
private $emailValidator;

/**
* @var AuthenticationInterface
*/
private $authentication;

/**
* @var CustomerRepositoryInterface
*/
private $customerRepository;

/**
* ResetPassword constructor.
*
* @param AuthenticationInterface $authentication
* @param CustomerRepositoryInterface $customerRepository
* @param AccountManagementInterface $customerAccountManagement
* @param EmailValidator $emailValidator
*/
public function __construct(
AuthenticationInterface $authentication,
CustomerRepositoryInterface $customerRepository,
AccountManagementInterface $customerAccountManagement,
EmailValidator $emailValidator
) {
$this->authentication = $authentication;
$this->customerRepository = $customerRepository;
$this->customerAccountManagement = $customerAccountManagement;
$this->emailValidator = $emailValidator;
}

/**
* Reset old password and set new
*
* @param Field $field
* @param ContextInterface $context
* @param ResolveInfo $info
* @param array|null $value
* @param array|null $args
*
* @return bool|Value|mixed
*
* @throws GraphQlInputException
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function resolve(
Field $field,
$context,
ResolveInfo $info,
array $value = null,
array $args = null
) {
if (empty($args['email'])) {
throw new GraphQlInputException(__('You must specify an email address.'));
}

if (!$this->emailValidator->isValid($args['email'])) {
throw new GraphQlInputException(__('The email address has an invalid format.'));
}

if (empty($args['resetPasswordToken'])) {
throw new GraphQlInputException(__('resetPasswordToken must be specified'));
}

if (empty($args['newPassword'])) {
throw new GraphQlInputException(__('newPassword must be specified'));
}

try {
$customer = $this->customerRepository->get($args['email']);
} catch (LocalizedException $e) {
throw new GraphQlInputException(__('Cannot set the customer\'s password'), $e);
}

if (true === $this->authentication->isLocked($customer->getId())) {
throw new GraphQlInputException(__('The account is locked'));
}

try {
return $this->customerAccountManagement->resetPassword(
$args['email'],
$args['resetPasswordToken'],
$args['newPassword']
);
} catch (LocalizedException $e) {
throw new GraphQlInputException(__('Cannot set the customer\'s password'), $e);
}
}
}
9 changes: 9 additions & 0 deletions app/code/Magento/CustomerGraphQl/etc/graphql/di.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,13 @@
</argument>
</arguments>
</type>
<type name="Magento\StoreGraphQl\Model\Resolver\Store\StoreConfigDataProvider">
rogyar marked this conversation as resolved.
Show resolved Hide resolved
<arguments>
<argument name="extendedConfigData" xsi:type="array">
<item name="required_character_classes_number" xsi:type="string">customer/password/required_character_classes_number</item>
<item name="minimum_password_length" xsi:type="string">customer/password/minimum_password_length</item>
<item name="autocomplete_on_storefront" xsi:type="string">customer/password/autocomplete_on_storefront</item>
</argument>
</arguments>
</type>
</config>
8 changes: 8 additions & 0 deletions app/code/Magento/CustomerGraphQl/etc/schema.graphqls
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# Copyright © Magento, Inc. All rights reserved.
# See COPYING.txt for license details.

type StoreConfig {
required_character_classes_number : String @doc(description: "The number of different character classes required in a password (lowercase, uppercase, digits, special characters).")
minimum_password_length : String @doc(description: "The minimum number of characters required for a valid password.")
autocomplete_on_storefront : Boolean @doc(description: "Enable autocomplete on login and forgot password forms")
}

type Query {
customer: Customer @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\Customer") @doc(description: "The customer query returns information about a customer account") @cache(cacheable: false)
isEmailAvailable (
Expand All @@ -17,6 +23,8 @@ type Mutation {
createCustomerAddress(input: CustomerAddressInput!): CustomerAddress @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\CreateCustomerAddress") @doc(description: "Create customer address")
updateCustomerAddress(id: Int!, input: CustomerAddressInput): CustomerAddress @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\UpdateCustomerAddress") @doc(description: "Update customer address")
deleteCustomerAddress(id: Int!): Boolean @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\DeleteCustomerAddress") @doc(description: "Delete customer address")
requestPasswordResetEmail(email: String!): Boolean @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\RequestPasswordResetEmail") @doc(description: "Request an email with a reset password token for the registered customer identified by the specified email.")
resetPassword(email: String!, resetPasswordToken: String!, newPassword: String!): Boolean @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\ResetPassword") @doc(description: "Reset a customer's password using the reset password token that the customer received in an email after requesting it using requestPasswordResetEmail.")
}

input CustomerAddressInput {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\GraphQl\Customer;

use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\TestCase\GraphQlAbstract;

class RequestPasswordResetEmailTest extends GraphQlAbstract
lenaorobei marked this conversation as resolved.
Show resolved Hide resolved
{
/**
* @var LockCustomer
*/
private $lockCustomer;

protected function setUp(): void
{
parent::setUp();

$this->lockCustomer = Bootstrap::getObjectManager()->get(LockCustomer::class);
}
/**
* @magentoApiDataFixture Magento/Customer/_files/customer.php
*/
public function testCustomerAccountWithEmailAvailable()
{
$query =
<<<QUERY
mutation {
requestPasswordResetEmail(email: "customer@example.com")
}
QUERY;
$response = $this->graphQlMutation($query);

self::assertArrayHasKey('requestPasswordResetEmail', $response);
self::assertTrue($response['requestPasswordResetEmail']);
}

/**
* Check if customer account is not available
*
* @expectedException \Exception
* @expectedExceptionMessage Cannot reset the customer's password
*/
public function testCustomerAccountWithEmailNotAvailable()
{
$query =
<<<QUERY
mutation {
requestPasswordResetEmail(email: "customerNotAvalible@example.com")
}
QUERY;
$this->graphQlMutation($query);
}

/**
* Check if email value empty
*
* @expectedException \Exception
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, do not use exception annotations like expectedException and expectedExceptionMessage. This approach is deprecated in PHPUnit. Feel free to use the corresponding methods instead (self::expectException, self::expectExceptionMessage)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

* @expectedExceptionMessage You must specify an email address.
*/
public function testEmailAvailableEmptyValue()
{
$query = <<<QUERY
mutation {
requestPasswordResetEmail(email: "")
}
QUERY;
$this->graphQlMutation($query);
}

/**
* Check if email is invalid
*
* @expectedException \Exception
* @expectedExceptionMessage The email address has an invalid format.
*/
public function testEmailAvailableInvalidValue()
{
$query = <<<QUERY
mutation {
requestPasswordResetEmail(email: "invalid-email")
}
QUERY;
$this->graphQlMutation($query);
}

/**
* Check if email was sent for lock customer
*
* @magentoApiDataFixture Magento/Customer/_files/customer.php
*
* @expectedException \Exception
* @expectedExceptionMessage The account is locked
*/
public function testRequestPasswordResetEmailForLockCustomer()
{
$this->lockCustomer->execute(1);
$query =
<<<QUERY
mutation {
requestPasswordResetEmail(email: "customer@example.com")
}
QUERY;

$this->graphQlMutation($query);
}
}
Loading