Skip to content

Commit

Permalink
[frontend-api] registration after order refactoring (#3462)
Browse files Browse the repository at this point in the history
  • Loading branch information
vitek-rostislav authored Oct 16, 2024
2 parents 0c7df8d + 342b826 commit 410a586
Show file tree
Hide file tree
Showing 13 changed files with 183 additions and 79 deletions.
6 changes: 3 additions & 3 deletions src/Model/Customer/User/CustomerUserUpdateDataFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@
use Shopsys\FrameworkBundle\Model\Customer\User\CustomerUser;
use Shopsys\FrameworkBundle\Model\Customer\User\CustomerUserDataFactory as FrameworkCustomerUserDataFactory;
use Shopsys\FrameworkBundle\Model\Customer\User\CustomerUserUpdateData;
use Shopsys\FrameworkBundle\Model\Customer\User\CustomerUserUpdateDataFactoryInterface;
use Shopsys\FrameworkBundle\Model\Customer\User\CustomerUserUpdateDataFactory as FrameworkCustomerUserUpdateDataFactory;

class CustomerUserUpdateDataFactory
{
/**
* @param \Shopsys\FrameworkBundle\Model\Customer\User\CustomerUserUpdateDataFactoryInterface $customerUserUpdateDataFactory
* @param \Shopsys\FrameworkBundle\Model\Customer\User\CustomerUserUpdateDataFactory $customerUserUpdateDataFactory
* @param \Shopsys\FrameworkBundle\Model\Customer\BillingAddressDataFactory $billingAddressDataFactory
* @param \Shopsys\FrameworkBundle\Model\Customer\User\CustomerUserDataFactory $customerUserDataFactory
*/
public function __construct(
protected readonly CustomerUserUpdateDataFactoryInterface $customerUserUpdateDataFactory,
protected readonly FrameworkCustomerUserUpdateDataFactory $customerUserUpdateDataFactory,
protected readonly BillingAddressDataFactory $billingAddressDataFactory,
protected readonly FrameworkCustomerUserDataFactory $customerUserDataFactory,
) {
Expand Down
42 changes: 39 additions & 3 deletions src/Model/Customer/User/RegistrationFacade.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,18 @@

namespace Shopsys\FrontendApiBundle\Model\Customer\User;

use DateTime;
use Doctrine\ORM\EntityManagerInterface;
use Shopsys\FrameworkBundle\Component\Domain\Domain;
use Shopsys\FrameworkBundle\Model\Customer\Exception\DuplicateEmailException;
use Shopsys\FrameworkBundle\Model\Customer\User\CustomerUser;
use Shopsys\FrameworkBundle\Model\Customer\User\CustomerUserFacade;
use Shopsys\FrameworkBundle\Model\Customer\User\CustomerUserUpdateData;
use Shopsys\FrameworkBundle\Model\Customer\User\CustomerUserUpdateDataFactory as FrameworkCustomerUserUpdateDataFactory;
use Shopsys\FrameworkBundle\Model\Newsletter\NewsletterFacade;
use Shopsys\FrameworkBundle\Model\Order\Exception\OrderNotFoundException;
use Shopsys\FrameworkBundle\Model\Order\OrderFacade;
use Shopsys\FrontendApiBundle\Model\Order\Exception\RegisterByOrderIsNotPossibleUserError;

class RegistrationFacade
{
Expand All @@ -18,12 +24,18 @@ class RegistrationFacade
* @param \Shopsys\FrameworkBundle\Model\Customer\User\CustomerUserFacade $customerUserFacade
* @param \Shopsys\FrameworkBundle\Model\Newsletter\NewsletterFacade $newsletterFacade
* @param \Shopsys\FrameworkBundle\Component\Domain\Domain $domain
* @param \Shopsys\FrameworkBundle\Model\Order\OrderFacade $orderFacade
* @param \Doctrine\ORM\EntityManagerInterface $em
* @param \Shopsys\FrameworkBundle\Model\Customer\User\CustomerUserUpdateDataFactory $frameworkCustomerUserUpdateDataFactory
*/
public function __construct(
protected readonly CustomerUserUpdateDataFactory $customerUserUpdateDataFactory,
protected readonly CustomerUserFacade $customerUserFacade,
protected readonly NewsletterFacade $newsletterFacade,
protected readonly Domain $domain,
protected readonly OrderFacade $orderFacade,
protected readonly EntityManagerInterface $em,
protected readonly FrameworkCustomerUserUpdateDataFactory $frameworkCustomerUserUpdateDataFactory,
) {
}

Expand All @@ -49,12 +61,36 @@ public function register(RegistrationData $registrationData): CustomerUser

$customerUserUpdateData = $this->customerUserUpdateDataFactory->createFromRegistrationData($registrationData);

$customerUser = $this->customerUserFacade->create($customerUserUpdateData);
return $this->customerUserFacade->create($customerUserUpdateData);
}

if ($customerUser->isNewsletterSubscription()) {
$this->newsletterFacade->addSubscribedEmailIfNotExists($customerUser->getEmail(), $customerUser->getDomainId());
/**
* @param string $orderUrlHash
* @param string $password
* @return \Shopsys\FrameworkBundle\Model\Customer\User\CustomerUser
*/
public function registerByOrder(string $orderUrlHash, string $password): CustomerUser
{
try {
$order = $this->orderFacade->getByUrlHashAndDomain($orderUrlHash, $this->domain->getId());
} catch (OrderNotFoundException) {
throw new RegisterByOrderIsNotPossibleUserError('Order not found.');
}

if ($order->getCustomerUser() !== null) {
throw new RegisterByOrderIsNotPossibleUserError('Order is owned by another customer.');
}

if ($order->getCreatedAt() < new DateTime('-1 hour')) {
throw new RegisterByOrderIsNotPossibleUserError('Registration for a established order is possible only within an hour of establishment of an order.');
}

$customerUserUpdateData = $this->frameworkCustomerUserUpdateDataFactory->createFromOrder($order, $password);
$customerUser = $this->customerUserFacade->create($customerUserUpdateData);

$order->setCustomerUser($customerUser);
$this->em->flush();

return $customerUser;
}

Expand Down
50 changes: 34 additions & 16 deletions src/Model/Mutation/Customer/User/CustomerUserMutation.php
Original file line number Diff line number Diff line change
Expand Up @@ -155,26 +155,22 @@ public function registerMutation(Argument $argument, InputValidator $validator):
$this->mergeCartFacade->mergeCartByUuidToCustomerCart($argument['input']['cartUuid'], $customerUser);
}

if ($argument['input']['lastOrderUuid'] !== null) {
$this->orderFacade->pairCustomerUserWithOrderByOrderUuid($customerUser, $argument['input']['lastOrderUuid']);
}

$this->productListFacade->mergeProductListsToCustomerUser($argument['input']['productListsUuids'], $customerUser);

$deviceId = Uuid::uuid4()->toString();
return $this->loginRegisteredCustomerUser($customerUser);
}

$this->customerUserLoginTypeFacade->updateCustomerUserLoginTypes(
$this->customerUserLoginTypeDataFactory->create($customerUser, LoginTypeEnum::WEB),
);
/**
* @param \Overblog\GraphQLBundle\Definition\Argument $argument
* @return \Shopsys\FrontendApiBundle\Model\Security\LoginResultData
*/
public function registerByOrderMutation(Argument $argument): LoginResultData
{
$input = $argument['input'];
$customerUser = $this->registrationFacade->registerByOrder($input['orderUrlHash'], $input['password']);
$this->productListFacade->mergeProductListsToCustomerUser($input['productListsUuids'], $customerUser);

return $this->loginResultDataFactory->create(
$this->tokensDataFactory->create(
$this->tokenFacade->createAccessTokenAsString($customerUser, $deviceId),
$this->tokenFacade->createRefreshTokenAsString($customerUser, $deviceId),
),
$this->mergeCartFacade->shouldShowCartMergeInfo(),
true,
);
return $this->loginRegisteredCustomerUser($customerUser);
}

/**
Expand Down Expand Up @@ -295,4 +291,26 @@ protected function checkCustomerUserCanBeDeleted(
);
}
}

/**
* @param \Shopsys\FrameworkBundle\Model\Customer\User\CustomerUser $customerUser
* @return \Shopsys\FrontendApiBundle\Model\Security\LoginResultData
*/
protected function loginRegisteredCustomerUser(CustomerUser $customerUser): LoginResultData
{
$deviceId = Uuid::uuid4()->toString();

$this->customerUserLoginTypeFacade->updateCustomerUserLoginTypes(
$this->customerUserLoginTypeDataFactory->create($customerUser, LoginTypeEnum::WEB),
);

return $this->loginResultDataFactory->create(
$this->tokensDataFactory->create(
$this->tokenFacade->createAccessTokenAsString($customerUser, $deviceId),
$this->tokenFacade->createRefreshTokenAsString($customerUser, $deviceId),
),
$this->mergeCartFacade->shouldShowCartMergeInfo(),
true,
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
use Shopsys\FrameworkBundle\Model\Order\Exception\OrderException;
use Shopsys\FrontendApiBundle\Model\Error\UserErrorWithCodeInterface;

class OrderCannotBePairedException extends UserError implements OrderException, UserErrorWithCodeInterface
class RegisterByOrderIsNotPossibleUserError extends UserError implements OrderException, UserErrorWithCodeInterface
{
protected const CODE = 'order-cannot-be-paired-with-new-registration';
protected const string CODE = 'register-by-order-is-not-possible';

/**
* {@inheritdoc}
Expand Down
30 changes: 0 additions & 30 deletions src/Model/Order/OrderApiFacade.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,22 @@

namespace Shopsys\FrontendApiBundle\Model\Order;

use Doctrine\ORM\EntityManagerInterface;
use Shopsys\FrameworkBundle\Model\Customer\Customer;
use Shopsys\FrameworkBundle\Model\Customer\User\CustomerUser;
use Shopsys\FrameworkBundle\Model\Order\Exception\OrderNotFoundException;
use Shopsys\FrameworkBundle\Model\Order\Order;
use Shopsys\FrameworkBundle\Model\Order\OrderFacade;
use Shopsys\FrontendApiBundle\Model\Order\Exception\OrderCannotBePairedException;
use Shopsys\FrontendApiBundle\Model\Resolver\Order\Exception\OrderNotFoundUserError;

class OrderApiFacade
{
protected const int ONE_HOUR_REGISTRATION_WINDOW = 3600;

/**
* @param \Shopsys\FrontendApiBundle\Model\Order\OrderRepository $orderRepository
* @param \Shopsys\FrameworkBundle\Model\Order\OrderFacade $orderFacade
* @param \Doctrine\ORM\EntityManagerInterface $em
*/
public function __construct(
protected readonly OrderRepository $orderRepository,
protected readonly OrderFacade $orderFacade,
protected readonly EntityManagerInterface $em,
) {
}

Expand Down Expand Up @@ -103,30 +97,6 @@ public function findLastOrderByCustomerUser(CustomerUser $customerUser): ?Order
return $orderList[0];
}

/**
* @param \Shopsys\FrameworkBundle\Model\Customer\User\CustomerUser $customerUser
* @param string $orderUuid
*/
public function pairCustomerUserWithOrderByOrderUuid(CustomerUser $customerUser, string $orderUuid): void
{
$order = $this->getByUuid($orderUuid);

if ($order->getCustomerUser() !== null) {
throw new OrderCannotBePairedException('Order is owned by another customer.');
}

if ($order->getEmail() !== $customerUser->getEmail()) {
throw new OrderCannotBePairedException('Emails used in order and registration do not match.');
}

if ($order->getCreatedAt()->getTimestamp() < (time() - self::ONE_HOUR_REGISTRATION_WINDOW)) {
throw new OrderCannotBePairedException('Registration for a established order is possible only within an hour of establishment of an order.');
}

$order->setCustomerUser($customerUser);
$this->em->flush();
}

/**
* @param \Shopsys\FrameworkBundle\Model\Customer\Customer $customer
* @param int $limit
Expand Down
6 changes: 6 additions & 0 deletions src/Model/Resolver/Customer/User/CustomerUserResolverMap.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Overblog\GraphQLBundle\Resolver\ResolverMap;
use Shopsys\FrameworkBundle\Model\Customer\User\CustomerUser;
use Shopsys\FrameworkBundle\Model\Customer\User\Role\CustomerUserRoleResolver;
use Shopsys\FrameworkBundle\Model\Newsletter\NewsletterFacade;
use Shopsys\FrontendApiBundle\Model\Customer\User\LoginInfoFactory;
use Shopsys\FrontendApiBundle\Model\Customer\User\LoginType\CustomerUserLoginTypeFacade;
use Shopsys\FrontendApiBundle\Model\Customer\User\LoginType\Exception\MissingCustomerUserLoginTypeException;
Expand All @@ -17,11 +18,13 @@ class CustomerUserResolverMap extends ResolverMap
* @param \Shopsys\FrontendApiBundle\Model\Customer\User\LoginType\CustomerUserLoginTypeFacade $customerUserLoginTypeFacade
* @param \Shopsys\FrontendApiBundle\Model\Customer\User\LoginInfoFactory $loginInfoFactory
* @param \Shopsys\FrameworkBundle\Model\Customer\User\Role\CustomerUserRoleResolver $customerUserRoleResolver
* @param \Shopsys\FrameworkBundle\Model\Newsletter\NewsletterFacade $newsletterFacade
*/
public function __construct(
protected readonly CustomerUserLoginTypeFacade $customerUserLoginTypeFacade,
protected readonly LoginInfoFactory $loginInfoFactory,
protected readonly CustomerUserRoleResolver $customerUserRoleResolver,
protected readonly NewsletterFacade $newsletterFacade,
) {
}

Expand Down Expand Up @@ -67,6 +70,9 @@ protected function map(): array
'roles' => function (CustomerUser $customerUser) {
return $this->customerUserRoleResolver->getRolesForCustomerUser($customerUser);
},
'newsletterSubscription' => function (CustomerUser $customerUser) {
return $this->newsletterFacade->isSubscribed($customerUser);
},
];

return [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
NameInputObjectDecorator:
type: input-object
decorator: true
config:
fields:
firstName:
type: "String!"
description: "Customer user first name"
validation:
- NotBlank:
message: "Please enter first name"
- Length:
max: 100
maxMessage: "First name cannot be longer than {{ limit }} characters"
lastName:
type: "String!"
description: "Customer user last name"
validation:
- NotBlank:
message: "Please enter last name"
- Length:
max: 100
maxMessage: "Last name cannot be longer than {{ limit }} characters"
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
RegistrationByOrderInputDecorator:
type: input-object
decorator: true
config:
fields:
orderUrlHash:
type: "String!"
description: "Order URL hash"
validation:
- NotBlank:
message: "Please enter order URL hash"
password:
type: "Password!"
description: "Customer user password"
validation:
- NotBlank:
message: "Please enter new password"
- Length:
min: 6
minMessage: "Password must be at least {{ limit }} characters long"
productListsUuids:
type: "[Uuid!]!"
description: "Uuids of product lists that should be merged to the product lists of the user after registration"
defaultValue: []
Original file line number Diff line number Diff line change
@@ -1,30 +1,17 @@
RegistrationDataInputDecorator:
type: input-object
decorator: true
inherits:
- 'NameInputObjectDecorator'
- 'TelephoneInputObjectDecorator'
- 'BillingAddressInputObjectDecorator'
- 'CompanyInputObjectDecorator'
config:
description: "Represents the main input object to register customer user"
fields:
firstName:
type: "String!"
description: "Customer user first name"
validation:
- NotBlank:
message: "Please enter first name"
- Length:
max: 100
maxMessage: "First name cannot be longer than {{ limit }} characters"
lastName:
type: "String!"
description: "Customer user last name"
validation:
- NotBlank:
message: "Please enter last name"
- Length:
max: 100
maxMessage: "Last name cannot be longer than {{ limit }} characters"
email:
type: "String!"
description: "Customer user email."
description: "The customer's email address"
validation:
- NotBlank:
message: "Please enter email"
Expand All @@ -37,13 +24,20 @@ RegistrationDataInputDecorator:
message: "This email is already registered"
password:
type: "Password!"
description: "Customer user password."
description: "Customer user password"
validation:
- NotBlank:
message: "Please enter password"
message: "Please enter new password"
- Length:
min: 6
minMessage: "Password must be at least {{ limit }} characters long"
newsletterSubscription:
type: "Boolean!"
description: "Whether customer user should receive newsletters or not"
cartUuid:
type: "Uuid"
description: "Uuid of the cart that should be merged to the cart of the newly registered user"
defaultValue: null
productListsUuids:
type: "[Uuid!]!"
description: "Uuids of product lists that should be merged to the product lists of the user after registration"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
TelephoneInputObjectDecorator:
type: input-object
decorator: true
config:
fields:
telephone:
type: "String!"
description: "The customer's telephone number"
validation:
- NotBlank:
message: "Please enter telephone number"
- Length:
min: 9
minMessage: "Telephone number cannot be shorter than {{ limit }} characters"
max: 30
maxMessage: "Telephone number cannot be longer than {{ limit }} characters"
- Regex:
pattern: '/^[0-9\+]+$/'
message: "Please enter only numbers and the + sign"
Loading

0 comments on commit 410a586

Please sign in to comment.