Skip to content

Commit

Permalink
Provide logged in safe customer provider implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
lchrusciel committed Jan 3, 2019
1 parent 97507e6 commit 89de93e
Show file tree
Hide file tree
Showing 10 changed files with 251 additions and 261 deletions.
130 changes: 18 additions & 112 deletions spec/Handler/CompleteOrderHandlerSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,48 +5,36 @@
namespace spec\Sylius\ShopApiPlugin\Handler;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use SM\Factory\FactoryInterface as StateMachineFactoryInterface;
use SM\StateMachine\StateMachineInterface;
use Sylius\Component\Core\Model\CustomerInterface;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\ShopUserInterface;
use Sylius\Component\Core\OrderCheckoutTransitions;
use Sylius\Component\Core\Repository\CustomerRepositoryInterface;
use Sylius\Component\Core\Repository\OrderRepositoryInterface;
use Sylius\Component\Resource\Factory\FactoryInterface;
use Sylius\ShopApiPlugin\Command\CompleteOrder;
use Sylius\ShopApiPlugin\Exception\WrongUserException;
use Sylius\ShopApiPlugin\Provider\LoggedInShopUserProviderInterface;
use Symfony\Component\Security\Core\Exception\TokenNotFoundException;
use Sylius\ShopApiPlugin\Provider\CustomerProviderInterface;

final class CompleteOrderHandlerSpec extends ObjectBehavior
{
function let(
OrderRepositoryInterface $orderRepository,
CustomerRepositoryInterface $customerRepository,
FactoryInterface $customerFactory,
LoggedInShopUserProviderInterface $loggedInUserProvider,
CustomerProviderInterface $customerProvider,
StateMachineFactoryInterface $stateMachineFactory
): void {
$this->beConstructedWith($orderRepository, $customerRepository, $customerFactory, $loggedInUserProvider, $stateMachineFactory);
$this->beConstructedWith($orderRepository, $customerProvider, $stateMachineFactory);
}

function it_handles_order_completion_for_guest_checkout(
function it_handles_order_completion(
CustomerInterface $customer,
CustomerRepositoryInterface $customerRepository,
LoggedInShopUserProviderInterface $loggedInUserProvider,
FactoryInterface $customerFactory,
CustomerProviderInterface $customerProvider,
OrderInterface $order,
OrderRepositoryInterface $orderRepository,
StateMachineFactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine
): void {
$orderRepository->findOneBy(['tokenValue' => 'ORDERTOKEN'])->willReturn($order);

$customerRepository->findOneBy(['email' => 'example@customer.com'])->willReturn(null);
$customerFactory->createNew()->willReturn($customer);
$loggedInUserProvider->provide()->willThrow(TokenNotFoundException::class);
$customerProvider->provide('example@customer.com')->willReturn($customer);

$stateMachineFactory->get($order, OrderCheckoutTransitions::GRAPH)->willReturn($stateMachine);
$stateMachine->can('complete')->willReturn(true);
Expand All @@ -58,74 +46,17 @@ function it_handles_order_completion_for_guest_checkout(
$this->handle(new CompleteOrder('ORDERTOKEN', 'example@customer.com'));
}

function it_throws_an_exception_if_the_email_address_has_already_a_customer(
CustomerInterface $customer,
CustomerRepositoryInterface $customerRepository,
LoggedInShopUserProviderInterface $loggedInUserProvider,
ShopUserInterface $shopUser,
OrderInterface $order,
OrderRepositoryInterface $orderRepository,
StateMachineFactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine
): void {
$orderRepository->findOneBy(['tokenValue' => 'ORDERTOKEN'])->willReturn($order);

$customerRepository->findOneBy(['email' => 'example@customer.com'])->willReturn($customer);
$shopUser->getCustomer()->willReturn($customer);
$loggedInUserProvider->provide()->willThrow(TokenNotFoundException::class);

$stateMachineFactory->get($order, OrderCheckoutTransitions::GRAPH)->willReturn($stateMachine);
$stateMachine->can('complete')->willReturn(true);

$order->setNotes(Argument::any())->shouldNotBeCalled();
$order->setCustomer(Argument::any())->shouldNotBeCalled();
$stateMachine->apply(Argument::any())->shouldNotBeCalled();

$this->shouldThrow(WrongUserException::class)
->during('handle', [new CompleteOrder('ORDERTOKEN', 'example@customer.com')]);
}

function it_handles_order_completetion(
CustomerRepositoryInterface $customerRepository,
LoggedInShopUserProviderInterface $loggedInUserProvider,
CustomerInterface $loggedInCustomer,
ShopUserInterface $shopUser,
OrderInterface $order,
OrderRepositoryInterface $orderRepository,
StateMachineFactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine
): void {
$orderRepository->findOneBy(['tokenValue' => 'ORDERTOKEN'])->willReturn($order);

$customerRepository->findOneBy(Argument::any())->shouldNotBeCalled();
$shopUser->getCustomer()->willReturn($loggedInCustomer);
$loggedInUserProvider->provide()->willReturn($shopUser);

$stateMachineFactory->get($order, OrderCheckoutTransitions::GRAPH)->willReturn($stateMachine);
$stateMachine->can('complete')->willReturn(true);

$order->setCustomer($loggedInCustomer)->shouldBeCalled();
$order->setNotes(null)->shouldBeCalled();
$stateMachine->apply('complete')->shouldBeCalled();

$this->handle(new CompleteOrder('ORDERTOKEN', ''));
}

function it_handles_order_completion_with_notes(
CustomerInterface $customer,
CustomerRepositoryInterface $customerRepository,
LoggedInShopUserProviderInterface $loggedInUserProvider,
ShopUserInterface $shopUser,
CustomerProviderInterface $customerProvider,
OrderInterface $order,
OrderRepositoryInterface $orderRepository,
StateMachineFactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine
): void {
$orderRepository->findOneBy(['tokenValue' => 'ORDERTOKEN'])->willReturn($order);

$customerRepository->findOneBy(['email' => 'example@customer.com'])->willReturn($customer);
$shopUser->getCustomer()->willReturn($customer);
$loggedInUserProvider->provide()->willReturn($shopUser);
$customerProvider->provide('example@customer.com')->willReturn($customer);

$stateMachineFactory->get($order, OrderCheckoutTransitions::GRAPH)->willReturn($stateMachine);
$stateMachine->can('complete')->willReturn(true);
Expand All @@ -134,49 +65,21 @@ function it_handles_order_completion_with_notes(
$order->setCustomer($customer)->shouldBeCalled();
$stateMachine->apply('complete')->shouldBeCalled();

$this->handle(new CompleteOrder('ORDERTOKEN', '', 'Some notes'));
$this->handle(new CompleteOrder('ORDERTOKEN', 'example@customer.com', 'Some notes'));
}

function it_throws_an_exception_if_order_does_not_exist(
OrderRepositoryInterface $orderRepository
): void {
$orderRepository->findOneBy(['tokenValue' => 'ORDERTOKEN'])->willReturn(null);

$this->shouldThrow(\InvalidArgumentException::class)
->during('handle', [new CompleteOrder('ORDERTOKEN', 'example@customer.com')])
;
}

function it_throws_an_exception_if_the_user_is_logged_in_and_provides_email(
CustomerInterface $customer,
CustomerRepositoryInterface $customerRepository,
LoggedInShopUserProviderInterface $loggedInUserProvider,
CustomerInterface $loggedInCustomer,
ShopUserInterface $shopUser,
OrderInterface $order,
OrderRepositoryInterface $orderRepository,
StateMachineFactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine
): void {
$orderRepository->findOneBy(['tokenValue' => 'ORDERTOKEN'])->willReturn($order);

$customerRepository->findOneBy(Argument::any())->shouldNotBeCalled();
$shopUser->getCustomer()->willReturn($loggedInCustomer);
$loggedInUserProvider->provide()->willReturn($shopUser);

$stateMachineFactory->get($order, OrderCheckoutTransitions::GRAPH)->willReturn($stateMachine);
$stateMachine->can('complete')->willReturn(true);

$order->setCustomer($customer)->shouldNotBeCalled();
$order->setNotes('Some notes');
$stateMachine->apply('complete')->shouldNotBeCalled();

$this->shouldThrow(\InvalidArgumentException::class)
->during('handle', [new CompleteOrder('ORDERTOKEN', 'example@customer.com', 'Some notes')])
$this
->shouldThrow(\InvalidArgumentException::class)
->during('handle', [new CompleteOrder('ORDERTOKEN', 'example@customer.com')])
;
}

function it_throws_an_exception_if_order_cannot_be_addressed(
function it_throws_an_exception_if_order_cannot_be_completed(
StateMachineFactoryInterface $stateMachineFactory,
OrderInterface $order,
OrderRepositoryInterface $orderRepository,
Expand All @@ -185,8 +88,11 @@ function it_throws_an_exception_if_order_cannot_be_addressed(
$orderRepository->findOneBy(['tokenValue' => 'ORDERTOKEN'])->willReturn($order);

$stateMachineFactory->get($order, OrderCheckoutTransitions::GRAPH)->willReturn($stateMachine);
$stateMachine->can('complete')->willReturn(false);
$stateMachine->can(OrderCheckoutTransitions::TRANSITION_COMPLETE)->willReturn(false);

$this->shouldThrow(\InvalidArgumentException::class)->during('handle', [new CompleteOrder('ORDERTOKEN', 'example@customer.com')]);
$this
->shouldThrow(\InvalidArgumentException::class)
->during('handle', [new CompleteOrder('ORDERTOKEN', 'example@customer.com')])
;
}
}
45 changes: 0 additions & 45 deletions spec/Provider/CustomerProviderSpec.php

This file was deleted.

102 changes: 102 additions & 0 deletions spec/Provider/ShopUserAwareCustomerProviderSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php

declare(strict_types=1);

namespace spec\Sylius\ShopApiPlugin\Provider;

use PhpSpec\ObjectBehavior;
use Sylius\Component\Core\Model\CustomerInterface;
use Sylius\Component\Core\Model\ShopUserInterface;
use Sylius\Component\Core\Repository\CustomerRepositoryInterface;
use Sylius\Component\Resource\Factory\FactoryInterface;
use Sylius\ShopApiPlugin\Exception\WrongUserException;
use Sylius\ShopApiPlugin\Provider\CustomerProviderInterface;
use Sylius\ShopApiPlugin\Provider\LoggedInShopUserProviderInterface;

final class ShopUserAwareCustomerProviderSpec extends ObjectBehavior
{
function let(
CustomerRepositoryInterface $customerRepository,
FactoryInterface $customerFactory,
LoggedInShopUserProviderInterface $loggedInShopUserProvider
): void {
$this->beConstructedWith($customerRepository, $customerFactory, $loggedInShopUserProvider);
}

function it_is_customer_provider(): void
{
$this->shouldImplement(CustomerProviderInterface::class);
}

function it_provides_customer_from_reposiotory_if_it_does_not_have_related_shop_user(
CustomerRepositoryInterface $customerRepository,
CustomerInterface $customer,
LoggedInShopUserProviderInterface $loggedInShopUserProvider
): void {
$loggedInShopUserProvider->check()->willReturn(false);

$customerRepository->findOneBy(['email' => 'example@customer.com'])->willReturn($customer);

$customer->getUser()->willReturn(null);

$this->provide('example@customer.com')->shouldReturn($customer);
}

function it_creates_new_customer_if_it_does_not_exists(
CustomerRepositoryInterface $customerRepository,
FactoryInterface $customerFactory,
CustomerInterface $customer,
LoggedInShopUserProviderInterface $loggedInShopUserProvider
): void {
$loggedInShopUserProvider->check()->willReturn(false);
$customerRepository->findOneBy(['email' => 'example@customer.com'])->willReturn(null);
$customerFactory->createNew()->willReturn($customer);

$customer->setEmail('example@customer.com')->shouldBeCalled();
$customerRepository->add($customer)->shouldBeCalled();

$this->provide('example@customer.com')->shouldReturn($customer);
}

function it_provides_customer_from_reposiotory_if_it_has_related_shop_user_and_user_is_logged_in(
CustomerInterface $customer,
LoggedInShopUserProviderInterface $loggedInShopUserProvider,
ShopUserInterface $shopUser
): void {
$loggedInShopUserProvider->check()->willReturn(true);
$loggedInShopUserProvider->provide()->willReturn($shopUser);

$shopUser->getCustomer()->willReturn($customer);
$customer->getEmail()->willReturn('example@customer.com');

$this->provide('example@customer.com')->shouldReturn($customer);
}

function it_throws_an_exception_if_requested_customer_is_not_logged_in_but_has_related_shop_user(
CustomerRepositoryInterface $customerRepository,
CustomerInterface $customer,
LoggedInShopUserProviderInterface $loggedInShopUserProvider,
ShopUserInterface $shopUser
): void {
$customerRepository->findOneBy(['email' => 'example@customer.com'])->willReturn($customer);
$loggedInShopUserProvider->check()->willReturn(false);

$customer->getUser()->willReturn($shopUser);

$this->shouldThrow(WrongUserException::class)->during('provide', ['example@customer.com']);
}

function it_throws_an_exception_if_requested_customer_is_logged_in_but_customer_is_related_to_another_shop_user(
CustomerInterface $customer,
LoggedInShopUserProviderInterface $loggedInShopUserProvider,
ShopUserInterface $shopUser
): void {
$loggedInShopUserProvider->check()->willReturn(true);
$loggedInShopUserProvider->provide()->willReturn($shopUser);

$shopUser->getCustomer()->willReturn($customer);
$customer->getEmail()->willReturn('anotherCustomer@customer.com');

$this->shouldThrow(WrongUserException::class)->during('provide', ['example@customer.com']);
}
}
11 changes: 5 additions & 6 deletions src/Controller/Checkout/CompleteOrderAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
use Sylius\ShopApiPlugin\Provider\LoggedInShopUserProviderInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Exception\TokenNotFoundException;

final class CompleteOrderAction
{
Expand All @@ -37,11 +36,15 @@ public function __construct(

public function __invoke(Request $request): Response
{
if ($this->loggedInUserProvider->check()) {
$defaultEmail = $this->loggedInUserProvider->provide()->getEmail();
}

try {
$this->bus->handle(
new CompleteOrder(
$request->attributes->get('token'),
$request->request->get('email', ''),
$request->request->get('email', $defaultEmail ?? null),
$request->request->get('notes')
)
);
Expand All @@ -52,10 +55,6 @@ public function __invoke(Request $request): Response
Response::HTTP_UNAUTHORIZED
)
);
} catch (TokenNotFoundException $notLoggedInException) {
return $this->viewHandler->handle(
View::create('You need to be logged in', Response::HTTP_UNAUTHORIZED)
);
}

return $this->viewHandler->handle(View::create(null, Response::HTTP_NO_CONTENT));
Expand Down
Loading

0 comments on commit 89de93e

Please sign in to comment.