Skip to content

Commit

Permalink
make CheckoutResolver fail safe
Browse files Browse the repository at this point in the history
  • Loading branch information
vvasiloi committed Aug 14, 2021
1 parent c40d58d commit 119a091
Show file tree
Hide file tree
Showing 2 changed files with 224 additions and 13 deletions.
35 changes: 22 additions & 13 deletions src/Sylius/Bundle/CoreBundle/Checkout/CheckoutResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ public function __construct(
$this->stateMachineFactory = $stateMachineFactory;
}

public static function getSubscribedEvents(): array
{
return [
KernelEvents::REQUEST => 'onKernelRequest',
];
}

public function onKernelRequest(RequestEvent $event): void
{
if (!$event->isMasterRequest()) {
Expand All @@ -65,31 +72,33 @@ public function onKernelRequest(RequestEvent $event): void
$order = $this->cartContext->getCart();
if ($order->isEmpty()) {
$event->setResponse(new RedirectResponse($this->urlGenerator->generateForCart()));

return;
}

$stateMachine = $this->stateMachineFactory->get($order, $this->getRequestedGraph($request));
$graph = $this->getRequestedGraph($request);
$transition = $this->getRequestedTransition($request);

if ($stateMachine->can($this->getRequestedTransition($request))) {
if (null === $graph || null === $transition) {
return;
}

$event->setResponse(new RedirectResponse($this->urlGenerator->generateForOrderCheckoutState($order)));
}
$stateMachine = $this->stateMachineFactory->get($order, $graph);

public static function getSubscribedEvents(): array
{
return [
KernelEvents::REQUEST => 'onKernelRequest',
];
if ($stateMachine->can($transition)) {
return;
}

$event->setResponse(new RedirectResponse($this->urlGenerator->generateForOrderCheckoutState($order)));
}

private function getRequestedGraph(Request $request): string
private function getRequestedGraph(Request $request): ?string
{
return $request->attributes->get('_sylius')['state_machine']['graph'];
return $request->attributes->get('_sylius', [])['state_machine']['graph'] ?? null;
}

private function getRequestedTransition(Request $request): string
private function getRequestedTransition(Request $request): ?string
{
return $request->attributes->get('_sylius')['state_machine']['transition'];
return $request->attributes->get('_sylius', [])['state_machine']['transition'] ?? null;
}
}
202 changes: 202 additions & 0 deletions src/Sylius/Bundle/CoreBundle/spec/Checkout/CheckoutResolverSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace spec\Sylius\Bundle\CoreBundle\Checkout;

use Prophecy\Argument;
use SM\Factory\FactoryInterface;
use PhpSpec\ObjectBehavior;
use SM\StateMachine\StateMachineInterface;
use Sylius\Bundle\CoreBundle\Checkout\CheckoutStateUrlGeneratorInterface;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Order\Context\CartContextInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestMatcherInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;

final class CheckoutResolverSpec extends ObjectBehavior
{
function let(
CartContextInterface $cartContext,
CheckoutStateUrlGeneratorInterface $urlGenerator,
RequestMatcherInterface $requestMatcher,
FactoryInterface $stateMachineFactory
): void {
$this->beConstructedWith(
$cartContext,
$urlGenerator,
$requestMatcher,
$stateMachineFactory
);
}

function it_only_applies_to_the_main_request(RequestEvent $event): void
{
$event->isMasterRequest()->willReturn(false);
$event->getRequest()->shouldNotBeCalled();

$this->onKernelRequest($event);
}

function it_only_applies_to_a_mathched_request(
RequestEvent $event,
Request $request,
RequestMatcherInterface $requestMatcher,
CartContextInterface $cartContext
): void {
$event->isMasterRequest()->willReturn(true);
$event->getRequest()->willReturn($request);
$requestMatcher->matches($request)->willReturn(false);
$cartContext->getCart()->shouldNotBeCalled();

$this->onKernelRequest($event);
}

function it_redirects_when_order_has_no_items(
RequestEvent $event,
Request $request,
RequestMatcherInterface $requestMatcher,
CartContextInterface $cartContext,
OrderInterface $order,
CheckoutStateUrlGeneratorInterface $urlGenerator
): void {
$event->isMasterRequest()->willReturn(true);
$event->getRequest()->willReturn($request);
$requestMatcher->matches($request)->willReturn(true);
$cartContext->getCart()->willReturn($order);
$order->isEmpty()->willReturn(true);
$urlGenerator->generateForCart()->willReturn('/target-url');
$event->setResponse(Argument::type(RedirectResponse::class))->shouldBeCalled();

$this->onKernelRequest($event);
}

function it_does_nothing_when_there_is_no_sylius_request_attribute(
RequestEvent $event,
RequestMatcherInterface $requestMatcher,
CartContextInterface $cartContext,
OrderInterface $order,
FactoryInterface $stateMachineFactory
): void {
$request = new Request();
$event->isMasterRequest()->willReturn(true);
$event->getRequest()->willReturn($request);
$requestMatcher->matches($request)->willReturn(true);
$cartContext->getCart()->willReturn($order);
$order->isEmpty()->willReturn(false);
$stateMachineFactory->get($order, Argument::any())->shouldNotBeCalled();

$this->onKernelRequest($event);
}

function it_does_nothing_when_there_is_no_state_machine_request_attribute(
RequestEvent $event,
RequestMatcherInterface $requestMatcher,
CartContextInterface $cartContext,
OrderInterface $order,
FactoryInterface $stateMachineFactory
): void {
$request = new Request([], [], ['_sylius' => []]);
$event->isMasterRequest()->willReturn(true);
$event->getRequest()->willReturn($request);
$requestMatcher->matches($request)->willReturn(true);
$cartContext->getCart()->willReturn($order);
$order->isEmpty()->willReturn(false);
$stateMachineFactory->get($order, Argument::any())->shouldNotBeCalled();

$this->onKernelRequest($event);
}

function it_does_nothing_when_there_is_no_state_machine_graph_request_attribute(
RequestEvent $event,
RequestMatcherInterface $requestMatcher,
CartContextInterface $cartContext,
OrderInterface $order,
FactoryInterface $stateMachineFactory
): void {
$request = new Request([], [], ['_sylius' => ['state_machine' => ['transition' => 'test_transition']]]);
$event->isMasterRequest()->willReturn(true);
$event->getRequest()->willReturn($request);
$requestMatcher->matches($request)->willReturn(true);
$cartContext->getCart()->willReturn($order);
$order->isEmpty()->willReturn(false);
$stateMachineFactory->get($order, Argument::any())->shouldNotBeCalled();

$this->onKernelRequest($event);
}

function it_does_nothing_when_there_is_no_state_machine_transition_request_attribute(
RequestEvent $event,
RequestMatcherInterface $requestMatcher,
CartContextInterface $cartContext,
OrderInterface $order,
FactoryInterface $stateMachineFactory
): void {
$request = new Request([], [], ['_sylius' => ['state_machine' => ['graph' => 'test_graph']]]);
$event->isMasterRequest()->willReturn(true);
$event->getRequest()->willReturn($request);
$requestMatcher->matches($request)->willReturn(true);
$cartContext->getCart()->willReturn($order);
$order->isEmpty()->willReturn(false);
$stateMachineFactory->get($order, Argument::any())->shouldNotBeCalled();

$this->onKernelRequest($event);
}

function it_does_nothing_when_the_requested_transition_can_be_applied(
RequestEvent $event,
RequestMatcherInterface $requestMatcher,
CartContextInterface $cartContext,
OrderInterface $order,
FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine
): void {
$request = new Request([], [],
['_sylius' => ['state_machine' => ['graph' => 'test_graph', 'transition' => 'test_transition']]]);
$event->isMasterRequest()->willReturn(true);
$event->getRequest()->willReturn($request);
$requestMatcher->matches($request)->willReturn(true);
$cartContext->getCart()->willReturn($order);
$order->isEmpty()->willReturn(false);
$stateMachineFactory->get($order, 'test_graph')->willReturn($stateMachine);
$stateMachine->can('test_transition')->willReturn(true);
$event->setResponse(Argument::any())->shouldNotBeCalled();

$this->onKernelRequest($event);
}

function it_redirects_when_the_requested_transition_cannot_be_applied(
RequestEvent $event,
RequestMatcherInterface $requestMatcher,
CartContextInterface $cartContext,
OrderInterface $order,
FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine,
CheckoutStateUrlGeneratorInterface $urlGenerator
): void {
$request = new Request([], [],
['_sylius' => ['state_machine' => ['graph' => 'test_graph', 'transition' => 'test_transition']]]);
$event->isMasterRequest()->willReturn(true);
$event->getRequest()->willReturn($request);
$requestMatcher->matches($request)->willReturn(true);
$cartContext->getCart()->willReturn($order);
$order->isEmpty()->willReturn(false);
$stateMachineFactory->get($order, 'test_graph')->willReturn($stateMachine);
$stateMachine->can('test_transition')->willReturn(false);
$urlGenerator->generateForOrderCheckoutState($order)->willReturn('/target-url');
$event->setResponse(Argument::type(RedirectResponse::class))->shouldBeCalled();

$this->onKernelRequest($event);
}
}

0 comments on commit 119a091

Please sign in to comment.