Skip to content

Commit

Permalink
Fix for the duplicate cart
Browse files Browse the repository at this point in the history
  • Loading branch information
mamazu committed Feb 16, 2019
1 parent 3433f27 commit 433ec63
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 9 deletions.
49 changes: 44 additions & 5 deletions spec/Handler/Cart/PickupCartHandlerSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,42 @@
namespace spec\Sylius\ShopApiPlugin\Handler\Cart;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Sylius\Component\Channel\Repository\ChannelRepositoryInterface;
use Sylius\Component\Core\Model\ChannelInterface;
use Sylius\Component\Core\Model\CustomerInterface;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\ShopUserInterface;
use Sylius\Component\Core\Repository\OrderRepositoryInterface;
use Sylius\Component\Currency\Model\CurrencyInterface;
use Sylius\Component\Locale\Model\LocaleInterface;
use Sylius\Component\Resource\Factory\FactoryInterface;
use Sylius\ShopApiPlugin\Command\PickupCart;
use Sylius\ShopApiPlugin\Provider\LoggedInShopUserProviderInterface;

final class PickupCartHandlerSpec extends ObjectBehavior
{
function let(FactoryInterface $cartFactory, OrderRepositoryInterface $cartRepository, ChannelRepositoryInterface $channelRepository): void
{
$this->beConstructedWith($cartFactory, $cartRepository, $channelRepository);
function let(
FactoryInterface $cartFactory,
OrderRepositoryInterface $cartRepository,
ChannelRepositoryInterface $channelRepository,
LoggedInShopUserProviderInterface $loggedInShopUserProvider
): void {
$this->beConstructedWith(
$cartFactory,
$cartRepository,
$channelRepository,
$loggedInShopUserProvider
);
}

function it_handles_cart_pickup(
function it_handles_cart_pickup_for_not_logged_in_user(
ChannelInterface $channel,
CurrencyInterface $currency,
ChannelRepositoryInterface $channelRepository,
FactoryInterface $cartFactory,
LocaleInterface $locale,
LoggedInShopUserProviderInterface $loggedInShopUserProvider,
OrderInterface $cart,
OrderRepositoryInterface $cartRepository
): void {
Expand All @@ -43,11 +57,36 @@ function it_handles_cart_pickup(
$cart->setCurrencyCode('EUR')->shouldBeCalled();
$cart->setLocaleCode('de_DE')->shouldBeCalled();

$cartRepository->add($cart)->shouldBeCalled();
$loggedInShopUserProvider->isUserLoggedIn()->willReturn(false);
$cart->setCustomer(Argument::any())->shouldNotBeCalled();

$cartRepository->add($cart)->shouldBeCalledOnce();

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

function it_handles_cart_pickup_for_a_logged_in_user(
ChannelInterface $channel,
ChannelRepositoryInterface $channelRepository,
LoggedInShopUserProviderInterface $loggedInShopUserProvider,
ShopUserInterface $user,
CustomerInterface $customer,
OrderInterface $cart,
OrderRepositoryInterface $cartRepository
): void {
$channelRepository->findOneByCode('CHANNEL_CODE')->willReturn($channel);

$loggedInShopUserProvider->isUserLoggedIn()->willReturn(true);
$loggedInShopUserProvider->provide()->willReturn($user);

$user->getCustomer()->willReturn($customer);
$cartRepository->findOneBy(['customer' => $customer, 'channel' => $channel])->willReturn($cart);
$cart->setTokenValue('ORDERTOKEN')->shouldBeCalled();

$cartRepository->add(Argument::any())->shouldNotBeCalled();

$this->handle(new PickupCart('ORDERTOKEN', 'CHANNEL_CODE'));
}
function it_throws_an_exception_if_channel_is_not_found(
ChannelRepositoryInterface $channelRepository
): void {
Expand Down
37 changes: 35 additions & 2 deletions src/Handler/Cart/PickupCartHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@

namespace Sylius\ShopApiPlugin\Handler\Cart;

use Sylius\Bundle\CoreBundle\EventListener\UserCartRecalculationListener;
use Sylius\Component\Channel\Repository\ChannelRepositoryInterface;
use Sylius\Component\Core\Model\ChannelInterface;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Repository\OrderRepositoryInterface;
use Sylius\Component\Resource\Factory\FactoryInterface;
use Sylius\ShopApiPlugin\Command\PickupCart;
use Sylius\ShopApiPlugin\Provider\LoggedInShopUserProviderInterface;
use Webmozart\Assert\Assert;

final class PickupCartHandler
Expand All @@ -23,11 +25,19 @@ final class PickupCartHandler
/** @var ChannelRepositoryInterface */
private $channelRepository;

public function __construct(FactoryInterface $cartFactory, OrderRepositoryInterface $cartRepository, ChannelRepositoryInterface $channelRepository)
{
/** @var LoggedInShopUserProviderInterface */
private $loggedInShopUserProvider;

public function __construct(
FactoryInterface $cartFactory,
OrderRepositoryInterface $cartRepository,
ChannelRepositoryInterface $channelRepository,
LoggedInShopUserProviderInterface $loggedInShopUserProvider
) {
$this->cartFactory = $cartFactory;
$this->cartRepository = $cartRepository;
$this->channelRepository = $channelRepository;
$this->loggedInShopUserProvider = $loggedInShopUserProvider;
}

/** @param PickupCart $pickupCart */
Expand All @@ -38,6 +48,12 @@ public function handle(PickupCart $pickupCart)

Assert::notNull($channel, sprintf('Channel with %s code has not been found.', $pickupCart->channelCode()));

if ($this->loggedInShopUserProvider->isUserLoggedIn()) {
$this->handleUserCart($pickupCart->orderToken(), $channel);

return;
}

/** @var OrderInterface $cart */
$cart = $this->cartFactory->createNew();
$cart->setChannel($channel);
Expand All @@ -47,4 +63,21 @@ public function handle(PickupCart $pickupCart)

$this->cartRepository->add($cart);
}

/**
* If the user is logged in we do not need to create a cart.
*
* The reason: The `UserCartRecalculationListener` is called for every authenticated request. In the case the cart does not exist it creates one.
*
* @see UserCartRecalculationListener
*/
private function handleUserCart(string $tokenValue, ChannelInterface $channel): void
{
$shopUser = $this->loggedInShopUserProvider->provide();
$customer = $shopUser->getCustomer();

/** @var OrderInterface $cart */
$cart = $this->cartRepository->findOneBy(['customer' => $customer, 'channel' => $channel]);
$cart->setTokenValue($tokenValue);
}
}
1 change: 1 addition & 0 deletions src/Resources/config/services/handler/cart.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<argument type="service" id="sylius.factory.order"/>
<argument type="service" id="sylius.repository.order"/>
<argument type="service" id="sylius.repository.channel"/>
<argument type="service" id="sylius.shop_api_plugin.provider.current_user_provider"/>
<tag name="tactician.handler" command="Sylius\ShopApiPlugin\Command\PickupCart"/>
</service>

Expand Down
39 changes: 37 additions & 2 deletions tests/Controller/Cart/CartPickupApiTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@
namespace Tests\Sylius\ShopApiPlugin\Controller\Cart;

use League\Tactician\CommandBus;
use Sylius\Component\Core\Repository\OrderRepositoryInterface;
use Sylius\ShopApiPlugin\Command\PickupCart;
use Symfony\Component\HttpFoundation\Response;
use Tests\Sylius\ShopApiPlugin\Controller\JsonApiTestCase;
use Tests\Sylius\ShopApiPlugin\Controller\Utils\ShopUserLoginTrait;

final class CartPickupApiTest extends JsonApiTestCase
{
use ShopUserLoginTrait;

/**
* @test
*/
Expand All @@ -23,23 +27,52 @@ public function it_creates_a_new_cart(): void
$response = $this->client->getResponse();

$this->assertResponse($response, 'cart/empty_response', Response::HTTP_CREATED);

$orderRepository = $this->get('sylius.repository.order');
$count = $orderRepository->count([]);

$this->assertSame(1, $count, 'Only one cart should be created');
}

/**
* @test
*/
public function it_only_creates_one_cart_if_user_is_logged_in(): void
{
$this->loadFixturesFromFiles(['shop.yml', 'customer.yml']);

$this->logInUser('oliver@queen.com', '123password');

$this->client->request('POST', '/shop-api/WEB_GB/carts', [], [], static::CONTENT_TYPE_HEADER);
$response = $this->client->getResponse();
$this->assertResponseCode($response, Response::HTTP_CREATED);

/** @var OrderRepositoryInterface $orderRepository */
$orderRepository = $this->get('sylius.repository.order');
$orders = $orderRepository->findAll();

$this->assertCount(1, $orders, 'Only one cart should be created');
}

/**
* @deprecated
* @test
*/
public function it_creates_a_new_cart_using_deprecated_api(): void
{
$this->loadFixturesFromFiles(['shop.yml']);

$this->client->request('POST', '/shop-api/WEB_GB/carts/SDAOSLEFNWU35H3QLI5325', [], [], self::CONTENT_TYPE_HEADER);
$this->client->request(
'POST', '/shop-api/WEB_GB/carts/SDAOSLEFNWU35H3QLI5325', [], [], self::CONTENT_TYPE_HEADER
);

$response = $this->client->getResponse();

$this->assertResponse($response, 'cart/deprecated_empty_response', Response::HTTP_CREATED);
}

/**
* @deprecated
* @test
*/
public function it_does_not_allow_to_create_a_new_cart_if_token_is_already_used(): void
Expand All @@ -66,7 +99,9 @@ public function it_does_not_allow_to_create_a_new_cart_in_non_existent_channel()
{
$this->loadFixturesFromFiles(['shop.yml']);

$this->client->request('POST', '/shop-api/SPACE_KLINGON/carts/SDAOSLEFNWU35H3QLI5325', [], [], self::CONTENT_TYPE_HEADER);
$this->client->request(
'POST', '/shop-api/SPACE_KLINGON/carts/SDAOSLEFNWU35H3QLI5325', [], [], self::CONTENT_TYPE_HEADER
);

$response = $this->client->getResponse();

Expand Down

0 comments on commit 433ec63

Please sign in to comment.