From 354a5e5e7d37b3811dd7162fdc865996f43c82e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz?= Date: Fri, 20 Jan 2017 08:53:38 +0100 Subject: [PATCH 01/11] [API] Order to cart rename for routes --- .../config/routing/{order.yml => cart.yml} | 8 +-- .../routing/{order_item.yml => cart_item.yml} | 16 ++---- .../Resources/config/routing/main.yml | 12 ++--- .../{OrderApiTest.php => CartApiTest.php} | 50 +++++++++---------- .../{order => cart}/create_response.json | 0 .../create_validation_fail_response.json | 0 .../{order => cart}/index_response.json | 6 +-- .../{order => cart}/show_response.json | 0 8 files changed, 42 insertions(+), 50 deletions(-) rename src/Sylius/Bundle/ApiBundle/Resources/config/routing/{order.yml => cart.yml} (90%) rename src/Sylius/Bundle/ApiBundle/Resources/config/routing/{order_item.yml => cart_item.yml} (72%) rename tests/Controller/{OrderApiTest.php => CartApiTest.php} (63%) rename tests/Responses/Expected/{order => cart}/create_response.json (100%) rename tests/Responses/Expected/{order => cart}/create_validation_fail_response.json (100%) rename tests/Responses/Expected/{order => cart}/index_response.json (88%) rename tests/Responses/Expected/{order => cart}/show_response.json (100%) diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/routing/order.yml b/src/Sylius/Bundle/ApiBundle/Resources/config/routing/cart.yml similarity index 90% rename from src/Sylius/Bundle/ApiBundle/Resources/config/routing/order.yml rename to src/Sylius/Bundle/ApiBundle/Resources/config/routing/cart.yml index cfb770c584e..f13b1a4573e 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/routing/order.yml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/routing/cart.yml @@ -1,7 +1,7 @@ # This file is part of the Sylius package. # (c) Paweł Jędrzejewski -sylius_api_order_index: +sylius_api_cart_index: path: / methods: [GET] defaults: @@ -13,7 +13,7 @@ sylius_api_order_index: sorting: updatedAt: desc -sylius_api_order_create: +sylius_api_cart_create: path: / methods: [POST] defaults: @@ -22,7 +22,7 @@ sylius_api_order_create: serialization_version: $version form: Sylius\Bundle\ApiBundle\Form\Type\OrderType -sylius_api_order_delete: +sylius_api_cart_delete: path: /{id} methods: [DELETE] defaults: @@ -32,7 +32,7 @@ sylius_api_order_delete: serialization_version: $version csrf_protection: false -sylius_api_order_show: +sylius_api_cart_show: path: /{id} methods: [GET] defaults: diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/routing/order_item.yml b/src/Sylius/Bundle/ApiBundle/Resources/config/routing/cart_item.yml similarity index 72% rename from src/Sylius/Bundle/ApiBundle/Resources/config/routing/order_item.yml rename to src/Sylius/Bundle/ApiBundle/Resources/config/routing/cart_item.yml index 6301fb583b3..fa3e0dcbc6d 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/routing/order_item.yml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/routing/cart_item.yml @@ -1,18 +1,6 @@ # This file is part of the Sylius package. # (c) Paweł Jędrzejewski -sylius_api_order_item_index: - path: / - methods: [GET] - defaults: - _controller: sylius.controller.order_item:indexAction - _sylius: - serialization_version: $version - filterable: true - criteria: - order: $orderId - paginate: false - sylius_api_order_item_create: path: / methods: [POST] @@ -20,6 +8,10 @@ sylius_api_order_item_create: _controller: sylius.controller.order_item:createAction _sylius: serialization_version: $version + factory: + method: createForCart + arguments: + - expr:service('sylius.repository.order').find($cartId) sylius_api_order_item_update: path: /{id} diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/routing/main.yml b/src/Sylius/Bundle/ApiBundle/Resources/config/routing/main.yml index 9f24ae3f1f6..3197dbbfeb3 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/routing/main.yml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/routing/main.yml @@ -20,13 +20,13 @@ sylius_api_taxon: resource: "@SyliusApiBundle/Resources/config/routing/taxon.yml" prefix: /taxons -sylius_api_order: - resource: "@SyliusApiBundle/Resources/config/routing/order.yml" - prefix: /orders +sylius_api_cart: + resource: "@SyliusApiBundle/Resources/config/routing/cart.yml" + prefix: /carts -sylius_api_order_item: - resource: "@SyliusApiBundle/Resources/config/routing/order_item.yml" - prefix: /orders/{orderId}/items +sylius_api_cart_item: + resource: "@SyliusApiBundle/Resources/config/routing/cart_item.yml" + prefix: /carts/{cartId}/items sylius_api_adjustment: resource: "@SyliusApiBundle/Resources/config/routing/adjustment.yml" diff --git a/tests/Controller/OrderApiTest.php b/tests/Controller/CartApiTest.php similarity index 63% rename from tests/Controller/OrderApiTest.php rename to tests/Controller/CartApiTest.php index 79aba8d7e30..c7caf06f68a 100644 --- a/tests/Controller/OrderApiTest.php +++ b/tests/Controller/CartApiTest.php @@ -17,7 +17,7 @@ /** * @author Łukasz Chruściel */ -final class OrderApiTest extends JsonApiTestCase +final class CartApiTest extends JsonApiTestCase { /** * @var array @@ -32,7 +32,7 @@ final class OrderApiTest extends JsonApiTestCase */ public function it_denies_getting_an_order_for_non_authenticated_user() { - $this->client->request('GET', '/api/v1/orders/'); + $this->client->request('GET', '/api/v1/carts/'); $response = $this->client->getResponse(); $this->assertResponse($response, 'authentication/access_denied_response', Response::HTTP_UNAUTHORIZED); @@ -45,7 +45,7 @@ public function it_returns_not_found_response_when_requesting_details_of_an_orde { $this->loadFixturesFromFile('authentication/api_administrator.yml'); - $this->client->request('GET', '/api/v1/orders/-1', [], [], static::$authorizedHeaderWithContentType); + $this->client->request('GET', '/api/v1/carts/-1', [], [], static::$authorizedHeaderWithContentType); $response = $this->client->getResponse(); $this->assertResponse($response, 'error/not_found_response', Response::HTTP_NOT_FOUND); @@ -57,20 +57,20 @@ public function it_returns_not_found_response_when_requesting_details_of_an_orde public function it_allows_to_get_order() { $this->loadFixturesFromFile('authentication/api_administrator.yml'); - $orderData = $this->loadFixturesFromFile('resources/orders.yml'); + $orderData = $this->loadFixturesFromFile('resources/carts.yml'); - $this->client->request('GET', '/api/v1/orders/'.$orderData['order-001']->getId(), [], [], static::$authorizedHeaderWithContentType); + $this->client->request('GET', '/api/v1/carts/'.$orderData['order-001']->getId(), [], [], static::$authorizedHeaderWithContentType); $response = $this->client->getResponse(); - $this->assertResponse($response, 'order/show_response', Response::HTTP_OK); + $this->assertResponse($response, 'cart/show_response', Response::HTTP_OK); } /** * @test */ - public function it_denies_creating_order_for_non_authenticated_user() + public function it_denies_creating_cart_for_non_authenticated_user() { - $this->client->request('POST', '/api/v1/orders/'); + $this->client->request('POST', '/api/v1/carts/'); $response = $this->client->getResponse(); $this->assertResponse($response, 'authentication/access_denied_response', Response::HTTP_UNAUTHORIZED); @@ -79,23 +79,23 @@ public function it_denies_creating_order_for_non_authenticated_user() /** * @test */ - public function it_does_not_allow_to_create_order_without_specifying_required_data() + public function it_does_not_allow_to_create_cart_without_specifying_required_data() { $this->loadFixturesFromFile('authentication/api_administrator.yml'); - $this->client->request('POST', '/api/v1/orders/', [], [], static::$authorizedHeaderWithContentType); + $this->client->request('POST', '/api/v1/carts/', [], [], static::$authorizedHeaderWithContentType); $response = $this->client->getResponse(); - $this->assertResponse($response, 'order/create_validation_fail_response', Response::HTTP_BAD_REQUEST); + $this->assertResponse($response, 'cart/create_validation_fail_response', Response::HTTP_BAD_REQUEST); } /** * @test */ - public function it_allows_to_create_order() + public function it_allows_to_create_cart() { $this->loadFixturesFromFile('authentication/api_administrator.yml'); - $this->loadFixturesFromFile('resources/orders.yml'); + $this->loadFixturesFromFile('resources/carts.yml'); $data = <<client->request('POST', '/api/v1/orders/', [], [], static::$authorizedHeaderWithContentType, $data); + $this->client->request('POST', '/api/v1/carts/', [], [], static::$authorizedHeaderWithContentType, $data); $response = $this->client->getResponse(); - $this->assertResponse($response, 'order/show_response', Response::HTTP_CREATED); + $this->assertResponse($response, 'cart/show_response', Response::HTTP_CREATED); } /** * @test */ - public function it_denies_getting_orders_for_non_authenticated_user() + public function it_denies_getting_carts_for_non_authenticated_user() { - $this->client->request('GET', '/api/v1/orders/'); + $this->client->request('GET', '/api/v1/carts/'); $response = $this->client->getResponse(); $this->assertResponse($response, 'authentication/access_denied_response', Response::HTTP_UNAUTHORIZED); @@ -127,15 +127,15 @@ public function it_denies_getting_orders_for_non_authenticated_user() /** * @test */ - public function it_allows_to_get_orders_list() + public function it_allows_to_get_carts_list() { $this->loadFixturesFromFile('authentication/api_administrator.yml'); - $this->loadFixturesFromFile('resources/orders.yml'); + $this->loadFixturesFromFile('resources/carts.yml'); - $this->client->request('GET', '/api/v1/orders/', [], [], static::$authorizedHeaderWithContentType); + $this->client->request('GET', '/api/v1/carts/', [], [], static::$authorizedHeaderWithContentType); $response = $this->client->getResponse(); - $this->assertResponse($response, 'order/index_response', Response::HTTP_OK); + $this->assertResponse($response, 'cart/index_response', Response::HTTP_OK); } /** @@ -145,7 +145,7 @@ public function it_returns_not_found_response_when_trying_to_delete_order_which_ { $this->loadFixturesFromFile('authentication/api_administrator.yml'); - $this->client->request('DELETE', '/api/v1/orders/-1', [], [], static::$authorizedHeaderWithContentType); + $this->client->request('DELETE', '/api/v1/carts/-1', [], [], static::$authorizedHeaderWithContentType); $response = $this->client->getResponse(); $this->assertResponse($response, 'error/not_found_response', Response::HTTP_NOT_FOUND); @@ -157,14 +157,14 @@ public function it_returns_not_found_response_when_trying_to_delete_order_which_ public function it_allows_to_delete_order() { $this->loadFixturesFromFile('authentication/api_administrator.yml'); - $orders = $this->loadFixturesFromFile('resources/orders.yml'); + $carts = $this->loadFixturesFromFile('resources/carts.yml'); - $this->client->request('DELETE', '/api/v1/orders/'.$orders['order-001']->getId(), [], [], static::$authorizedHeaderWithContentType, []); + $this->client->request('DELETE', '/api/v1/carts/'.$carts['order-001']->getId(), [], [], static::$authorizedHeaderWithContentType, []); $response = $this->client->getResponse(); $this->assertResponseCode($response, Response::HTTP_NO_CONTENT); - $this->client->request('GET', '/api/v1/orders/'.$orders['order-001']->getId(), [], [], static::$authorizedHeaderWithContentType); + $this->client->request('GET', '/api/v1/carts/'.$carts['order-001']->getId(), [], [], static::$authorizedHeaderWithContentType); $response = $this->client->getResponse(); $this->assertResponse($response, 'error/not_found_response', Response::HTTP_NOT_FOUND); diff --git a/tests/Responses/Expected/order/create_response.json b/tests/Responses/Expected/cart/create_response.json similarity index 100% rename from tests/Responses/Expected/order/create_response.json rename to tests/Responses/Expected/cart/create_response.json diff --git a/tests/Responses/Expected/order/create_validation_fail_response.json b/tests/Responses/Expected/cart/create_validation_fail_response.json similarity index 100% rename from tests/Responses/Expected/order/create_validation_fail_response.json rename to tests/Responses/Expected/cart/create_validation_fail_response.json diff --git a/tests/Responses/Expected/order/index_response.json b/tests/Responses/Expected/cart/index_response.json similarity index 88% rename from tests/Responses/Expected/order/index_response.json rename to tests/Responses/Expected/cart/index_response.json index 4f8aa5b2db2..c7b7d1754d7 100644 --- a/tests/Responses/Expected/order/index_response.json +++ b/tests/Responses/Expected/cart/index_response.json @@ -5,13 +5,13 @@ "total": 1, "_links": { "self": { - "href": "\/api\/v1\/orders\/?page=1&limit=10" + "href": "\/api\/v1\/carts\/?page=1&limit=10" }, "first": { - "href": "\/api\/v1\/orders\/?page=1&limit=10" + "href": "\/api\/v1\/carts\/?page=1&limit=10" }, "last": { - "href": "\/api\/v1\/orders\/?page=1&limit=10" + "href": "\/api\/v1\/carts\/?page=1&limit=10" } }, "_embedded": { diff --git a/tests/Responses/Expected/order/show_response.json b/tests/Responses/Expected/cart/show_response.json similarity index 100% rename from tests/Responses/Expected/order/show_response.json rename to tests/Responses/Expected/cart/show_response.json From f0bf283fddecb26de73bf9dc3b1ac6e3fb52d236 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz?= Date: Fri, 20 Jan 2017 08:54:54 +0100 Subject: [PATCH 02/11] [Api] Adding to cart --- .../EventListener/AddToCartListener.php | 47 ++++++ .../ApiBundle/Form/Type/OrderItemType.php | 87 +++++++++++ .../Resources/config/routing/cart_item.yml | 2 + .../ApiBundle/Resources/config/services.xml | 5 + .../Resources/config/services/form.xml | 4 + .../config/serializer/Model.OrderItemUnit.yml | 2 +- .../Resources/translations/validators.en.yml | 4 + .../Core/Factory/CartItemFactory.php | 14 ++ .../Core/Factory/CartItemFactoryInterface.php | 8 ++ .../Core/spec/Factory/CartItemFactorySpec.php | 13 ++ tests/Controller/CartApiTest.php | 136 +++++++++++++++++- tests/DataFixtures/ORM/resources/carts.yml | 98 +++++++++++++ tests/DataFixtures/ORM/resources/orders.yml | 34 ----- ...art_quantity_validation_fail_response.json | 14 ++ .../Expected/cart/add_to_cart_response.json | 66 +++++++++ .../add_to_cart_validation_fail_response.json | 18 +++ ...to_cart_with_bigger_quantity_response.json | 86 +++++++++++ 17 files changed, 600 insertions(+), 38 deletions(-) create mode 100644 src/Sylius/Bundle/ApiBundle/EventListener/AddToCartListener.php create mode 100644 src/Sylius/Bundle/ApiBundle/Form/Type/OrderItemType.php create mode 100644 src/Sylius/Bundle/OrderBundle/Resources/translations/validators.en.yml create mode 100644 tests/DataFixtures/ORM/resources/carts.yml delete mode 100644 tests/DataFixtures/ORM/resources/orders.yml create mode 100644 tests/Responses/Expected/cart/add_to_cart_quantity_validation_fail_response.json create mode 100644 tests/Responses/Expected/cart/add_to_cart_response.json create mode 100644 tests/Responses/Expected/cart/add_to_cart_validation_fail_response.json create mode 100644 tests/Responses/Expected/cart/add_to_cart_with_bigger_quantity_response.json diff --git a/src/Sylius/Bundle/ApiBundle/EventListener/AddToCartListener.php b/src/Sylius/Bundle/ApiBundle/EventListener/AddToCartListener.php new file mode 100644 index 00000000000..315cf5d6086 --- /dev/null +++ b/src/Sylius/Bundle/ApiBundle/EventListener/AddToCartListener.php @@ -0,0 +1,47 @@ + + */ +final class AddToCartListener +{ + /** + * @var OrderProcessorInterface + */ + private $orderProcessor; + + /** + * @param OrderProcessorInterface $orderProcessor + */ + public function __construct(OrderProcessorInterface $orderProcessor) + { + $this->orderProcessor = $orderProcessor; + } + + /** + * @param GenericEvent $event + */ + public function cartItemResolver(GenericEvent $event) + { + $item = $event->getSubject(); + Assert::isInstanceOf($item, OrderItem::class); + + $this->orderProcessor->process($item->getOrder()); + } +} diff --git a/src/Sylius/Bundle/ApiBundle/Form/Type/OrderItemType.php b/src/Sylius/Bundle/ApiBundle/Form/Type/OrderItemType.php new file mode 100644 index 00000000000..d82ac820ebf --- /dev/null +++ b/src/Sylius/Bundle/ApiBundle/Form/Type/OrderItemType.php @@ -0,0 +1,87 @@ + + */ +final class OrderItemType extends AbstractType +{ + /** + * @var ProductVariantRepositoryInterface + */ + private $variantRepository; + + /** + * @param ProductVariantRepositoryInterface $variantRepository + */ + public function __construct(ProductVariantRepositoryInterface $variantRepository) + { + $this->variantRepository = $variantRepository; + } + + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->add('variant', TextType::class, [ + 'constraints' => [ + new NotBlank(['groups' => ['sylius']]), + ], + ]); + + $builder->get('variant')->addModelTransformer(new ResourceToIdentifierTransformer($this->variantRepository, 'code')); + + $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) { + $orderItem = $event->getData(); + + if (null === $orderItem) { + throw new UnexpectedTypeException($orderItem, OrderItemInterface::class); + } + + if (null !== $orderItem->getId()) { + $form = $event->getForm(); + + $form->remove('variant'); + } + }); + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return BaseOrderItemType::class; + } + + /** + * {@inheritdoc} + */ + public function getBlockPrefix() + { + return 'sylius_api_cart_item'; + } +} diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/routing/cart_item.yml b/src/Sylius/Bundle/ApiBundle/Resources/config/routing/cart_item.yml index fa3e0dcbc6d..fe5f48fb8d2 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/routing/cart_item.yml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/routing/cart_item.yml @@ -8,6 +8,7 @@ sylius_api_order_item_create: _controller: sylius.controller.order_item:createAction _sylius: serialization_version: $version + form: Sylius\Bundle\ApiBundle\Form\Type\OrderItemType factory: method: createForCart arguments: @@ -20,6 +21,7 @@ sylius_api_order_item_update: _controller: sylius.controller.order_item:updateAction _sylius: serialization_version: $version + form: Sylius\Bundle\ApiBundle\Form\Type\OrderItemType criteria: order: $orderId diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/services.xml b/src/Sylius/Bundle/ApiBundle/Resources/config/services.xml index b46dd8e1404..e0056a8fe7d 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/services.xml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/services.xml @@ -23,5 +23,10 @@ %fos_oauth_server.model.client.class% + + + + + diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/services/form.xml b/src/Sylius/Bundle/ApiBundle/Resources/config/services/form.xml index 2154fc030cd..7daab339858 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/services/form.xml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/services/form.xml @@ -52,5 +52,9 @@ + + + + diff --git a/src/Sylius/Bundle/CoreBundle/Resources/config/serializer/Model.OrderItemUnit.yml b/src/Sylius/Bundle/CoreBundle/Resources/config/serializer/Model.OrderItemUnit.yml index deb69553ca6..6d719b17db0 100644 --- a/src/Sylius/Bundle/CoreBundle/Resources/config/serializer/Model.OrderItemUnit.yml +++ b/src/Sylius/Bundle/CoreBundle/Resources/config/serializer/Model.OrderItemUnit.yml @@ -4,7 +4,7 @@ Sylius\Component\Core\Model\OrderItemUnit: relations: - rel: order href: - route: sylius_api_order_show + route: sylius_api_cart_show parameters: id: expr(object.getOrderItem().getOrder().getId()) version: 1 diff --git a/src/Sylius/Bundle/OrderBundle/Resources/translations/validators.en.yml b/src/Sylius/Bundle/OrderBundle/Resources/translations/validators.en.yml new file mode 100644 index 00000000000..b984f947d6b --- /dev/null +++ b/src/Sylius/Bundle/OrderBundle/Resources/translations/validators.en.yml @@ -0,0 +1,4 @@ +sylius: + order_item: + quantity: + min: Quantity of an order item cannot be lower than 1. diff --git a/src/Sylius/Component/Core/Factory/CartItemFactory.php b/src/Sylius/Component/Core/Factory/CartItemFactory.php index 21a4eaf4a93..a8e98212eab 100644 --- a/src/Sylius/Component/Core/Factory/CartItemFactory.php +++ b/src/Sylius/Component/Core/Factory/CartItemFactory.php @@ -11,6 +11,8 @@ namespace Sylius\Component\Core\Factory; +use Sylius\Component\Core\Model\OrderInterface; +use Sylius\Component\Core\Model\OrderItem; use Sylius\Component\Core\Model\ProductInterface; use Sylius\Component\Resource\Factory\FactoryInterface; use Sylius\Component\Product\Resolver\ProductVariantResolverInterface; @@ -58,4 +60,16 @@ public function createForProduct(ProductInterface $product) return $cartItem; } + + /** + * {@inheritdoc} + */ + public function createForCart(OrderInterface $order) + { + /** @var OrderItem $cartItem */ + $cartItem = $this->createNew(); + $cartItem->setOrder($order); + + return $cartItem; + } } diff --git a/src/Sylius/Component/Core/Factory/CartItemFactoryInterface.php b/src/Sylius/Component/Core/Factory/CartItemFactoryInterface.php index aa277e6a3b0..bbf7e4f39f2 100644 --- a/src/Sylius/Component/Core/Factory/CartItemFactoryInterface.php +++ b/src/Sylius/Component/Core/Factory/CartItemFactoryInterface.php @@ -11,6 +11,7 @@ namespace Sylius\Component\Core\Factory; +use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Order\Model\OrderItemInterface; use Sylius\Component\Core\Model\ProductInterface; use Sylius\Component\Resource\Factory\FactoryInterface; @@ -26,4 +27,11 @@ interface CartItemFactoryInterface extends FactoryInterface * @return OrderItemInterface */ public function createForProduct(ProductInterface $product); + + /** + * @param OrderInterface $order + * + * @return OrderItemInterface + */ + public function createForCart(OrderInterface $order); } diff --git a/src/Sylius/Component/Core/spec/Factory/CartItemFactorySpec.php b/src/Sylius/Component/Core/spec/Factory/CartItemFactorySpec.php index c9f97ae0ac3..e82a820651c 100644 --- a/src/Sylius/Component/Core/spec/Factory/CartItemFactorySpec.php +++ b/src/Sylius/Component/Core/spec/Factory/CartItemFactorySpec.php @@ -14,6 +14,7 @@ use PhpSpec\ObjectBehavior; use Sylius\Component\Core\Factory\CartItemFactory; use Sylius\Component\Core\Factory\CartItemFactoryInterface; +use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\OrderItemInterface; use Sylius\Component\Core\Model\ProductInterface; use Sylius\Component\Core\Model\ProductVariantInterface; @@ -66,4 +67,16 @@ function it_creates_a_cart_item_and_assigns_a_product_variant( $this->createForProduct($product)->shouldReturn($cartItem); } + + function it_creates_a_cart_item_for_given_cart( + FactoryInterface $decoratedFactory, + OrderItemInterface $cartItem, + OrderInterface $order + ) { + $decoratedFactory->createNew()->willReturn($cartItem); + + $cartItem->setOrder($order)->shouldBeCalled(); + + $this->createForCart($order)->shouldReturn($cartItem); + } } diff --git a/tests/Controller/CartApiTest.php b/tests/Controller/CartApiTest.php index c7caf06f68a..ec99dea7ac9 100644 --- a/tests/Controller/CartApiTest.php +++ b/tests/Controller/CartApiTest.php @@ -59,7 +59,7 @@ public function it_allows_to_get_order() $this->loadFixturesFromFile('authentication/api_administrator.yml'); $orderData = $this->loadFixturesFromFile('resources/carts.yml'); - $this->client->request('GET', '/api/v1/carts/'.$orderData['order-001']->getId(), [], [], static::$authorizedHeaderWithContentType); + $this->client->request('GET', '/api/v1/carts/'.$orderData['order_001']->getId(), [], [], static::$authorizedHeaderWithContentType); $response = $this->client->getResponse(); $this->assertResponse($response, 'cart/show_response', Response::HTTP_OK); @@ -159,14 +159,144 @@ public function it_allows_to_delete_order() $this->loadFixturesFromFile('authentication/api_administrator.yml'); $carts = $this->loadFixturesFromFile('resources/carts.yml'); - $this->client->request('DELETE', '/api/v1/carts/'.$carts['order-001']->getId(), [], [], static::$authorizedHeaderWithContentType, []); + $this->client->request('DELETE', '/api/v1/carts/'.$carts['order_001']->getId(), [], [], static::$authorizedHeaderWithContentType, []); $response = $this->client->getResponse(); $this->assertResponseCode($response, Response::HTTP_NO_CONTENT); - $this->client->request('GET', '/api/v1/carts/'.$carts['order-001']->getId(), [], [], static::$authorizedHeaderWithContentType); + $this->client->request('GET', '/api/v1/carts/'.$carts['order_001']->getId(), [], [], static::$authorizedHeaderWithContentType); $response = $this->client->getResponse(); $this->assertResponse($response, 'error/not_found_response', Response::HTTP_NOT_FOUND); } + + /** + * @test + */ + public function it_denies_adding_a_product_to_cart_for_non_authenticated_user() + { + $this->client->request('POST', '/api/v1/carts/1/items/'); + + $response = $this->client->getResponse(); + $this->assertResponse($response, 'authentication/access_denied_response', Response::HTTP_UNAUTHORIZED); + } + + /** + * @test + */ + public function it_does_not_allow_to_add_item_to_cart_without_providing_all_needed_fields() + { + $this->loadFixturesFromFile('authentication/api_administrator.yml'); + $carts = $this->loadFixturesFromFile('resources/carts.yml'); + + $this->client->request('POST', sprintf('/api/v1/carts/%s/items/', $carts['order_001']->getId()), [], [], static::$authorizedHeaderWithContentType); + + $response = $this->client->getResponse(); + $this->assertResponse($response, 'cart/add_to_cart_validation_fail_response', Response::HTTP_BAD_REQUEST); + } + + /** + * @test + */ + public function it_adds_an_item_to_the_cart() + { + $this->loadFixturesFromFile('authentication/api_administrator.yml'); + $carts = $this->loadFixturesFromFile('resources/carts.yml'); + + $data = +<<client->request('POST', sprintf('/api/v1/carts/%s/items/', $carts['order_001']->getId()), [], [], static::$authorizedHeaderWithContentType, $data); + + $response = $this->client->getResponse(); + $this->assertResponse($response, 'cart/add_to_cart_response', Response::HTTP_CREATED); + } + + /** + * @test + */ + public function it_does_not_allow_to_add_item_with_negative_quantity() + { + $this->loadFixturesFromFile('authentication/api_administrator.yml'); + $carts = $this->loadFixturesFromFile('resources/carts.yml'); + + $data = +<<client->request('POST', sprintf('/api/v1/carts/%s/items/', $carts['order_001']->getId()), [], [], static::$authorizedHeaderWithContentType, $data); + + $response = $this->client->getResponse(); + $this->assertResponse($response, 'cart/add_to_cart_quantity_validation_fail_response', Response::HTTP_BAD_REQUEST); + } + + /** + * @test + */ + public function it_adds_an_item_to_the_cart_with_quantity_bigger_than_one() + { + $this->loadFixturesFromFile('authentication/api_administrator.yml'); + $carts = $this->loadFixturesFromFile('resources/carts.yml'); + + $data = +<<client->request('POST', sprintf('/api/v1/carts/%s/items/', $carts['order_001']->getId()), [], [], static::$authorizedHeaderWithContentType, $data); + + $response = $this->client->getResponse(); + $this->assertResponse($response, 'cart/add_to_cart_with_bigger_quantity_response', Response::HTTP_CREATED); + } + + /** + * @test + */ + public function it_adds_an_item_to_the_cart_with_that_contains_tracked_variant() + { + $this->loadFixturesFromFile('authentication/api_administrator.yml'); + $carts = $this->loadFixturesFromFile('resources/carts.yml'); + + $data = +<<client->request('POST', sprintf('/api/v1/carts/%s/items/', $carts['order_001']->getId()), [], [], static::$authorizedHeaderWithContentType, $data); + + $response = $this->client->getResponse(); + $this->assertResponse($response, 'cart/add_to_cart_hard_available_item_response', Response::HTTP_CREATED); + } + + /** + * @test + */ + public function it_checks_if_requested_variant_is_available() + { + $this->loadFixturesFromFile('authentication/api_administrator.yml'); + $carts = $this->loadFixturesFromFile('resources/carts.yml'); + + $data = +<<client->request('POST', sprintf('/api/v1/carts/%s/items/', $carts['order_001']->getId()), [], [], static::$authorizedHeaderWithContentType, $data); + + $response = $this->client->getResponse(); + $this->assertResponse($response, 'cart/add_to_cart_hard_available_item_validation_error_response', Response::HTTP_BAD_REQUEST); + } } diff --git a/tests/DataFixtures/ORM/resources/carts.yml b/tests/DataFixtures/ORM/resources/carts.yml new file mode 100644 index 00000000000..80e518324cd --- /dev/null +++ b/tests/DataFixtures/ORM/resources/carts.yml @@ -0,0 +1,98 @@ +Sylius\Component\Core\Model\Order: + order_001: + channel: "@channel_web" + currencyCode: USD + localeCode: en_US + customer: "@customer_oliver" + +Sylius\Component\Core\Model\Channel: + channel_web: + code: "WEB" + name: "Web Channel" + hostname: "localhost" + description: "Lorem ipsum" + baseCurrency: "@currency" + defaultLocale: "@locale" + color: "black" + enabled: true + taxCalculationStrategy: "order_items_based" + +Sylius\Component\Currency\Model\Currency: + currency: + code: USD + +Sylius\Component\Locale\Model\Locale: + locale: + code: en_US + enabled: true + +Sylius\Component\Core\Model\Customer: + customer_oliver: + firstName: Oliver + lastName: Queen + email: oliver.queen@star-city.com + emailCanonical: oliver.queen@star-city.com + birthday: + +Sylius\Component\Core\Model\Product: + mug: + code: MUG + channels: ["@channel_web"] + currentLocale: en_US + currentTranslation: "@mug_translation" + +Sylius\Component\Core\Model\ProductTranslation: + mug_translation: + slug: mug + locale: en_US + name: 'Mug' + description: + translatable: "@mug" + +Sylius\Component\Core\Model\ProductVariant: + mug_sw: + code: MUG_SW + product: "@mug" + currentLocale: en_US + currentTranslation: "@sw_mug_translation" + updatedAt: 2015-10-10 + channelPricings: ["@sw_mug_web_channel_pricing"] + mug_lotr: + code: MUG_LOTR + product: "@mug" + currentLocale: en_US + currentTranslation: "@lotr_mug_translation" + updatedAt: 2015-10-04 + channelPricings: ["@lotr_mug_web_channel_pricing"] + mug_bb: + code: MUG_BB + product: "@mug" + currentLocale: en_US + currentTranslation: "@bb_mug_translation" + updatedAt: 2015-10-05 + channelPricings: ["@bb_mug_web_channel_pricing"] + +Sylius\Component\Product\Model\ProductVariantTranslation: + sw_mug_translation: + locale: en_US + name: 'Star wars mug' + translatable: "@mug_sw" + lotr_mug_translation: + locale: en_US + name: 'Lotr mug' + translatable: "@mug_lotr" + bb_mug_translation: + locale: en_US + name: 'Breaking bad mug' + translatable: "@mug_bb" + +Sylius\Component\Core\Model\ChannelPricing: + sw_mug_web_channel_pricing: + channel: "@channel_web" + price: 20 + lotr_mug_web_channel_pricing: + channel: "@channel_web" + price: 20 + bb_mug_web_channel_pricing: + channel: "@channel_web" + price: 20 diff --git a/tests/DataFixtures/ORM/resources/orders.yml b/tests/DataFixtures/ORM/resources/orders.yml deleted file mode 100644 index 895ed463e5c..00000000000 --- a/tests/DataFixtures/ORM/resources/orders.yml +++ /dev/null @@ -1,34 +0,0 @@ -Sylius\Component\Core\Model\Order: - order-001: - channel: "@channel-web" - currencyCode: USD - localeCode: en_US - customer: "@customer_oliver" - -Sylius\Component\Core\Model\Channel: - channel-web: - code: "WEB" - name: "Web Channel" - hostname: "localhost" - description: "Lorem ipsum" - baseCurrency: "@currency" - defaultLocale: "@locale" - color: "black" - enabled: true - taxCalculationStrategy: "order_items_based" - -Sylius\Component\Currency\Model\Currency: - currency: - code: USD - -Sylius\Component\Locale\Model\Locale: - locale: - code: en_US - -Sylius\Component\Core\Model\Customer: - customer_oliver: - firstName: Oliver - lastName: Queen - email: oliver.queen@star-city.com - emailCanonical: oliver.queen@star-city.com - birthday: diff --git a/tests/Responses/Expected/cart/add_to_cart_quantity_validation_fail_response.json b/tests/Responses/Expected/cart/add_to_cart_quantity_validation_fail_response.json new file mode 100644 index 00000000000..85768cdcd45 --- /dev/null +++ b/tests/Responses/Expected/cart/add_to_cart_quantity_validation_fail_response.json @@ -0,0 +1,14 @@ +{ + "code": 400, + "message": "Validation Failed", + "errors": { + "children": { + "quantity": { + "errors": [ + "Quantity of an order item cannot be lower than 1." + ] + }, + "variant": { } + } + } +} diff --git a/tests/Responses/Expected/cart/add_to_cart_response.json b/tests/Responses/Expected/cart/add_to_cart_response.json new file mode 100644 index 00000000000..bac52af1a2a --- /dev/null +++ b/tests/Responses/Expected/cart/add_to_cart_response.json @@ -0,0 +1,66 @@ +{ + "id": @integer@, + "order": { + "id": @integer@, + "items": [], + "items_total": 20, + "adjustments": [], + "adjustments_total": 0, + "total": 20, + "state": "cart", + "created_at": "@string@.isDateTime()", + "updated_at": "@string@.isDateTime()", + "channel": { + "id": @integer@, + "code": "WEB", + "name": "Web Channel", + "description": "Lorem ipsum", + "hostname": "localhost", + "color": "black", + "created_at": "@string@.isDateTime()", + "updated_at": "@string@.isDateTime()", + "enabled": true, + "tax_calculation_strategy": "order_items_based", + "_links": { + "self": { + "href": "@string@" + } + } + }, + "payments": [], + "shipments": [], + "checkout_state": "cart" + }, + "quantity": 1, + "unit_price": 20, + "total": 20, + "immutable": false, + "units": [ + { + "id": @integer@, + "adjustments": [], + "adjustments_total": 0, + "_links": { + "order": { + "href": "@string@" + } + } + } + ], + "units_total": 20, + "adjustments": [], + "adjustments_total": 0, + "variant": { + "id": @integer@, + "on_hold": 0, + "tracked": false + }, + "_links": { + "product": { + "href": "@string@" + }, + "variant": { + "href": "@string@" + } + } +} diff --git a/tests/Responses/Expected/cart/add_to_cart_validation_fail_response.json b/tests/Responses/Expected/cart/add_to_cart_validation_fail_response.json new file mode 100644 index 00000000000..4162abb02fe --- /dev/null +++ b/tests/Responses/Expected/cart/add_to_cart_validation_fail_response.json @@ -0,0 +1,18 @@ +{ + "code": 400, + "message": "Validation Failed", + "errors": { + "children": { + "quantity": { + "errors": [ + "Quantity of an order item cannot be lower than 1." + ] + }, + "variant": { + "errors": [ + "This value should not be blank." + ] + } + } + } +} diff --git a/tests/Responses/Expected/cart/add_to_cart_with_bigger_quantity_response.json b/tests/Responses/Expected/cart/add_to_cart_with_bigger_quantity_response.json new file mode 100644 index 00000000000..fa614687321 --- /dev/null +++ b/tests/Responses/Expected/cart/add_to_cart_with_bigger_quantity_response.json @@ -0,0 +1,86 @@ +{ + "id": @integer@, + "order": { + "id": @integer@, + "items": [], + "items_total": 60, + "adjustments": [], + "adjustments_total": 0, + "total": 60, + "state": "cart", + "created_at": "@string@.isDateTime()", + "updated_at": "@string@.isDateTime()", + "channel": { + "id": @integer@, + "code": "WEB", + "name": "Web Channel", + "description": "Lorem ipsum", + "hostname": "localhost", + "color": "black", + "created_at": "@string@.isDateTime()", + "updated_at": "@string@.isDateTime()", + "enabled": true, + "tax_calculation_strategy": "order_items_based", + "_links": { + "self": { + "href": "@string@" + } + } + }, + "payments": [], + "shipments": [], + "checkout_state": "cart" + }, + "quantity": 3, + "unit_price": 20, + "total": 60, + "immutable": false, + "units": [ + { + "id": @integer@, + "adjustments": [], + "adjustments_total": 0, + "_links": { + "order": { + "href": "@string@" + } + } + }, + { + "id": @integer@, + "adjustments": [], + "adjustments_total": 0, + "_links": { + "order": { + "href": "@string@" + } + } + }, + { + "id": @integer@, + "adjustments": [], + "adjustments_total": 0, + "_links": { + "order": { + "href": "@string@" + } + } + } + ], + "units_total": 60, + "adjustments": [], + "adjustments_total": 0, + "variant": { + "id": @integer@, + "on_hold": 0, + "tracked": false + }, + "_links": { + "product": { + "href": "@string@" + }, + "variant": { + "href": "@string@" + } + } +} From 7da6d187a248bf889de86cad0d8b61a09dfb3979 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz?= Date: Fri, 20 Jan 2017 09:30:38 +0100 Subject: [PATCH 03/11] [API] Varaint availableness check --- tests/DataFixtures/ORM/resources/carts.yml | 16 +++++ ..._to_cart_hard_available_item_response.json | 66 +++++++++++++++++++ ...ilable_item_validation_error_response.json | 17 +++++ 3 files changed, 99 insertions(+) create mode 100644 tests/Responses/Expected/cart/add_to_cart_hard_available_item_response.json create mode 100644 tests/Responses/Expected/cart/add_to_cart_hard_available_item_validation_error_response.json diff --git a/tests/DataFixtures/ORM/resources/carts.yml b/tests/DataFixtures/ORM/resources/carts.yml index 80e518324cd..b80187c0d8d 100644 --- a/tests/DataFixtures/ORM/resources/carts.yml +++ b/tests/DataFixtures/ORM/resources/carts.yml @@ -71,6 +71,15 @@ Sylius\Component\Core\Model\ProductVariant: currentTranslation: "@bb_mug_translation" updatedAt: 2015-10-05 channelPricings: ["@bb_mug_web_channel_pricing"] + hard_available_mug: + code: HARD_AVAILABLE_MUG + product: "@mug" + currentLocale: en_US + currentTranslation: "@hard_available_mug_translation" + updatedAt: 2015-10-05 + channelPricings: ["@hard_available_mug_web_channel_pricing"] + tracked: true + onHand: 2 Sylius\Component\Product\Model\ProductVariantTranslation: sw_mug_translation: @@ -85,6 +94,10 @@ Sylius\Component\Product\Model\ProductVariantTranslation: locale: en_US name: 'Breaking bad mug' translatable: "@mug_bb" + hard_available_mug_translation: + locale: en_US + name: 'Breaking bad mug' + translatable: "@hard_available_mug" Sylius\Component\Core\Model\ChannelPricing: sw_mug_web_channel_pricing: @@ -96,3 +109,6 @@ Sylius\Component\Core\Model\ChannelPricing: bb_mug_web_channel_pricing: channel: "@channel_web" price: 20 + hard_available_mug_web_channel_pricing: + channel: "@channel_web" + price: 20 diff --git a/tests/Responses/Expected/cart/add_to_cart_hard_available_item_response.json b/tests/Responses/Expected/cart/add_to_cart_hard_available_item_response.json new file mode 100644 index 00000000000..8bc172f668e --- /dev/null +++ b/tests/Responses/Expected/cart/add_to_cart_hard_available_item_response.json @@ -0,0 +1,66 @@ +{ + "id": @integer@, + "order": { + "id": @integer@, + "items": [], + "items_total": 20, + "adjustments": [], + "adjustments_total": 0, + "total": 20, + "state": "cart", + "created_at": "@string@.isDateTime()", + "updated_at": "@string@.isDateTime()", + "channel": { + "id": @integer@, + "code": "WEB", + "name": "Web Channel", + "description": "Lorem ipsum", + "hostname": "localhost", + "color": "black", + "created_at": "@string@.isDateTime()", + "updated_at": "@string@.isDateTime()", + "enabled": true, + "tax_calculation_strategy": "order_items_based", + "_links": { + "self": { + "href": "@string@" + } + } + }, + "payments": [], + "shipments": [], + "checkout_state": "cart" + }, + "quantity": 1, + "unit_price": 20, + "total": 20, + "immutable": false, + "units": [ + { + "id": @integer@, + "adjustments": [], + "adjustments_total": 0, + "_links": { + "order": { + "href": "@string@" + } + } + } + ], + "units_total": 20, + "adjustments": [], + "adjustments_total": 0, + "variant": { + "id": @integer@, + "on_hold": 0, + "tracked": true + }, + "_links": { + "product": { + "href": "@string@" + }, + "variant": { + "href": "@string@" + } + } +} diff --git a/tests/Responses/Expected/cart/add_to_cart_hard_available_item_validation_error_response.json b/tests/Responses/Expected/cart/add_to_cart_hard_available_item_validation_error_response.json new file mode 100644 index 00000000000..43283952394 --- /dev/null +++ b/tests/Responses/Expected/cart/add_to_cart_hard_available_item_validation_error_response.json @@ -0,0 +1,17 @@ +{ + "code":400, + "message":"Validation Failed", + "errors":{ + "errors":[ + "Mug does not have sufficient stock." + ], + "children":{ + "quantity":{ + + }, + "variant":{ + + } + } + } +} From de5066d0cb1dda60378490fc14d517ee4d10b2ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz?= Date: Fri, 20 Jan 2017 14:52:49 +0100 Subject: [PATCH 04/11] [API] Update cart item quantity --- .../OrderBundle/Form/Type/OrderItemType.php | 1 - tests/Controller/CartApiTest.php | 92 ++++++++++++ tests/DataFixtures/ORM/resources/carts.yml | 28 ---- .../ORM/resources/fulfilled_cart.yml | 138 ++++++++++++++++++ ...te_cart_item_validation_fail_response.json | 12 ++ ...e_cart_item_validation_error_response.json | 14 ++ 6 files changed, 256 insertions(+), 29 deletions(-) create mode 100644 tests/DataFixtures/ORM/resources/fulfilled_cart.yml create mode 100644 tests/Responses/Expected/cart/update_cart_item_validation_fail_response.json create mode 100644 tests/Responses/Expected/cart/update_hard_available_cart_item_validation_error_response.json diff --git a/src/Sylius/Bundle/OrderBundle/Form/Type/OrderItemType.php b/src/Sylius/Bundle/OrderBundle/Form/Type/OrderItemType.php index 81e62a485fe..1652174cc6c 100644 --- a/src/Sylius/Bundle/OrderBundle/Form/Type/OrderItemType.php +++ b/src/Sylius/Bundle/OrderBundle/Form/Type/OrderItemType.php @@ -48,7 +48,6 @@ public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('quantity', IntegerType::class, [ - 'attr' => ['min' => 1], 'label' => 'sylius.ui.quantity', ]) ->setDataMapper($this->dataMapper) diff --git a/tests/Controller/CartApiTest.php b/tests/Controller/CartApiTest.php index ec99dea7ac9..78403e89519 100644 --- a/tests/Controller/CartApiTest.php +++ b/tests/Controller/CartApiTest.php @@ -12,6 +12,8 @@ namespace Sylius\Tests\Controller; use Lakion\ApiTestCase\JsonApiTestCase; +use Sylius\Component\Core\Model\OrderInterface; +use Sylius\Component\Order\Model\OrderItemInterface; use Symfony\Component\HttpFoundation\Response; /** @@ -299,4 +301,94 @@ public function it_checks_if_requested_variant_is_available() $response = $this->client->getResponse(); $this->assertResponse($response, 'cart/add_to_cart_hard_available_item_validation_error_response', Response::HTTP_BAD_REQUEST); } + + /** + * @test + */ + public function it_denies_updating_a_cart_item_quantity_for_non_authenticated_user() + { + $this->client->request('PUT', '/api/v1/carts/1/items/1'); + + $response = $this->client->getResponse(); + $this->assertResponse($response, 'authentication/access_denied_response', Response::HTTP_UNAUTHORIZED); + } + + /** + * @test + */ + public function it_does_not_allow_to_update_items_variant() + { + $this->loadFixturesFromFile('authentication/api_administrator.yml'); + $fulfilledCart = $this->loadFixturesFromFile('resources/fulfilled_cart.yml'); + /** @var OrderInterface $order */ + $order = $fulfilledCart['fulfilled_cart']; + /** @var OrderItemInterface $orderItem */ + $orderItem = $fulfilledCart['sw_mug_item']; + + $data = +<<getId(), $orderItem->getId()); + + $this->client->request('PUT', $url, [], [], static::$authorizedHeaderWithContentType, $data); + + $response = $this->client->getResponse(); + $this->assertResponse($response, 'cart/update_cart_item_validation_fail_response', Response::HTTP_BAD_REQUEST); + } + + /** + * @test + */ + public function it_updates_item_quantity() + { + $this->loadFixturesFromFile('authentication/api_administrator.yml'); + $fulfilledCart = $this->loadFixturesFromFile('resources/fulfilled_cart.yml'); + /** @var OrderInterface $order */ + $order = $fulfilledCart['fulfilled_cart']; + /** @var OrderItemInterface $orderItem */ + $orderItem = $fulfilledCart['sw_mug_item']; + + $url = sprintf('/api/v1/carts/%s/items/%s', $order->getId(), $orderItem->getId()); + + $data = +<<client->request('PUT', $url, [], [], static::$authorizedHeaderWithContentType, $data); + + $response = $this->client->getResponse(); + $this->assertResponseCode($response, Response::HTTP_NO_CONTENT); + } + + /** + * @test + */ + public function it_checks_if_requested_variant_is_available_during_quantity_update() + { + $this->loadFixturesFromFile('authentication/api_administrator.yml'); + $fulfilledCart = $this->loadFixturesFromFile('resources/fulfilled_cart.yml'); + /** @var OrderInterface $order */ + $order = $fulfilledCart['fulfilled_cart']; + /** @var OrderItemInterface $orderItem */ + $orderItem = $fulfilledCart['hard_available_mug_item']; + + $url = sprintf('/api/v1/carts/%s/items/%s', $order->getId(), $orderItem->getId()); + + $data = +<<client->request('PUT', $url, [], [], static::$authorizedHeaderWithContentType, $data); + + $response = $this->client->getResponse(); + $this->assertResponse($response, 'cart/update_hard_available_cart_item_validation_error_response', Response::HTTP_BAD_REQUEST); + } } diff --git a/tests/DataFixtures/ORM/resources/carts.yml b/tests/DataFixtures/ORM/resources/carts.yml index b80187c0d8d..6a928765217 100644 --- a/tests/DataFixtures/ORM/resources/carts.yml +++ b/tests/DataFixtures/ORM/resources/carts.yml @@ -57,20 +57,6 @@ Sylius\Component\Core\Model\ProductVariant: currentTranslation: "@sw_mug_translation" updatedAt: 2015-10-10 channelPricings: ["@sw_mug_web_channel_pricing"] - mug_lotr: - code: MUG_LOTR - product: "@mug" - currentLocale: en_US - currentTranslation: "@lotr_mug_translation" - updatedAt: 2015-10-04 - channelPricings: ["@lotr_mug_web_channel_pricing"] - mug_bb: - code: MUG_BB - product: "@mug" - currentLocale: en_US - currentTranslation: "@bb_mug_translation" - updatedAt: 2015-10-05 - channelPricings: ["@bb_mug_web_channel_pricing"] hard_available_mug: code: HARD_AVAILABLE_MUG product: "@mug" @@ -86,14 +72,6 @@ Sylius\Component\Product\Model\ProductVariantTranslation: locale: en_US name: 'Star wars mug' translatable: "@mug_sw" - lotr_mug_translation: - locale: en_US - name: 'Lotr mug' - translatable: "@mug_lotr" - bb_mug_translation: - locale: en_US - name: 'Breaking bad mug' - translatable: "@mug_bb" hard_available_mug_translation: locale: en_US name: 'Breaking bad mug' @@ -103,12 +81,6 @@ Sylius\Component\Core\Model\ChannelPricing: sw_mug_web_channel_pricing: channel: "@channel_web" price: 20 - lotr_mug_web_channel_pricing: - channel: "@channel_web" - price: 20 - bb_mug_web_channel_pricing: - channel: "@channel_web" - price: 20 hard_available_mug_web_channel_pricing: channel: "@channel_web" price: 20 diff --git a/tests/DataFixtures/ORM/resources/fulfilled_cart.yml b/tests/DataFixtures/ORM/resources/fulfilled_cart.yml new file mode 100644 index 00000000000..a2e401648bb --- /dev/null +++ b/tests/DataFixtures/ORM/resources/fulfilled_cart.yml @@ -0,0 +1,138 @@ +Sylius\Component\Core\Model\Order: + fulfilled_cart: + channel: "@channel_web" + addItem: ["@sw_mug_item", "@hard_available_mug_item"] + currencyCode: USD + localeCode: en_US + customer: "@customer_oliver" + +Sylius\Component\Core\Model\OrderItem: + sw_mug_item: + quantity: 2 + unitPrice: 2000 + addUnit: ["@sw_mug_item_unit1", "@sw_mug_item_unit2"] + variant: "@mug_sw" + order: "@fulfilled_cart" + hard_available_mug_item: + quantity: 1 + unitPrice: 2000 + addUnit: ["@hard_available_mug_item_unit"] + variant: "@hard_available_mug" + order: "@fulfilled_cart" + +Sylius\Component\Core\Model\OrderItemUnit: + sw_mug_item_unit1: + __construct: ["@sw_mug_item"] + sw_mug_item_unit2: + __construct: ["@sw_mug_item"] + hard_available_mug_item_unit: + __construct: ["@hard_available_mug_item"] + +Sylius\Component\Core\Model\Channel: + channel_web: + code: "WEB" + name: "Web Channel" + hostname: "localhost" + description: "Lorem ipsum" + baseCurrency: "@currency" + defaultLocale: "@locale" + color: "black" + enabled: true + taxCalculationStrategy: "order_items_based" + +Sylius\Component\Currency\Model\Currency: + currency: + code: USD + +Sylius\Component\Locale\Model\Locale: + locale: + code: en_US + enabled: true + +Sylius\Component\Core\Model\Customer: + customer_oliver: + firstName: Oliver + lastName: Queen + email: oliver.queen@star-city.com + emailCanonical: oliver.queen@star-city.com + birthday: + +Sylius\Component\Core\Model\Product: + mug: + code: MUG + channels: ["@channel_web"] + currentLocale: en_US + currentTranslation: "@mug_translation" + +Sylius\Component\Core\Model\ProductTranslation: + mug_translation: + slug: mug + locale: en_US + name: 'Mug' + description: + translatable: "@mug" + +Sylius\Component\Core\Model\ProductVariant: + mug_sw: + code: MUG_SW + product: "@mug" + currentLocale: en_US + currentTranslation: "@sw_mug_translation" + updatedAt: 2015-10-10 + channelPricings: ["@sw_mug_web_channel_pricing"] + mug_lotr: + code: MUG_LOTR + product: "@mug" + currentLocale: en_US + currentTranslation: "@lotr_mug_translation" + updatedAt: 2015-10-04 + channelPricings: ["@lotr_mug_web_channel_pricing"] + mug_bb: + code: MUG_BB + product: "@mug" + currentLocale: en_US + currentTranslation: "@bb_mug_translation" + updatedAt: 2015-10-05 + channelPricings: ["@bb_mug_web_channel_pricing"] + hard_available_mug: + code: HARD_AVAILABLE_MUG + product: "@mug" + currentLocale: en_US + currentTranslation: "@hard_available_mug_translation" + updatedAt: 2015-10-05 + channelPricings: ["@hard_available_mug_web_channel_pricing"] + tracked: true + onHand: 2 + onHold: 1 + +Sylius\Component\Product\Model\ProductVariantTranslation: + sw_mug_translation: + locale: en_US + name: 'Star wars mug' + translatable: "@mug_sw" + lotr_mug_translation: + locale: en_US + name: 'Lotr mug' + translatable: "@mug_lotr" + bb_mug_translation: + locale: en_US + name: 'Breaking bad mug' + translatable: "@mug_bb" + hard_available_mug_translation: + locale: en_US + name: 'Breaking bad mug' + translatable: "@hard_available_mug" + +Sylius\Component\Core\Model\ChannelPricing: + sw_mug_web_channel_pricing: + channel: "@channel_web" + price: 20 + lotr_mug_web_channel_pricing: + channel: "@channel_web" + price: 20 + bb_mug_web_channel_pricing: + channel: "@channel_web" + price: 20 + hard_available_mug_web_channel_pricing: + channel: "@channel_web" + price: 20 diff --git a/tests/Responses/Expected/cart/update_cart_item_validation_fail_response.json b/tests/Responses/Expected/cart/update_cart_item_validation_fail_response.json new file mode 100644 index 00000000000..169aac9d672 --- /dev/null +++ b/tests/Responses/Expected/cart/update_cart_item_validation_fail_response.json @@ -0,0 +1,12 @@ +{ + "code": 400, + "message": "Validation Failed", + "errors": { + "errors": [ + "This form should not contain extra fields." + ], + "children": { + "quantity": {} + } + } +} diff --git a/tests/Responses/Expected/cart/update_hard_available_cart_item_validation_error_response.json b/tests/Responses/Expected/cart/update_hard_available_cart_item_validation_error_response.json new file mode 100644 index 00000000000..0c6c1f1ec92 --- /dev/null +++ b/tests/Responses/Expected/cart/update_hard_available_cart_item_validation_error_response.json @@ -0,0 +1,14 @@ +{ + "code":400, + "message":"Validation Failed", + "errors":{ + "errors":[ + "Mug does not have sufficient stock." + ], + "children":{ + "quantity":{ + + } + } + } +} From 11121434473f490b84a2a2c3c006ec9d60a263a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz?= Date: Fri, 20 Jan 2017 15:28:16 +0100 Subject: [PATCH 05/11] [API] Delete cart items --- .../Resources/config/routing/cart.yml | 3 + .../Resources/config/routing/main.yml | 4 + .../Resources/config/routing/order.yml | 12 +++ tests/Controller/CartApiTest.php | 78 ++++++++++++++++++- tests/Controller/OrderApiTest.php | 69 ++++++++++++++++ tests/DataFixtures/ORM/resources/order.yml | 36 +++++++++ 6 files changed, 200 insertions(+), 2 deletions(-) create mode 100644 src/Sylius/Bundle/ApiBundle/Resources/config/routing/order.yml create mode 100644 tests/Controller/OrderApiTest.php create mode 100644 tests/DataFixtures/ORM/resources/order.yml diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/routing/cart.yml b/src/Sylius/Bundle/ApiBundle/Resources/config/routing/cart.yml index f13b1a4573e..4355f2cf32e 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/routing/cart.yml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/routing/cart.yml @@ -31,6 +31,9 @@ sylius_api_cart_delete: _sylius: serialization_version: $version csrf_protection: false + repository: + method: findCartById + arguments: [$id] sylius_api_cart_show: path: /{id} diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/routing/main.yml b/src/Sylius/Bundle/ApiBundle/Resources/config/routing/main.yml index 3197dbbfeb3..9415c9ff0fd 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/routing/main.yml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/routing/main.yml @@ -24,6 +24,10 @@ sylius_api_cart: resource: "@SyliusApiBundle/Resources/config/routing/cart.yml" prefix: /carts +sylius_api_order: + resource: "@SyliusApiBundle/Resources/config/routing/order.yml" + prefix: /orders + sylius_api_cart_item: resource: "@SyliusApiBundle/Resources/config/routing/cart_item.yml" prefix: /carts/{cartId}/items diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/routing/order.yml b/src/Sylius/Bundle/ApiBundle/Resources/config/routing/order.yml new file mode 100644 index 00000000000..6bf5d63e861 --- /dev/null +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/routing/order.yml @@ -0,0 +1,12 @@ +# This file is part of the Sylius package. +# (c) Paweł Jędrzejewski + +sylius_api_order_delete: + path: /{id} + methods: [DELETE] + defaults: + _controller: sylius.controller.order:deleteAction + _format: json + _sylius: + serialization_version: $version + csrf_protection: false diff --git a/tests/Controller/CartApiTest.php b/tests/Controller/CartApiTest.php index 78403e89519..941cc2dde17 100644 --- a/tests/Controller/CartApiTest.php +++ b/tests/Controller/CartApiTest.php @@ -143,7 +143,18 @@ public function it_allows_to_get_carts_list() /** * @test */ - public function it_returns_not_found_response_when_trying_to_delete_order_which_does_not_exist() + public function it_denies_carts_deletion_for_non_authenticated_user() + { + $this->client->request('DELETE', '/api/v1/carts/-1'); + + $response = $this->client->getResponse(); + $this->assertResponse($response, 'authentication/access_denied_response', Response::HTTP_UNAUTHORIZED); + } + + /** + * @test + */ + public function it_returns_not_found_response_when_trying_to_delete_cart_which_does_not_exist() { $this->loadFixturesFromFile('authentication/api_administrator.yml'); @@ -156,7 +167,7 @@ public function it_returns_not_found_response_when_trying_to_delete_order_which_ /** * @test */ - public function it_allows_to_delete_order() + public function it_allows_to_delete_cart() { $this->loadFixturesFromFile('authentication/api_administrator.yml'); $carts = $this->loadFixturesFromFile('resources/carts.yml'); @@ -172,6 +183,20 @@ public function it_allows_to_delete_order() $this->assertResponse($response, 'error/not_found_response', Response::HTTP_NOT_FOUND); } + /** + * @test + */ + public function it_does_not_allow_to_delete_orders_in_state_different_than_cart() + { + $this->loadFixturesFromFile('authentication/api_administrator.yml'); + $carts = $this->loadFixturesFromFile('resources/order.yml'); + + $this->client->request('DELETE', '/api/v1/carts/'.$carts['order_001']->getId(), [], [], static::$authorizedHeaderWithContentType, []); + + $response = $this->client->getResponse(); + $this->assertResponse($response, 'error/not_found_response', Response::HTTP_NOT_FOUND); + } + /** * @test */ @@ -391,4 +416,53 @@ public function it_checks_if_requested_variant_is_available_during_quantity_upda $response = $this->client->getResponse(); $this->assertResponse($response, 'cart/update_hard_available_cart_item_validation_error_response', Response::HTTP_BAD_REQUEST); } + + /** + * @test + */ + public function it_denies_carts_item_deletation_for_non_authenticated_user() + { + $this->client->request('DELETE', '/api/v1/carts/-1/items/-1'); + + $response = $this->client->getResponse(); + $this->assertResponse($response, 'authentication/access_denied_response', Response::HTTP_UNAUTHORIZED); + } + + /** + * @test + */ + public function it_returns_not_found_response_when_trying_to_delete_cart_item_which_does_not_exist() + { + $this->loadFixturesFromFile('authentication/api_administrator.yml'); + $fulfilledCart = $this->loadFixturesFromFile('resources/fulfilled_cart.yml'); + /** @var OrderInterface $order */ + $order = $fulfilledCart['fulfilled_cart']; + + $url = sprintf('/api/v1/carts/%s/items/-1', $order->getId()); + + $this->client->request('DELETE', $url, [], [], static::$authorizedHeaderWithContentType); + + $response = $this->client->getResponse(); + $this->assertResponse($response, 'error/not_found_response', Response::HTTP_NOT_FOUND); + } + + /** + * @test + */ + public function it_allows_to_delete_cart_item() + { + $this->loadFixturesFromFile('authentication/api_administrator.yml'); + $fulfilledCart = $this->loadFixturesFromFile('resources/fulfilled_cart.yml'); + /** @var OrderInterface $order */ + $order = $fulfilledCart['fulfilled_cart']; + /** @var OrderItemInterface $orderItem */ + $orderItem = $fulfilledCart['hard_available_mug_item']; + + $url = sprintf('/api/v1/carts/%s/items/%s', $order->getId(), $orderItem->getId()); + + $this->client->request('DELETE', $url, [], [], static::$authorizedHeaderWithContentType); + + $response = $this->client->getResponse(); + $this->assertResponseCode($response, Response::HTTP_NO_CONTENT); + } } diff --git a/tests/Controller/OrderApiTest.php b/tests/Controller/OrderApiTest.php new file mode 100644 index 00000000000..bf49b36cf54 --- /dev/null +++ b/tests/Controller/OrderApiTest.php @@ -0,0 +1,69 @@ + + */ +final class OrderApiTest extends JsonApiTestCase +{ + /** + * @var array + */ + private static $authorizedHeaderWithContentType = [ + 'HTTP_Authorization' => 'Bearer SampleTokenNjZkNjY2MDEwMTAzMDkxMGE0OTlhYzU3NzYyMTE0ZGQ3ODcyMDAwM2EwMDZjNDI5NDlhMDdlMQ', + 'CONTENT_TYPE' => 'application/json', + ]; + + /** + * @test + */ + public function it_denies_order_deletion_for_non_authenticated_user() + { + $this->client->request('DELETE', '/api/v1/orders/-1', [], []); + + $response = $this->client->getResponse(); + $this->assertResponse($response, 'authentication/access_denied_response', Response::HTTP_UNAUTHORIZED); + } + + /** + * @test + */ + public function it_returns_not_found_response_when_trying_to_delete_order_which_does_not_exist() + { + $this->loadFixturesFromFile('authentication/api_administrator.yml'); + + $this->client->request('DELETE', '/api/v1/orders/-1', [], [], static::$authorizedHeaderWithContentType); + + $response = $this->client->getResponse(); + $this->assertResponse($response, 'error/not_found_response', Response::HTTP_NOT_FOUND); + } + + /** + * @test + */ + public function it_allows_to_delete_order() + { + $this->loadFixturesFromFile('authentication/api_administrator.yml'); + $carts = $this->loadFixturesFromFile('resources/order.yml'); + + $this->client->request('DELETE', '/api/v1/orders/'.$carts['order_001']->getId(), [], [], static::$authorizedHeaderWithContentType, []); + + $response = $this->client->getResponse(); + $this->assertResponseCode($response, Response::HTTP_NO_CONTENT); + } +} diff --git a/tests/DataFixtures/ORM/resources/order.yml b/tests/DataFixtures/ORM/resources/order.yml new file mode 100644 index 00000000000..8c742d2384d --- /dev/null +++ b/tests/DataFixtures/ORM/resources/order.yml @@ -0,0 +1,36 @@ +Sylius\Component\Core\Model\Order: + order_001: + channel: "@channel_web" + currencyCode: USD + localeCode: en_US + customer: "@customer_oliver" + state: new + +Sylius\Component\Core\Model\Channel: + channel_web: + code: "WEB" + name: "Web Channel" + hostname: "localhost" + description: "Lorem ipsum" + baseCurrency: "@currency" + defaultLocale: "@locale" + color: "black" + enabled: true + taxCalculationStrategy: "order_items_based" + +Sylius\Component\Currency\Model\Currency: + currency: + code: USD + +Sylius\Component\Locale\Model\Locale: + locale: + code: en_US + enabled: true + +Sylius\Component\Core\Model\Customer: + customer_oliver: + firstName: Oliver + lastName: Queen + email: oliver.queen@star-city.com + emailCanonical: oliver.queen@star-city.com + birthday: From c631f5ae27c397b68a1781384b3cec2d748f8521 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz?= Date: Mon, 23 Jan 2017 15:57:57 +0100 Subject: [PATCH 06/11] [API] Only orders in cart state can be accessed via cart endpoint --- .../ApiBundle/Resources/config/app/config.yml | 5 +- .../ApiBundle/Resources/config/grids/cart.yml | 97 +++++++++++++++++++ .../Resources/config/routing/cart.yml | 6 +- .../Resources/config/routing/cart_item.yml | 6 +- .../Resources/config/routing/main.yml | 9 +- .../Resources/config/routing/order.yml | 12 --- .../Doctrine/ORM/OrderRepository.php | 11 +++ .../Repository/OrderRepositoryInterface.php | 5 + tests/Controller/CartApiTest.php | 48 +++++++-- tests/Controller/OrderApiTest.php | 61 ++++++++++++ tests/DataFixtures/ORM/resources/carts.yml | 1 + .../cart/create_validation_fail_response.json | 10 +- .../Expected/cart/empty_index_response.json | 21 ++++ .../Expected/order/cart_show_response.json | 31 ++++++ .../Expected/order/order_show_response.json | 31 ++++++ 15 files changed, 322 insertions(+), 32 deletions(-) create mode 100644 src/Sylius/Bundle/ApiBundle/Resources/config/grids/cart.yml delete mode 100644 src/Sylius/Bundle/ApiBundle/Resources/config/routing/order.yml create mode 100644 tests/Responses/Expected/cart/empty_index_response.json create mode 100644 tests/Responses/Expected/order/cart_show_response.json create mode 100644 tests/Responses/Expected/order/order_show_response.json diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/app/config.yml b/src/Sylius/Bundle/ApiBundle/Resources/config/app/config.yml index fe1cb26e801..2dc4b80b8e0 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/app/config.yml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/app/config.yml @@ -2,10 +2,11 @@ # (c) Paweł Jędrzejewski imports: + - { resource: "@SyliusApiBundle/Resources/config/app/fixtures.yml" } + - { resource: "@SyliusApiBundle/Resources/config/grids/product.yml" } - { resource: "@SyliusApiBundle/Resources/config/grids/product_variant.yml" } - - - { resource: "@SyliusApiBundle/Resources/config/app/fixtures.yml" } + - { resource: "@SyliusApiBundle/Resources/config/grids/cart.yml" } sylius_api: resources: diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/grids/cart.yml b/src/Sylius/Bundle/ApiBundle/Resources/config/grids/cart.yml new file mode 100644 index 00000000000..d5c8e013a07 --- /dev/null +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/grids/cart.yml @@ -0,0 +1,97 @@ +sylius_grid: + grids: + sylius_api_cart: + driver: + name: doctrine/orm + options: + class: "%sylius.model.order.class%" + repository: + method: createCartQueryBuilder + sorting: + number: desc + fields: + channel: + type: twig + label: sylius.ui.channel + sortable: channel.code + options: + template: "@SyliusAdmin/Order/Grid/Field/channel.html.twig" + number: + type: twig + label: sylius.ui.number + path: . + sortable: ~ + options: + template: "@SyliusAdmin/Order/Grid/Field/number.html.twig" + date: + type: datetime + label: sylius.ui.date + path: checkoutCompletedAt + sortable: checkoutCompletedAt + options: + format: d-m-Y H:i:s + customer: + type: twig + label: sylius.ui.customer + sortable: customer.lastName + options: + template: "@SyliusAdmin/Order/Grid/Field/customer.html.twig" + state: + type: twig + label: sylius.ui.state + sortable: ~ + options: + template: "@SyliusUi/Grid/Field/state.html.twig" + vars: + labels: "@SyliusAdmin/Order/Label/State" + paymentState: + type: twig + label: sylius.ui.payment_state + sortable: ~ + options: + template: "@SyliusUi/Grid/Field/state.html.twig" + vars: + labels: "@SyliusAdmin/Order/Label/PaymentState" + shippingState: + type: twig + label: sylius.ui.shipping_state + sortable: ~ + options: + template: "@SyliusUi/Grid/Field/state.html.twig" + vars: + labels: "@SyliusAdmin/Order/Label/ShippingState" + total: + type: twig + label: sylius.ui.total + path: . + sortable: total + options: + template: "@SyliusAdmin/Order/Grid/Field/total.html.twig" + currencyCode: + type: string + label: sylius.ui.currency + sortable: ~ + filters: + number: + type: string + label: sylius.ui.number + customer: + type: string + label: sylius.ui.customer + options: + fields: [customer.email, customer.firstName, customer.lastName] + date: + type: date + label: sylius.ui.date + options: + field: checkoutCompletedAt + channel: + type: entity + label: sylius.ui.channel + form_options: + class: "%sylius.model.channel.class%" + total: + type: money + label: sylius.ui.total + options: + currency_field: currencyCode diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/routing/cart.yml b/src/Sylius/Bundle/ApiBundle/Resources/config/routing/cart.yml index 4355f2cf32e..55bb3353c4c 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/routing/cart.yml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/routing/cart.yml @@ -10,8 +10,7 @@ sylius_api_cart_index: serialization_version: $version paginate: $limit sortable: true - sorting: - updatedAt: desc + grid: sylius_api_cart sylius_api_cart_create: path: / @@ -42,3 +41,6 @@ sylius_api_cart_show: _controller: sylius.controller.order:showAction _sylius: serialization_version: $version + repository: + method: findCartById + arguments: [$id] diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/routing/cart_item.yml b/src/Sylius/Bundle/ApiBundle/Resources/config/routing/cart_item.yml index fe5f48fb8d2..b8aeaeb8688 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/routing/cart_item.yml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/routing/cart_item.yml @@ -12,7 +12,7 @@ sylius_api_order_item_create: factory: method: createForCart arguments: - - expr:service('sylius.repository.order').find($cartId) + - "expr:service('sylius.repository.order').findCartById($cartId)" sylius_api_order_item_update: path: /{id} @@ -22,8 +22,6 @@ sylius_api_order_item_update: _sylius: serialization_version: $version form: Sylius\Bundle\ApiBundle\Form\Type\OrderItemType - criteria: - order: $orderId sylius_api_order_item_delete: path: /{id} @@ -32,6 +30,4 @@ sylius_api_order_item_delete: _controller: sylius.controller.order_item:deleteAction _sylius: serialization_version: $version - criteria: - order: $orderId csrf_protection: false diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/routing/main.yml b/src/Sylius/Bundle/ApiBundle/Resources/config/routing/main.yml index 9415c9ff0fd..ff635cc063d 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/routing/main.yml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/routing/main.yml @@ -25,8 +25,13 @@ sylius_api_cart: prefix: /carts sylius_api_order: - resource: "@SyliusApiBundle/Resources/config/routing/order.yml" - prefix: /orders + resource: | + alias: sylius.order + section: api + only: [show, delete] + grid: sylius_admin_order + serialization_version: $version + type: sylius.resource_api sylius_api_cart_item: resource: "@SyliusApiBundle/Resources/config/routing/cart_item.yml" diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/routing/order.yml b/src/Sylius/Bundle/ApiBundle/Resources/config/routing/order.yml deleted file mode 100644 index 6bf5d63e861..00000000000 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/routing/order.yml +++ /dev/null @@ -1,12 +0,0 @@ -# This file is part of the Sylius package. -# (c) Paweł Jędrzejewski - -sylius_api_order_delete: - path: /{id} - methods: [DELETE] - defaults: - _controller: sylius.controller.order:deleteAction - _format: json - _sylius: - serialization_version: $version - csrf_protection: false diff --git a/src/Sylius/Bundle/OrderBundle/Doctrine/ORM/OrderRepository.php b/src/Sylius/Bundle/OrderBundle/Doctrine/ORM/OrderRepository.php index 99a82d5219a..e8c5415f407 100644 --- a/src/Sylius/Bundle/OrderBundle/Doctrine/ORM/OrderRepository.php +++ b/src/Sylius/Bundle/OrderBundle/Doctrine/ORM/OrderRepository.php @@ -34,6 +34,17 @@ public function count() ; } + /** + * {@inheritdoc} + */ + public function createCartQueryBuilder() + { + return $this->createQueryBuilder('o') + ->andWhere('o.state = :state') + ->setParameter('state', OrderInterface::STATE_CART) + ; + } + /** * {@inheritdoc} */ diff --git a/src/Sylius/Component/Order/Repository/OrderRepositoryInterface.php b/src/Sylius/Component/Order/Repository/OrderRepositoryInterface.php index df3962c0624..f659cc95cbe 100644 --- a/src/Sylius/Component/Order/Repository/OrderRepositoryInterface.php +++ b/src/Sylius/Component/Order/Repository/OrderRepositoryInterface.php @@ -58,4 +58,9 @@ public function findCartById($id); * @return OrderInterface[] */ public function findCartsNotModifiedSince(\DateTime $terminalDate); + + /** + * @return \Doctrine\ORM\QueryBuilder + */ + public function createCartQueryBuilder(); } diff --git a/tests/Controller/CartApiTest.php b/tests/Controller/CartApiTest.php index 941cc2dde17..4a60a6f9ebb 100644 --- a/tests/Controller/CartApiTest.php +++ b/tests/Controller/CartApiTest.php @@ -29,12 +29,20 @@ final class CartApiTest extends JsonApiTestCase 'CONTENT_TYPE' => 'application/json', ]; + /** + * @var array + */ + private static $authorizedHeaderWithAccept = [ + 'HTTP_Authorization' => 'Bearer SampleTokenNjZkNjY2MDEwMTAzMDkxMGE0OTlhYzU3NzYyMTE0ZGQ3ODcyMDAwM2EwMDZjNDI5NDlhMDdlMQ', + 'ACCEPT' => 'application/json', + ]; + /** * @test */ public function it_denies_getting_an_order_for_non_authenticated_user() { - $this->client->request('GET', '/api/v1/carts/'); + $this->client->request('GET', '/api/v1/carts/-1'); $response = $this->client->getResponse(); $this->assertResponse($response, 'authentication/access_denied_response', Response::HTTP_UNAUTHORIZED); @@ -47,7 +55,7 @@ public function it_returns_not_found_response_when_requesting_details_of_an_orde { $this->loadFixturesFromFile('authentication/api_administrator.yml'); - $this->client->request('GET', '/api/v1/carts/-1', [], [], static::$authorizedHeaderWithContentType); + $this->client->request('GET', '/api/v1/carts/-1', [], [], static::$authorizedHeaderWithAccept); $response = $this->client->getResponse(); $this->assertResponse($response, 'error/not_found_response', Response::HTTP_NOT_FOUND); @@ -61,12 +69,26 @@ public function it_allows_to_get_order() $this->loadFixturesFromFile('authentication/api_administrator.yml'); $orderData = $this->loadFixturesFromFile('resources/carts.yml'); - $this->client->request('GET', '/api/v1/carts/'.$orderData['order_001']->getId(), [], [], static::$authorizedHeaderWithContentType); + $this->client->request('GET', '/api/v1/carts/'.$orderData['order_001']->getId(), [], [], static::$authorizedHeaderWithAccept); $response = $this->client->getResponse(); $this->assertResponse($response, 'cart/show_response', Response::HTTP_OK); } + /** + * @test + */ + public function it_does_not_show_orders_in_stare_other_than_cart() + { + $this->loadFixturesFromFile('authentication/api_administrator.yml'); + $orderData = $this->loadFixturesFromFile('resources/order.yml'); + + $this->client->request('GET', '/api/v1/carts/'.$orderData['order_001']->getId(), [], [], static::$authorizedHeaderWithAccept); + + $response = $this->client->getResponse(); + $this->assertResponse($response, 'error/not_found_response', Response::HTTP_NOT_FOUND); + } + /** * @test */ @@ -134,12 +156,26 @@ public function it_allows_to_get_carts_list() $this->loadFixturesFromFile('authentication/api_administrator.yml'); $this->loadFixturesFromFile('resources/carts.yml'); - $this->client->request('GET', '/api/v1/carts/', [], [], static::$authorizedHeaderWithContentType); + $this->client->request('GET', '/api/v1/carts/', [], [], static::$authorizedHeaderWithAccept); $response = $this->client->getResponse(); $this->assertResponse($response, 'cart/index_response', Response::HTTP_OK); } + /** + * @test + */ + public function it_does_not_allow_to_list_orders_in_state_different_than_cart() + { + $this->loadFixturesFromFile('authentication/api_administrator.yml'); + $this->loadFixturesFromFile('resources/order.yml'); + + $this->client->request('GET', '/api/v1/carts/', [], [], static::$authorizedHeaderWithAccept); + + $response = $this->client->getResponse(); + $this->assertResponse($response, 'cart/empty_index_response', Response::HTTP_OK); + } + /** * @test */ @@ -172,7 +208,7 @@ public function it_allows_to_delete_cart() $this->loadFixturesFromFile('authentication/api_administrator.yml'); $carts = $this->loadFixturesFromFile('resources/carts.yml'); - $this->client->request('DELETE', '/api/v1/carts/'.$carts['order_001']->getId(), [], [], static::$authorizedHeaderWithContentType, []); + $this->client->request('DELETE', '/api/v1/carts/'.$carts['order_001']->getId(), [], [], static::$authorizedHeaderWithContentType); $response = $this->client->getResponse(); $this->assertResponseCode($response, Response::HTTP_NO_CONTENT); @@ -191,7 +227,7 @@ public function it_does_not_allow_to_delete_orders_in_state_different_than_cart( $this->loadFixturesFromFile('authentication/api_administrator.yml'); $carts = $this->loadFixturesFromFile('resources/order.yml'); - $this->client->request('DELETE', '/api/v1/carts/'.$carts['order_001']->getId(), [], [], static::$authorizedHeaderWithContentType, []); + $this->client->request('DELETE', '/api/v1/carts/'.$carts['order_001']->getId(), [], [], static::$authorizedHeaderWithContentType); $response = $this->client->getResponse(); $this->assertResponse($response, 'error/not_found_response', Response::HTTP_NOT_FOUND); diff --git a/tests/Controller/OrderApiTest.php b/tests/Controller/OrderApiTest.php index bf49b36cf54..5abab4e1eff 100644 --- a/tests/Controller/OrderApiTest.php +++ b/tests/Controller/OrderApiTest.php @@ -29,6 +29,14 @@ final class OrderApiTest extends JsonApiTestCase 'CONTENT_TYPE' => 'application/json', ]; + /** + * @var array + */ + private static $authorizedHeaderWithAccept = [ + 'HTTP_Authorization' => 'Bearer SampleTokenNjZkNjY2MDEwMTAzMDkxMGE0OTlhYzU3NzYyMTE0ZGQ3ODcyMDAwM2EwMDZjNDI5NDlhMDdlMQ', + 'CONTENT_TYPE' => 'application/json', + ]; + /** * @test */ @@ -66,4 +74,57 @@ public function it_allows_to_delete_order() $response = $this->client->getResponse(); $this->assertResponseCode($response, Response::HTTP_NO_CONTENT); } + + + /** + * @test + */ + public function it_denies_getting_an_order_for_non_authenticated_user() + { + $this->client->request('GET', '/api/v1/orders/-1'); + + $response = $this->client->getResponse(); + $this->assertResponse($response, 'authentication/access_denied_response', Response::HTTP_UNAUTHORIZED); + } + + /** + * @test + */ + public function it_returns_not_found_response_when_requesting_details_of_an_order_which_does_not_exist() + { + $this->loadFixturesFromFile('authentication/api_administrator.yml'); + + $this->client->request('GET', '/api/v1/orders/-1', [], [], static::$authorizedHeaderWithAccept); + + $response = $this->client->getResponse(); + $this->assertResponse($response, 'error/not_found_response', Response::HTTP_NOT_FOUND); + } + + /** + * @test + */ + public function it_allows_to_get_cart() + { + $this->loadFixturesFromFile('authentication/api_administrator.yml'); + $orderData = $this->loadFixturesFromFile('resources/carts.yml'); + + $this->client->request('GET', '/api/v1/orders/'.$orderData['order_001']->getId(), [], [], static::$authorizedHeaderWithAccept); + + $response = $this->client->getResponse(); + $this->assertResponse($response, 'order/cart_show_response', Response::HTTP_OK); + } + + /** + * @test + */ + public function it_allows_to_get_an_order() + { + $this->loadFixturesFromFile('authentication/api_administrator.yml'); + $orderData = $this->loadFixturesFromFile('resources/order.yml'); + + $this->client->request('GET', '/api/v1/orders/'.$orderData['order_001']->getId(), [], [], static::$authorizedHeaderWithAccept); + + $response = $this->client->getResponse(); + $this->assertResponse($response, 'order/order_show_response', Response::HTTP_OK); + } } diff --git a/tests/DataFixtures/ORM/resources/carts.yml b/tests/DataFixtures/ORM/resources/carts.yml index 6a928765217..5ee7f4423e9 100644 --- a/tests/DataFixtures/ORM/resources/carts.yml +++ b/tests/DataFixtures/ORM/resources/carts.yml @@ -3,6 +3,7 @@ Sylius\Component\Core\Model\Order: channel: "@channel_web" currencyCode: USD localeCode: en_US + state: cart customer: "@customer_oliver" Sylius\Component\Core\Model\Channel: diff --git a/tests/Responses/Expected/cart/create_validation_fail_response.json b/tests/Responses/Expected/cart/create_validation_fail_response.json index 11990f9b5df..4ed0dc91b76 100644 --- a/tests/Responses/Expected/cart/create_validation_fail_response.json +++ b/tests/Responses/Expected/cart/create_validation_fail_response.json @@ -3,10 +3,14 @@ "message": "Validation Failed", "errors": { "children": { - "customer": {}, + "customer": { + "errors": [ + "This value should not be blank." + ] + }, "localeCode": { - "errors": [ - "This value should not be blank." + "errors": [ + "This value should not be blank." ] }, "channel": { diff --git a/tests/Responses/Expected/cart/empty_index_response.json b/tests/Responses/Expected/cart/empty_index_response.json new file mode 100644 index 00000000000..ea4ca645739 --- /dev/null +++ b/tests/Responses/Expected/cart/empty_index_response.json @@ -0,0 +1,21 @@ +{ + "page": 1, + "limit": 10, + "pages": 1, + "total": 0, + "_links": { + "self": { + "href": "\/api\/v1\/carts\/?page=1&limit=10" + }, + "first": { + "href": "\/api\/v1\/carts\/?page=1&limit=10" + }, + "last": { + "href": "\/api\/v1\/carts\/?page=1&limit=10" + } + }, + "_embedded": { + "items": [ + ] + } +} diff --git a/tests/Responses/Expected/order/cart_show_response.json b/tests/Responses/Expected/order/cart_show_response.json new file mode 100644 index 00000000000..69785632f32 --- /dev/null +++ b/tests/Responses/Expected/order/cart_show_response.json @@ -0,0 +1,31 @@ +{ + "id": @integer@, + "items": [], + "items_total": 0, + "adjustments": [], + "adjustments_total": 0, + "total": 0, + "state": "cart", + "created_at": "@string@.isDateTime()", + "updated_at": "@string@.isDateTime()", + "channel": { + "id": @integer@, + "code": "WEB", + "name": "Web Channel", + "description": "Lorem ipsum", + "hostname": "localhost", + "color": "black", + "created_at": "@string@.isDateTime()", + "updated_at": "@string@.isDateTime()", + "enabled": true, + "tax_calculation_strategy": "order_items_based", + "_links": { + "self": { + "href": "@string@" + } + } + }, + "payments": [], + "shipments": [], + "checkout_state": "cart" +} diff --git a/tests/Responses/Expected/order/order_show_response.json b/tests/Responses/Expected/order/order_show_response.json new file mode 100644 index 00000000000..411c7bbfa29 --- /dev/null +++ b/tests/Responses/Expected/order/order_show_response.json @@ -0,0 +1,31 @@ +{ + "id": @integer@, + "items": [], + "items_total": 0, + "adjustments": [], + "adjustments_total": 0, + "total": 0, + "state": "new", + "created_at": "@string@.isDateTime()", + "updated_at": "@string@.isDateTime()", + "channel": { + "id": @integer@, + "code": "WEB", + "name": "Web Channel", + "description": "Lorem ipsum", + "hostname": "localhost", + "color": "black", + "created_at": "@string@.isDateTime()", + "updated_at": "@string@.isDateTime()", + "enabled": true, + "tax_calculation_strategy": "order_items_based", + "_links": { + "self": { + "href": "@string@" + } + } + }, + "payments": [], + "shipments": [], + "checkout_state": "cart" +} From b436a680056713289c6a55de80c33988c255c451 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz?= Date: Tue, 31 Jan 2017 08:44:41 +0100 Subject: [PATCH 07/11] [API] Configure cart and related items serialization --- .../EventListener/AddToCartListener.php | 21 ++- .../ApiBundle/Form/Type/OrderItemType.php | 7 +- .../Bundle/ApiBundle/Form/Type/OrderType.php | 6 +- .../ApiBundle/Resources/config/grids/cart.yml | 62 ++------ .../Resources/config/routing/cart.yml | 3 + .../Resources/config/routing/cart_item.yml | 7 + .../Resources/config/routing/main.yml | 2 +- .../ApiBundle/Resources/config/services.xml | 4 +- .../Resources/config/services/form.xml | 4 + .../EventListener/AddToCartListenerSpec.php | 44 ++++++ .../config/serializer/Model.Channel.yml | 19 ++- .../config/serializer/Model.Channel.yml | 1 + .../config/serializer/Model.Customer.yml | 2 +- .../config/serializer/Model.Order.yml | 15 +- .../config/serializer/Model.OrderItem.yml | 25 +-- .../config/serializer/Model.OrderItemUnit.yml | 2 +- .../config/serializer/Model.Customer.yml | 17 +- .../Doctrine/ORM/OrderItemRepository.php | 40 +++++ .../OrderBundle/Form/Type/OrderItemType.php | 1 + .../config/serializer/Model.Adjustment.yml | 20 +++ .../config/serializer/Model.Order.yml | 39 +++++ .../config/serializer/Model.OrderItem.yml | 40 +++++ .../config/serializer/Model.OrderItemUnit.yml | 20 +++ .../services/integrations/doctrine/orm.xml | 1 + .../config/serializer/Model.User.yml | 10 +- .../OrderItemRepositoryInterface.php | 29 ++++ tests/Controller/CartApiTest.php | 148 +++++++++++------- tests/Controller/OrderApiTest.php | 49 +----- .../ORM/resources/{carts.yml => cart.yml} | 1 - .../ORM/resources/fulfilled_cart.yml | 2 +- tests/DataFixtures/ORM/resources/order.yml | 3 +- ..._to_cart_hard_available_item_response.json | 40 ++--- .../Expected/cart/add_to_cart_response.json | 90 +++++------ ...to_cart_with_bigger_quantity_response.json | 66 +++----- .../Expected/cart/create_response.json | 31 ---- .../cart/increase_quantity_response.json | 90 +++++++++++ .../Expected/cart/index_response.json | 26 ++- .../Expected/cart/show_response.json | 26 ++- .../checkout/addressed_order_response.json | 14 +- .../checkout/completed_order_response.json | 14 +- .../payment_selected_order_response.json | 14 +- .../shipping_selected_order_response.json | 14 +- .../Expected/customer/create_response.json | 7 +- .../customer/create_with_user_response.json | 7 +- .../Expected/customer/index_response.json | 35 ++++- .../customer/partial_update_response.json | 7 +- .../Expected/customer/show_response.json | 7 +- .../customer/show_with_user_response.json | 7 +- .../Expected/customer/update_response.json | 7 +- .../Expected/order/cart_show_response.json | 17 +- .../Expected/order/order_show_response.json | 19 ++- 51 files changed, 780 insertions(+), 402 deletions(-) create mode 100644 src/Sylius/Bundle/ApiBundle/spec/EventListener/AddToCartListenerSpec.php create mode 100644 src/Sylius/Bundle/OrderBundle/Doctrine/ORM/OrderItemRepository.php create mode 100644 src/Sylius/Bundle/OrderBundle/Resources/config/serializer/Model.Adjustment.yml create mode 100644 src/Sylius/Bundle/OrderBundle/Resources/config/serializer/Model.Order.yml create mode 100644 src/Sylius/Bundle/OrderBundle/Resources/config/serializer/Model.OrderItem.yml create mode 100644 src/Sylius/Bundle/OrderBundle/Resources/config/serializer/Model.OrderItemUnit.yml create mode 100644 src/Sylius/Component/Order/Repository/OrderItemRepositoryInterface.php rename tests/DataFixtures/ORM/resources/{carts.yml => cart.yml} (99%) delete mode 100644 tests/Responses/Expected/cart/create_response.json create mode 100644 tests/Responses/Expected/cart/increase_quantity_response.json diff --git a/src/Sylius/Bundle/ApiBundle/EventListener/AddToCartListener.php b/src/Sylius/Bundle/ApiBundle/EventListener/AddToCartListener.php index 315cf5d6086..d4d39c18efb 100644 --- a/src/Sylius/Bundle/ApiBundle/EventListener/AddToCartListener.php +++ b/src/Sylius/Bundle/ApiBundle/EventListener/AddToCartListener.php @@ -11,7 +11,8 @@ namespace Sylius\Bundle\ApiBundle\EventListener; -use Sylius\Component\Core\Model\OrderItem; +use Doctrine\Common\Persistence\ObjectManager; +use Sylius\Component\Core\Model\OrderItemInterface; use Sylius\Component\Order\Processor\OrderProcessorInterface; use Symfony\Component\EventDispatcher\GenericEvent; use Webmozart\Assert\Assert; @@ -26,22 +27,32 @@ final class AddToCartListener */ private $orderProcessor; + /** + * @var ObjectManager + */ + private $objectManager; + /** * @param OrderProcessorInterface $orderProcessor + * @param ObjectManager $objectManager */ - public function __construct(OrderProcessorInterface $orderProcessor) + public function __construct(OrderProcessorInterface $orderProcessor, ObjectManager $objectManager) { $this->orderProcessor = $orderProcessor; + $this->objectManager = $objectManager; } /** * @param GenericEvent $event */ - public function cartItemResolver(GenericEvent $event) + public function recalculateOrder(GenericEvent $event) { $item = $event->getSubject(); - Assert::isInstanceOf($item, OrderItem::class); + Assert::isInstanceOf($item, OrderItemInterface::class); + $order = $item->getOrder(); + + $this->orderProcessor->process($order); - $this->orderProcessor->process($item->getOrder()); + $this->objectManager->persist($order); } } diff --git a/src/Sylius/Bundle/ApiBundle/Form/Type/OrderItemType.php b/src/Sylius/Bundle/ApiBundle/Form/Type/OrderItemType.php index d82ac820ebf..d1db2e0c197 100644 --- a/src/Sylius/Bundle/ApiBundle/Form/Type/OrderItemType.php +++ b/src/Sylius/Bundle/ApiBundle/Form/Type/OrderItemType.php @@ -22,6 +22,7 @@ use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; use Symfony\Component\Validator\Constraints\NotBlank; +use Webmozart\Assert\Assert; /** * @author Łukasz Chruściel @@ -57,9 +58,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) { $orderItem = $event->getData(); - if (null === $orderItem) { - throw new UnexpectedTypeException($orderItem, OrderItemInterface::class); - } + Assert::notNull($orderItem); if (null !== $orderItem->getId()) { $form = $event->getForm(); @@ -82,6 +81,6 @@ public function getParent() */ public function getBlockPrefix() { - return 'sylius_api_cart_item'; + return 'sylius_api_order_item'; } } diff --git a/src/Sylius/Bundle/ApiBundle/Form/Type/OrderType.php b/src/Sylius/Bundle/ApiBundle/Form/Type/OrderType.php index 4485adf659a..4f8c554d844 100644 --- a/src/Sylius/Bundle/ApiBundle/Form/Type/OrderType.php +++ b/src/Sylius/Bundle/ApiBundle/Form/Type/OrderType.php @@ -53,7 +53,11 @@ public function __construct($dataClass, array $validationGroups = [], Repository public function buildForm(FormBuilderInterface $builder, array $options) { $builder - ->add('customer', CustomerChoiceType::class) + ->add('customer', CustomerChoiceType::class, [ + 'constraints' => [ + new NotBlank(['groups' => ['sylius']]), + ], + ]) ->add('localeCode', LocaleChoiceType::class, [ 'constraints' => [ new NotBlank(['groups' => ['sylius']]), diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/grids/cart.yml b/src/Sylius/Bundle/ApiBundle/Resources/config/grids/cart.yml index d5c8e013a07..3c8f81dc1fe 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/grids/cart.yml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/grids/cart.yml @@ -11,62 +11,26 @@ sylius_grid: number: desc fields: channel: - type: twig - label: sylius.ui.channel + type: string + path: channel.code sortable: channel.code - options: - template: "@SyliusAdmin/Order/Grid/Field/channel.html.twig" number: - type: twig - label: sylius.ui.number + type: string path: . sortable: ~ - options: - template: "@SyliusAdmin/Order/Grid/Field/number.html.twig" date: type: datetime - label: sylius.ui.date path: checkoutCompletedAt sortable: checkoutCompletedAt - options: - format: d-m-Y H:i:s customer: - type: twig - label: sylius.ui.customer - sortable: customer.lastName + type: string + sortable: customer.email + path: customer.email options: template: "@SyliusAdmin/Order/Grid/Field/customer.html.twig" - state: - type: twig - label: sylius.ui.state - sortable: ~ - options: - template: "@SyliusUi/Grid/Field/state.html.twig" - vars: - labels: "@SyliusAdmin/Order/Label/State" - paymentState: - type: twig - label: sylius.ui.payment_state - sortable: ~ - options: - template: "@SyliusUi/Grid/Field/state.html.twig" - vars: - labels: "@SyliusAdmin/Order/Label/PaymentState" - shippingState: - type: twig - label: sylius.ui.shipping_state - sortable: ~ - options: - template: "@SyliusUi/Grid/Field/state.html.twig" - vars: - labels: "@SyliusAdmin/Order/Label/ShippingState" total: - type: twig - label: sylius.ui.total - path: . + type: integer sortable: total - options: - template: "@SyliusAdmin/Order/Grid/Field/total.html.twig" currencyCode: type: string label: sylius.ui.currency @@ -74,24 +38,20 @@ sylius_grid: filters: number: type: string - label: sylius.ui.number customer: type: string - label: sylius.ui.customer options: - fields: [customer.email, customer.firstName, customer.lastName] + fields: [customer.email] date: type: date label: sylius.ui.date options: field: checkoutCompletedAt channel: - type: entity - label: sylius.ui.channel - form_options: - class: "%sylius.model.channel.class%" + type: string + options: + fields: [channel.code] total: type: money - label: sylius.ui.total options: currency_field: currencyCode diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/routing/cart.yml b/src/Sylius/Bundle/ApiBundle/Resources/config/routing/cart.yml index 55bb3353c4c..2f715120096 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/routing/cart.yml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/routing/cart.yml @@ -11,6 +11,7 @@ sylius_api_cart_index: paginate: $limit sortable: true grid: sylius_api_cart + serialization_groups: [Default] sylius_api_cart_create: path: / @@ -20,6 +21,7 @@ sylius_api_cart_create: _sylius: serialization_version: $version form: Sylius\Bundle\ApiBundle\Form\Type\OrderType + serialization_groups: [DetailedCart] sylius_api_cart_delete: path: /{id} @@ -44,3 +46,4 @@ sylius_api_cart_show: repository: method: findCartById arguments: [$id] + serialization_groups: [DetailedCart] diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/routing/cart_item.yml b/src/Sylius/Bundle/ApiBundle/Resources/config/routing/cart_item.yml index b8aeaeb8688..6b3bf174c7e 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/routing/cart_item.yml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/routing/cart_item.yml @@ -13,6 +13,7 @@ sylius_api_order_item_create: method: createForCart arguments: - "expr:service('sylius.repository.order').findCartById($cartId)" + serialization_groups: [DetailedCart] sylius_api_order_item_update: path: /{id} @@ -22,6 +23,9 @@ sylius_api_order_item_update: _sylius: serialization_version: $version form: Sylius\Bundle\ApiBundle\Form\Type\OrderItemType + repository: + method: findOneByIdAndCartId + arguments: [$id, $cartId] sylius_api_order_item_delete: path: /{id} @@ -31,3 +35,6 @@ sylius_api_order_item_delete: _sylius: serialization_version: $version csrf_protection: false + repository: + method: findOneByIdAndCartId + arguments: [$id, $cartId] diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/routing/main.yml b/src/Sylius/Bundle/ApiBundle/Resources/config/routing/main.yml index ff635cc063d..029d5159c69 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/routing/main.yml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/routing/main.yml @@ -28,7 +28,7 @@ sylius_api_order: resource: | alias: sylius.order section: api - only: [show, delete] + only: [show] grid: sylius_admin_order serialization_version: $version type: sylius.resource_api diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/services.xml b/src/Sylius/Bundle/ApiBundle/Resources/config/services.xml index e0056a8fe7d..807c6c23761 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/services.xml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/services.xml @@ -26,7 +26,9 @@ - + + + diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/services/form.xml b/src/Sylius/Bundle/ApiBundle/Resources/config/services/form.xml index 7daab339858..1639a0de0b0 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/services/form.xml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/services/form.xml @@ -56,5 +56,9 @@ + + + + diff --git a/src/Sylius/Bundle/ApiBundle/spec/EventListener/AddToCartListenerSpec.php b/src/Sylius/Bundle/ApiBundle/spec/EventListener/AddToCartListenerSpec.php new file mode 100644 index 00000000000..7ab8bf25eec --- /dev/null +++ b/src/Sylius/Bundle/ApiBundle/spec/EventListener/AddToCartListenerSpec.php @@ -0,0 +1,44 @@ +beConstructedWith($orderProcessor, $manager); + } + + function it_is_initializable() + { + $this->shouldHaveType(AddToCartListener::class); + } + + function it_recalculates_cart(OrderProcessorInterface $orderProcessor, ObjectManager $manager, GenericEvent $event, OrderItemInterface $orderItem, OrderInterface $order) + { + $event->getSubject()->willReturn($orderItem); + $orderItem->getOrder()->willReturn($order); + + $orderProcessor->process($order)->shouldBeCalled(); + $manager->persist($order)->shouldBeCalled(); + + $this->recalculateOrder($event); + } +} diff --git a/src/Sylius/Bundle/ChannelBundle/Resources/config/serializer/Model.Channel.yml b/src/Sylius/Bundle/ChannelBundle/Resources/config/serializer/Model.Channel.yml index 9d68032e9e8..0a848646384 100644 --- a/src/Sylius/Bundle/ChannelBundle/Resources/config/serializer/Model.Channel.yml +++ b/src/Sylius/Bundle/ChannelBundle/Resources/config/serializer/Model.Channel.yml @@ -6,34 +6,45 @@ Sylius\Component\Channel\Model\Channel: expose: true type: integer xml_attribute: true + groups: [Detailed] code: expose: true type: string + groups: [Default, Detailed, DetailedCart] name: expose: true type: string + groups: [Detailed] hostname: expose: true type: string + groups: [Detailed] enabled: expose: true type: boolean + groups: [Detailed] description: expose: true type: string + groups: [Detailed] color: expose: true type: string + groups: [Detailed] createdAt: expose: true type: DateTime + groups: [Detailed] updatedAt: expose: true type: DateTime + groups: [Detailed] relations: - rel: self href: - route: sylius_api_channel_show - parameters: - id: expr(object.getId()) - version: 1 + route: sylius_api_channel_show + parameters: + id: expr(object.getId()) + version: 1 + exclusion: + groups: [Default, Detailed, DetailedCart] diff --git a/src/Sylius/Bundle/CoreBundle/Resources/config/serializer/Model.Channel.yml b/src/Sylius/Bundle/CoreBundle/Resources/config/serializer/Model.Channel.yml index 58402147918..f78ebc16aef 100644 --- a/src/Sylius/Bundle/CoreBundle/Resources/config/serializer/Model.Channel.yml +++ b/src/Sylius/Bundle/CoreBundle/Resources/config/serializer/Model.Channel.yml @@ -5,3 +5,4 @@ Sylius\Component\Core\Model\Channel: taxCalculationStrategy: expose: true type: string + groups: [Detailed] diff --git a/src/Sylius/Bundle/CoreBundle/Resources/config/serializer/Model.Customer.yml b/src/Sylius/Bundle/CoreBundle/Resources/config/serializer/Model.Customer.yml index b83c7e97349..73e3070b88f 100644 --- a/src/Sylius/Bundle/CoreBundle/Resources/config/serializer/Model.Customer.yml +++ b/src/Sylius/Bundle/CoreBundle/Resources/config/serializer/Model.Customer.yml @@ -4,4 +4,4 @@ Sylius\Component\Core\Model\Customer: properties: user: expose: true - groups: [Default, Detailed] + groups: [Default, Detailed, DetailedCart] diff --git a/src/Sylius/Bundle/CoreBundle/Resources/config/serializer/Model.Order.yml b/src/Sylius/Bundle/CoreBundle/Resources/config/serializer/Model.Order.yml index d5fa55cb6eb..ad616b50571 100644 --- a/src/Sylius/Bundle/CoreBundle/Resources/config/serializer/Model.Order.yml +++ b/src/Sylius/Bundle/CoreBundle/Resources/config/serializer/Model.Order.yml @@ -5,22 +5,29 @@ Sylius\Component\Core\Model\Order: checkoutState: expose: true type: string - currency: + groups: [Default, Detailed, DetailedCart] + currencyCode: expose: true type: string + groups: [Default, Detailed, DetailedCart] shippingAddress: expose: true + groups: [Detailed] billingAddress: expose: true + groups: [Detailed] shipments: expose: true + groups: [Detailed] payments: expose: true - user: + groups: [Detailed] + customer: expose: true + groups: [Default, Detailed, DetailedCart] channel: expose: true + groups: [Default, Detailed, DetailedCart] promotionCoupon: expose: true - checkoutCompletedAt: - expose: true + groups: [Default, Detailed, DetailedCart] diff --git a/src/Sylius/Bundle/CoreBundle/Resources/config/serializer/Model.OrderItem.yml b/src/Sylius/Bundle/CoreBundle/Resources/config/serializer/Model.OrderItem.yml index abf6ba4b996..670ffee1a80 100644 --- a/src/Sylius/Bundle/CoreBundle/Resources/config/serializer/Model.OrderItem.yml +++ b/src/Sylius/Bundle/CoreBundle/Resources/config/serializer/Model.OrderItem.yml @@ -4,21 +4,24 @@ Sylius\Component\Core\Model\OrderItem: properties: variant: expose: true + groups: [Default, Detailed, DetailedCart] relations: - rel: product exclusion: - exclude_if: expr(!object.getVariant()) + exclude_if: expr(!object.getVariant()) + groups: [Default, Detailed, DetailedCart] href: - route: sylius_api_product_show - parameters: - id: expr(object.getVariant().getProduct().getId()) - version: 1 + route: sylius_api_product_show + parameters: + id: expr(object.getVariant().getProduct().getId()) + version: 1 - rel: variant exclusion: - exclude_if: expr(!object.getVariant()) + exclude_if: expr(!object.getVariant()) + groups: [Default, Detailed, DetailedCart] href: - route: sylius_api_product_variant_show - parameters: - id: expr(object.getVariant().getId()) - productId: expr(object.getVariant().getProduct().getId()) - version: 1 + route: sylius_api_product_variant_show + parameters: + id: expr(object.getVariant().getId()) + productId: expr(object.getVariant().getProduct().getId()) + version: 1 diff --git a/src/Sylius/Bundle/CoreBundle/Resources/config/serializer/Model.OrderItemUnit.yml b/src/Sylius/Bundle/CoreBundle/Resources/config/serializer/Model.OrderItemUnit.yml index 6d719b17db0..deb69553ca6 100644 --- a/src/Sylius/Bundle/CoreBundle/Resources/config/serializer/Model.OrderItemUnit.yml +++ b/src/Sylius/Bundle/CoreBundle/Resources/config/serializer/Model.OrderItemUnit.yml @@ -4,7 +4,7 @@ Sylius\Component\Core\Model\OrderItemUnit: relations: - rel: order href: - route: sylius_api_cart_show + route: sylius_api_order_show parameters: id: expr(object.getOrderItem().getOrder().getId()) version: 1 diff --git a/src/Sylius/Bundle/CustomerBundle/Resources/config/serializer/Model.Customer.yml b/src/Sylius/Bundle/CustomerBundle/Resources/config/serializer/Model.Customer.yml index a97c2d543e1..3c4c74d7b94 100644 --- a/src/Sylius/Bundle/CustomerBundle/Resources/config/serializer/Model.Customer.yml +++ b/src/Sylius/Bundle/CustomerBundle/Resources/config/serializer/Model.Customer.yml @@ -5,11 +5,11 @@ Sylius\Component\Customer\Model\Customer: id: expose: true type: integer - groups: [Default, Detailed] + groups: [Default, Detailed, DetailedCart] email: expose: true type: string - groups: [Default, Detailed] + groups: [Default, Detailed, DetailedCart] emailCanonical: expose: true type: string @@ -17,11 +17,11 @@ Sylius\Component\Customer\Model\Customer: firstName: expose: true type: string - groups: [Default, Detailed] + groups: [Default, Detailed, DetailedCart] lastName: expose: true type: string - groups: [Default, Detailed] + groups: [Default, Detailed, DetailedCart] gender: expose: true type: string @@ -34,3 +34,12 @@ Sylius\Component\Customer\Model\Customer: expose: true type: array groups: [Detailed] + relations: + - rel: self + href: + route: sylius_api_customer_show + parameters: + id: expr(object.getId()) + version: 1 + exclusion: + groups: [Default, Detailed, DetailedCart] diff --git a/src/Sylius/Bundle/OrderBundle/Doctrine/ORM/OrderItemRepository.php b/src/Sylius/Bundle/OrderBundle/Doctrine/ORM/OrderItemRepository.php new file mode 100644 index 00000000000..0dd3f9a795f --- /dev/null +++ b/src/Sylius/Bundle/OrderBundle/Doctrine/ORM/OrderItemRepository.php @@ -0,0 +1,40 @@ + + */ +class OrderItemRepository extends EntityRepository implements OrderItemRepositoryInterface +{ + /** + * {@inheritdoc} + */ + public function findOneByIdAndCartId($id, $cartId) + { + return $this->createQueryBuilder('o') + ->innerJoin('o.order', 'cart') + ->andWhere('cart.state = :state') + ->andWhere('o.id = :id') + ->andWhere('cart.id = :cartId') + ->setParameter('state', OrderInterface::STATE_CART) + ->setParameter('id', $id) + ->setParameter('cartId', $cartId) + ->getQuery() + ->getOneOrNullResult() + ; + } +} diff --git a/src/Sylius/Bundle/OrderBundle/Form/Type/OrderItemType.php b/src/Sylius/Bundle/OrderBundle/Form/Type/OrderItemType.php index 1652174cc6c..81e62a485fe 100644 --- a/src/Sylius/Bundle/OrderBundle/Form/Type/OrderItemType.php +++ b/src/Sylius/Bundle/OrderBundle/Form/Type/OrderItemType.php @@ -48,6 +48,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('quantity', IntegerType::class, [ + 'attr' => ['min' => 1], 'label' => 'sylius.ui.quantity', ]) ->setDataMapper($this->dataMapper) diff --git a/src/Sylius/Bundle/OrderBundle/Resources/config/serializer/Model.Adjustment.yml b/src/Sylius/Bundle/OrderBundle/Resources/config/serializer/Model.Adjustment.yml new file mode 100644 index 00000000000..ddef95f06a7 --- /dev/null +++ b/src/Sylius/Bundle/OrderBundle/Resources/config/serializer/Model.Adjustment.yml @@ -0,0 +1,20 @@ +Sylius\Component\Order\Model\Adjustment: + exclusion_policy: ALL + xml_root_name: order + properties: + id: + expose: true + type: integer + groups: [Default, Detailed, DetailedCart] + type: + expose: true + type: string + groups: [Default, Detailed, DetailedCart] + label: + expose: true + type: string + groups: [Default, Detailed, DetailedCart] + amount: + expose: true + type: integer + groups: [Default, Detailed, DetailedCart] diff --git a/src/Sylius/Bundle/OrderBundle/Resources/config/serializer/Model.Order.yml b/src/Sylius/Bundle/OrderBundle/Resources/config/serializer/Model.Order.yml new file mode 100644 index 00000000000..dab93284973 --- /dev/null +++ b/src/Sylius/Bundle/OrderBundle/Resources/config/serializer/Model.Order.yml @@ -0,0 +1,39 @@ +Sylius\Component\Order\Model\Order: + exclusion_policy: ALL + xml_root_name: order + properties: + id: + expose: true + type: integer + groups: [Default, Detailed, DetailedCart] + items: + expose: true + type: array + groups: [Default, Detailed, DetailedCart] + itemsTotal: + expose: true + type: integer + groups: [Default, Detailed, DetailedCart] + adjustments: + expose: true + type: array + groups: [Default, Detailed, DetailedCart] + adjustmentsTotal: + expose: true + type: integer + groups: [Default, Detailed, DetailedCart] + total: + expose: true + type: integer + groups: [Default, Detailed, DetailedCart] + state: + expose: true + type: string + groups: [Detailed] + checkoutCompletedAt: + expose: true + groups: [Detailed] + number: + expose: true + type: string + groups: [Detailed] diff --git a/src/Sylius/Bundle/OrderBundle/Resources/config/serializer/Model.OrderItem.yml b/src/Sylius/Bundle/OrderBundle/Resources/config/serializer/Model.OrderItem.yml new file mode 100644 index 00000000000..9ae3ce0fa50 --- /dev/null +++ b/src/Sylius/Bundle/OrderBundle/Resources/config/serializer/Model.OrderItem.yml @@ -0,0 +1,40 @@ +Sylius\Component\Order\Model\OrderItem: + exclusion_policy: ALL + xml_root_name: order + properties: + id: + expose: true + type: integer + groups: [Default, Detailed, DetailedCart] + order: + expose: true + type: Sylius\Component\Order\Model\Order + groups: [Default, Detailed, DetailedCart] + quantity: + expose: true + type: integer + groups: [Default, Detailed, DetailedCart] + unitPrice: + expose: true + type: integer + groups: [Default, Detailed, DetailedCart] + total: + expose: true + type: integer + groups: [Default, Detailed, DetailedCart] + units: + expose: true + type: array + groups: [Default, Detailed, DetailedCart] + unitsTotal: + expose: true + type: integer + groups: [Default, Detailed, DetailedCart] + adjustments: + expose: true + type: array + groups: [Default, Detailed, DetailedCart] + adjustmentsTotal: + expose: true + type: integer + groups: [Default, Detailed, DetailedCart] diff --git a/src/Sylius/Bundle/OrderBundle/Resources/config/serializer/Model.OrderItemUnit.yml b/src/Sylius/Bundle/OrderBundle/Resources/config/serializer/Model.OrderItemUnit.yml new file mode 100644 index 00000000000..8ecb6b46445 --- /dev/null +++ b/src/Sylius/Bundle/OrderBundle/Resources/config/serializer/Model.OrderItemUnit.yml @@ -0,0 +1,20 @@ +Sylius\Component\Order\Model\OrderItemUnit: + exclusion_policy: ALL + xml_root_name: order + properties: + id: + expose: true + type: integer + groups: [Default, Detailed, DetailedCart] + orderItem: + expose: true + type: Sylius\Component\Order\Model\OrderItem + groups: [Default, Detailed, DetailedCart] + adjustments: + expose: true + type: array + groups: [Default, Detailed, DetailedCart] + adjustmentsTotal: + expose: true + type: integer + groups: [Default, Detailed, DetailedCart] diff --git a/src/Sylius/Bundle/OrderBundle/Resources/config/services/integrations/doctrine/orm.xml b/src/Sylius/Bundle/OrderBundle/Resources/config/services/integrations/doctrine/orm.xml index a6f8f8305fe..48309e2b81f 100644 --- a/src/Sylius/Bundle/OrderBundle/Resources/config/services/integrations/doctrine/orm.xml +++ b/src/Sylius/Bundle/OrderBundle/Resources/config/services/integrations/doctrine/orm.xml @@ -14,5 +14,6 @@ Sylius\Bundle\OrderBundle\Doctrine\ORM\OrderRepository + Sylius\Bundle\OrderBundle\Doctrine\ORM\OrderItemRepository diff --git a/src/Sylius/Bundle/UserBundle/Resources/config/serializer/Model.User.yml b/src/Sylius/Bundle/UserBundle/Resources/config/serializer/Model.User.yml index ddf776dabc7..1ea9ade05a5 100644 --- a/src/Sylius/Bundle/UserBundle/Resources/config/serializer/Model.User.yml +++ b/src/Sylius/Bundle/UserBundle/Resources/config/serializer/Model.User.yml @@ -5,23 +5,23 @@ Sylius\Component\User\Model\User: id: expose: true type: integer - groups: [Default, Detailed] + groups: [Default, Detailed, DetailedCart] username: expose: true type: string - groups: [Default, Detailed, Secured] + groups: [Default, Detailed, DetailedCart, Secured] usernameCanonical: expose: true type: string - groups: [Detailed] + groups: [Detailed, DetailedCart] email: expose: true type: string - groups: [Default, Detailed] + groups: [Default, Detailed, DetailedCart] emailCanonical: expose: true type: string - groups: [Detailed] + groups: [Detailed, DetailedCart] enabled: expose: true type: boolean diff --git a/src/Sylius/Component/Order/Repository/OrderItemRepositoryInterface.php b/src/Sylius/Component/Order/Repository/OrderItemRepositoryInterface.php new file mode 100644 index 00000000000..0da99ee6723 --- /dev/null +++ b/src/Sylius/Component/Order/Repository/OrderItemRepositoryInterface.php @@ -0,0 +1,29 @@ + + */ +interface OrderItemRepositoryInterface extends RepositoryInterface +{ + /** + * @param mixed $id + * @param mixed $cartId + * + * @return OrderItemInterface + */ + public function findOneByIdAndCartId($id, $cartId); +} diff --git a/tests/Controller/CartApiTest.php b/tests/Controller/CartApiTest.php index 4a60a6f9ebb..6fb7753fef5 100644 --- a/tests/Controller/CartApiTest.php +++ b/tests/Controller/CartApiTest.php @@ -40,7 +40,7 @@ final class CartApiTest extends JsonApiTestCase /** * @test */ - public function it_denies_getting_an_order_for_non_authenticated_user() + public function it_denies_getting_an_cart_for_non_authenticated_user() { $this->client->request('GET', '/api/v1/carts/-1'); @@ -51,7 +51,7 @@ public function it_denies_getting_an_order_for_non_authenticated_user() /** * @test */ - public function it_returns_not_found_response_when_requesting_details_of_an_order_which_does_not_exist() + public function it_returns_not_found_response_when_requesting_details_of_an_cart_which_does_not_exist() { $this->loadFixturesFromFile('authentication/api_administrator.yml'); @@ -64,12 +64,14 @@ public function it_returns_not_found_response_when_requesting_details_of_an_orde /** * @test */ - public function it_allows_to_get_order() + public function it_allows_to_get_cart() { $this->loadFixturesFromFile('authentication/api_administrator.yml'); - $orderData = $this->loadFixturesFromFile('resources/carts.yml'); + $cartData = $this->loadFixturesFromFile('resources/cart.yml'); + /** @var OrderInterface $cart */ + $cart = $cartData['order_001']; - $this->client->request('GET', '/api/v1/carts/'.$orderData['order_001']->getId(), [], [], static::$authorizedHeaderWithAccept); + $this->client->request('GET', '/api/v1/carts/'.$cart->getId(), [], [], static::$authorizedHeaderWithAccept); $response = $this->client->getResponse(); $this->assertResponse($response, 'cart/show_response', Response::HTTP_OK); @@ -78,12 +80,14 @@ public function it_allows_to_get_order() /** * @test */ - public function it_does_not_show_orders_in_stare_other_than_cart() + public function it_does_not_show_orders_in_state_other_than_cart() { $this->loadFixturesFromFile('authentication/api_administrator.yml'); $orderData = $this->loadFixturesFromFile('resources/order.yml'); + /** @var OrderInterface $cart */ + $order = $orderData['order_001']; - $this->client->request('GET', '/api/v1/carts/'.$orderData['order_001']->getId(), [], [], static::$authorizedHeaderWithAccept); + $this->client->request('GET', '/api/v1/carts/'.$order->getId(), [], [], static::$authorizedHeaderWithAccept); $response = $this->client->getResponse(); $this->assertResponse($response, 'error/not_found_response', Response::HTTP_NOT_FOUND); @@ -119,7 +123,7 @@ public function it_does_not_allow_to_create_cart_without_specifying_required_dat public function it_allows_to_create_cart() { $this->loadFixturesFromFile('authentication/api_administrator.yml'); - $this->loadFixturesFromFile('resources/carts.yml'); + $this->loadFixturesFromFile('resources/cart.yml'); $data = <<loadFixturesFromFile('authentication/api_administrator.yml'); - $this->loadFixturesFromFile('resources/carts.yml'); + $this->loadFixturesFromFile('resources/cart.yml'); $this->client->request('GET', '/api/v1/carts/', [], [], static::$authorizedHeaderWithAccept); @@ -165,7 +169,7 @@ public function it_allows_to_get_carts_list() /** * @test */ - public function it_does_not_allow_to_list_orders_in_state_different_than_cart() + public function it_does_not_allow_to_list_carts_in_state_different_than_cart() { $this->loadFixturesFromFile('authentication/api_administrator.yml'); $this->loadFixturesFromFile('resources/order.yml'); @@ -206,14 +210,18 @@ public function it_returns_not_found_response_when_trying_to_delete_cart_which_d public function it_allows_to_delete_cart() { $this->loadFixturesFromFile('authentication/api_administrator.yml'); - $carts = $this->loadFixturesFromFile('resources/carts.yml'); + $carts = $this->loadFixturesFromFile('resources/cart.yml'); - $this->client->request('DELETE', '/api/v1/carts/'.$carts['order_001']->getId(), [], [], static::$authorizedHeaderWithContentType); + /** @var OrderItemInterface $cart */ + $cart = $carts['order_001']; + $url = '/api/v1/carts/'.$cart->getId(); + + $this->client->request('DELETE', $url, [], [], static::$authorizedHeaderWithContentType); $response = $this->client->getResponse(); $this->assertResponseCode($response, Response::HTTP_NO_CONTENT); - $this->client->request('GET', '/api/v1/carts/'.$carts['order_001']->getId(), [], [], static::$authorizedHeaderWithContentType); + $this->client->request('GET', $url, [], [], static::$authorizedHeaderWithContentType); $response = $this->client->getResponse(); $this->assertResponse($response, 'error/not_found_response', Response::HTTP_NOT_FOUND); @@ -227,7 +235,10 @@ public function it_does_not_allow_to_delete_orders_in_state_different_than_cart( $this->loadFixturesFromFile('authentication/api_administrator.yml'); $carts = $this->loadFixturesFromFile('resources/order.yml'); - $this->client->request('DELETE', '/api/v1/carts/'.$carts['order_001']->getId(), [], [], static::$authorizedHeaderWithContentType); + /** @var OrderItemInterface $cart */ + $cart = $carts['order_001']; + + $this->client->request('DELETE', '/api/v1/carts/'.$cart->getId(), [], [], static::$authorizedHeaderWithContentType); $response = $this->client->getResponse(); $this->assertResponse($response, 'error/not_found_response', Response::HTTP_NOT_FOUND); @@ -247,12 +258,16 @@ public function it_denies_adding_a_product_to_cart_for_non_authenticated_user() /** * @test */ - public function it_does_not_allow_to_add_item_to_cart_without_providing_all_needed_fields() + public function it_does_not_allow_to_add_item_to_cart_without_providing_required_fields() { $this->loadFixturesFromFile('authentication/api_administrator.yml'); - $carts = $this->loadFixturesFromFile('resources/carts.yml'); + $carts = $this->loadFixturesFromFile('resources/cart.yml'); + + /** @var OrderInterface $cart */ + $cart = $carts['order_001']; + $url = sprintf('/api/v1/carts/%s/items/', $cart->getId()); - $this->client->request('POST', sprintf('/api/v1/carts/%s/items/', $carts['order_001']->getId()), [], [], static::$authorizedHeaderWithContentType); + $this->client->request('POST', $url, [], [], static::$authorizedHeaderWithContentType); $response = $this->client->getResponse(); $this->assertResponse($response, 'cart/add_to_cart_validation_fail_response', Response::HTTP_BAD_REQUEST); @@ -264,7 +279,11 @@ public function it_does_not_allow_to_add_item_to_cart_without_providing_all_need public function it_adds_an_item_to_the_cart() { $this->loadFixturesFromFile('authentication/api_administrator.yml'); - $carts = $this->loadFixturesFromFile('resources/carts.yml'); + $carts = $this->loadFixturesFromFile('resources/cart.yml'); + + /** @var OrderInterface $cart */ + $cart = $carts['order_001']; + $url = sprintf('/api/v1/carts/%s/items/', $cart->getId()); $data = <<client->request('POST', sprintf('/api/v1/carts/%s/items/', $carts['order_001']->getId()), [], [], static::$authorizedHeaderWithContentType, $data); + $this->client->request('POST', $url, [], [], static::$authorizedHeaderWithContentType, $data); $response = $this->client->getResponse(); $this->assertResponse($response, 'cart/add_to_cart_response', Response::HTTP_CREATED); @@ -285,7 +304,11 @@ public function it_adds_an_item_to_the_cart() public function it_does_not_allow_to_add_item_with_negative_quantity() { $this->loadFixturesFromFile('authentication/api_administrator.yml'); - $carts = $this->loadFixturesFromFile('resources/carts.yml'); + $carts = $this->loadFixturesFromFile('resources/cart.yml'); + + /** @var OrderInterface $cart */ + $cart = $carts['order_001']; + $url = sprintf('/api/v1/carts/%s/items/', $cart->getId()); $data = <<client->request('POST', sprintf('/api/v1/carts/%s/items/', $carts['order_001']->getId()), [], [], static::$authorizedHeaderWithContentType, $data); + $this->client->request('POST', $url, [], [], static::$authorizedHeaderWithContentType, $data); $response = $this->client->getResponse(); $this->assertResponse($response, 'cart/add_to_cart_quantity_validation_fail_response', Response::HTTP_BAD_REQUEST); @@ -306,7 +329,11 @@ public function it_does_not_allow_to_add_item_with_negative_quantity() public function it_adds_an_item_to_the_cart_with_quantity_bigger_than_one() { $this->loadFixturesFromFile('authentication/api_administrator.yml'); - $carts = $this->loadFixturesFromFile('resources/carts.yml'); + $carts = $this->loadFixturesFromFile('resources/cart.yml'); + + /** @var OrderInterface $cart */ + $cart = $carts['order_001']; + $url = sprintf('/api/v1/carts/%s/items/', $cart->getId()); $data = <<client->request('POST', sprintf('/api/v1/carts/%s/items/', $carts['order_001']->getId()), [], [], static::$authorizedHeaderWithContentType, $data); + $this->client->request('POST', $url, [], [], static::$authorizedHeaderWithContentType, $data); $response = $this->client->getResponse(); $this->assertResponse($response, 'cart/add_to_cart_with_bigger_quantity_response', Response::HTTP_CREATED); @@ -324,10 +351,14 @@ public function it_adds_an_item_to_the_cart_with_quantity_bigger_than_one() /** * @test */ - public function it_adds_an_item_to_the_cart_with_that_contains_tracked_variant() + public function it_adds_an_item_with_tracked_variant_to_the_cart() { $this->loadFixturesFromFile('authentication/api_administrator.yml'); - $carts = $this->loadFixturesFromFile('resources/carts.yml'); + $carts = $this->loadFixturesFromFile('resources/cart.yml'); + + /** @var OrderInterface $cart */ + $cart = $carts['order_001']; + $url = sprintf('/api/v1/carts/%s/items/', $cart->getId()); $data = <<client->request('POST', sprintf('/api/v1/carts/%s/items/', $carts['order_001']->getId()), [], [], static::$authorizedHeaderWithContentType, $data); + $this->client->request('POST', $url, [], [], static::$authorizedHeaderWithContentType, $data); $response = $this->client->getResponse(); $this->assertResponse($response, 'cart/add_to_cart_hard_available_item_response', Response::HTTP_CREATED); @@ -348,7 +379,11 @@ public function it_adds_an_item_to_the_cart_with_that_contains_tracked_variant() public function it_checks_if_requested_variant_is_available() { $this->loadFixturesFromFile('authentication/api_administrator.yml'); - $carts = $this->loadFixturesFromFile('resources/carts.yml'); + $carts = $this->loadFixturesFromFile('resources/cart.yml'); + + /** @var OrderInterface $cart */ + $cart = $carts['order_001']; + $url = sprintf('/api/v1/carts/%s/items/', $cart->getId()); $data = <<client->request('POST', sprintf('/api/v1/carts/%s/items/', $carts['order_001']->getId()), [], [], static::$authorizedHeaderWithContentType, $data); + $this->client->request('POST', $url, [], [], static::$authorizedHeaderWithContentType, $data); $response = $this->client->getResponse(); $this->assertResponse($response, 'cart/add_to_cart_hard_available_item_validation_error_response', Response::HTTP_BAD_REQUEST); @@ -381,10 +416,12 @@ public function it_does_not_allow_to_update_items_variant() { $this->loadFixturesFromFile('authentication/api_administrator.yml'); $fulfilledCart = $this->loadFixturesFromFile('resources/fulfilled_cart.yml'); - /** @var OrderInterface $order */ - $order = $fulfilledCart['fulfilled_cart']; - /** @var OrderItemInterface $orderItem */ - $orderItem = $fulfilledCart['sw_mug_item']; + + /** @var OrderInterface $cart */ + $cart = $fulfilledCart['fulfilled_cart']; + /** @var OrderItemInterface $cartItem */ + $cartItem = $fulfilledCart['sw_mug_item']; + $url = sprintf('/api/v1/carts/%s/items/%s', $cart->getId(), $cartItem->getId()); $data = <<getId(), $orderItem->getId()); $this->client->request('PUT', $url, [], [], static::$authorizedHeaderWithContentType, $data); @@ -408,12 +444,13 @@ public function it_updates_item_quantity() { $this->loadFixturesFromFile('authentication/api_administrator.yml'); $fulfilledCart = $this->loadFixturesFromFile('resources/fulfilled_cart.yml'); - /** @var OrderInterface $order */ - $order = $fulfilledCart['fulfilled_cart']; - /** @var OrderItemInterface $orderItem */ - $orderItem = $fulfilledCart['sw_mug_item']; - $url = sprintf('/api/v1/carts/%s/items/%s', $order->getId(), $orderItem->getId()); + /** @var OrderInterface $cart */ + $cart = $fulfilledCart['fulfilled_cart']; + /** @var OrderItemInterface $cartItem */ + $cartItem = $fulfilledCart['sw_mug_item']; + $updateUrl = sprintf('/api/v1/carts/%s/items/%s', $cart->getId(), $cartItem->getId()); + $orderUrl = sprintf('/api/v1/carts/%s', $cart->getId()); $data = <<client->request('PUT', $url, [], [], static::$authorizedHeaderWithContentType, $data); + $this->client->request('PUT', $updateUrl, [], [], static::$authorizedHeaderWithContentType, $data); $response = $this->client->getResponse(); $this->assertResponseCode($response, Response::HTTP_NO_CONTENT); + + $this->client->request('GET', $orderUrl, [], [], static::$authorizedHeaderWithAccept); + + $response = $this->client->getResponse(); + $this->assertResponse($response, 'cart/increase_quantity_response', Response::HTTP_OK); } /** @@ -434,12 +476,12 @@ public function it_checks_if_requested_variant_is_available_during_quantity_upda { $this->loadFixturesFromFile('authentication/api_administrator.yml'); $fulfilledCart = $this->loadFixturesFromFile('resources/fulfilled_cart.yml'); - /** @var OrderInterface $order */ - $order = $fulfilledCart['fulfilled_cart']; - /** @var OrderItemInterface $orderItem */ - $orderItem = $fulfilledCart['hard_available_mug_item']; - $url = sprintf('/api/v1/carts/%s/items/%s', $order->getId(), $orderItem->getId()); + /** @var OrderInterface $cart */ + $cart = $fulfilledCart['fulfilled_cart']; + /** @var OrderItemInterface $cartItem */ + $cartItem = $fulfilledCart['hard_available_mug_item']; + $url = sprintf('/api/v1/carts/%s/items/%s', $cart->getId(), $cartItem->getId()); $data = <<client->request('DELETE', '/api/v1/carts/-1/items/-1'); @@ -471,10 +513,10 @@ public function it_returns_not_found_response_when_trying_to_delete_cart_item_wh { $this->loadFixturesFromFile('authentication/api_administrator.yml'); $fulfilledCart = $this->loadFixturesFromFile('resources/fulfilled_cart.yml'); - /** @var OrderInterface $order */ - $order = $fulfilledCart['fulfilled_cart']; - $url = sprintf('/api/v1/carts/%s/items/-1', $order->getId()); + /** @var OrderInterface $cart */ + $cart = $fulfilledCart['fulfilled_cart']; + $url = sprintf('/api/v1/carts/%s/items/-1', $cart->getId()); $this->client->request('DELETE', $url, [], [], static::$authorizedHeaderWithContentType); @@ -489,12 +531,12 @@ public function it_allows_to_delete_cart_item() { $this->loadFixturesFromFile('authentication/api_administrator.yml'); $fulfilledCart = $this->loadFixturesFromFile('resources/fulfilled_cart.yml'); - /** @var OrderInterface $order */ - $order = $fulfilledCart['fulfilled_cart']; - /** @var OrderItemInterface $orderItem */ - $orderItem = $fulfilledCart['hard_available_mug_item']; - $url = sprintf('/api/v1/carts/%s/items/%s', $order->getId(), $orderItem->getId()); + /** @var OrderInterface $cart */ + $cart = $fulfilledCart['fulfilled_cart']; + /** @var OrderItemInterface $cartItem */ + $cartItem = $fulfilledCart['hard_available_mug_item']; + $url = sprintf('/api/v1/carts/%s/items/%s', $cart->getId(), $cartItem->getId()); $this->client->request('DELETE', $url, [], [], static::$authorizedHeaderWithContentType); diff --git a/tests/Controller/OrderApiTest.php b/tests/Controller/OrderApiTest.php index 5abab4e1eff..2884981f923 100644 --- a/tests/Controller/OrderApiTest.php +++ b/tests/Controller/OrderApiTest.php @@ -21,14 +21,6 @@ */ final class OrderApiTest extends JsonApiTestCase { - /** - * @var array - */ - private static $authorizedHeaderWithContentType = [ - 'HTTP_Authorization' => 'Bearer SampleTokenNjZkNjY2MDEwMTAzMDkxMGE0OTlhYzU3NzYyMTE0ZGQ3ODcyMDAwM2EwMDZjNDI5NDlhMDdlMQ', - 'CONTENT_TYPE' => 'application/json', - ]; - /** * @var array */ @@ -37,45 +29,6 @@ final class OrderApiTest extends JsonApiTestCase 'CONTENT_TYPE' => 'application/json', ]; - /** - * @test - */ - public function it_denies_order_deletion_for_non_authenticated_user() - { - $this->client->request('DELETE', '/api/v1/orders/-1', [], []); - - $response = $this->client->getResponse(); - $this->assertResponse($response, 'authentication/access_denied_response', Response::HTTP_UNAUTHORIZED); - } - - /** - * @test - */ - public function it_returns_not_found_response_when_trying_to_delete_order_which_does_not_exist() - { - $this->loadFixturesFromFile('authentication/api_administrator.yml'); - - $this->client->request('DELETE', '/api/v1/orders/-1', [], [], static::$authorizedHeaderWithContentType); - - $response = $this->client->getResponse(); - $this->assertResponse($response, 'error/not_found_response', Response::HTTP_NOT_FOUND); - } - - /** - * @test - */ - public function it_allows_to_delete_order() - { - $this->loadFixturesFromFile('authentication/api_administrator.yml'); - $carts = $this->loadFixturesFromFile('resources/order.yml'); - - $this->client->request('DELETE', '/api/v1/orders/'.$carts['order_001']->getId(), [], [], static::$authorizedHeaderWithContentType, []); - - $response = $this->client->getResponse(); - $this->assertResponseCode($response, Response::HTTP_NO_CONTENT); - } - - /** * @test */ @@ -106,7 +59,7 @@ public function it_returns_not_found_response_when_requesting_details_of_an_orde public function it_allows_to_get_cart() { $this->loadFixturesFromFile('authentication/api_administrator.yml'); - $orderData = $this->loadFixturesFromFile('resources/carts.yml'); + $orderData = $this->loadFixturesFromFile('resources/cart.yml'); $this->client->request('GET', '/api/v1/orders/'.$orderData['order_001']->getId(), [], [], static::$authorizedHeaderWithAccept); diff --git a/tests/DataFixtures/ORM/resources/carts.yml b/tests/DataFixtures/ORM/resources/cart.yml similarity index 99% rename from tests/DataFixtures/ORM/resources/carts.yml rename to tests/DataFixtures/ORM/resources/cart.yml index 5ee7f4423e9..dc7293e8d0d 100644 --- a/tests/DataFixtures/ORM/resources/carts.yml +++ b/tests/DataFixtures/ORM/resources/cart.yml @@ -25,7 +25,6 @@ Sylius\Component\Currency\Model\Currency: Sylius\Component\Locale\Model\Locale: locale: code: en_US - enabled: true Sylius\Component\Core\Model\Customer: customer_oliver: diff --git a/tests/DataFixtures/ORM/resources/fulfilled_cart.yml b/tests/DataFixtures/ORM/resources/fulfilled_cart.yml index a2e401648bb..d1e9f2a39b3 100644 --- a/tests/DataFixtures/ORM/resources/fulfilled_cart.yml +++ b/tests/DataFixtures/ORM/resources/fulfilled_cart.yml @@ -5,6 +5,7 @@ Sylius\Component\Core\Model\Order: currencyCode: USD localeCode: en_US customer: "@customer_oliver" + state: cart Sylius\Component\Core\Model\OrderItem: sw_mug_item: @@ -47,7 +48,6 @@ Sylius\Component\Currency\Model\Currency: Sylius\Component\Locale\Model\Locale: locale: code: en_US - enabled: true Sylius\Component\Core\Model\Customer: customer_oliver: diff --git a/tests/DataFixtures/ORM/resources/order.yml b/tests/DataFixtures/ORM/resources/order.yml index 8c742d2384d..3c528ce222a 100644 --- a/tests/DataFixtures/ORM/resources/order.yml +++ b/tests/DataFixtures/ORM/resources/order.yml @@ -4,7 +4,7 @@ Sylius\Component\Core\Model\Order: currencyCode: USD localeCode: en_US customer: "@customer_oliver" - state: new + state: completed Sylius\Component\Core\Model\Channel: channel_web: @@ -25,7 +25,6 @@ Sylius\Component\Currency\Model\Currency: Sylius\Component\Locale\Model\Locale: locale: code: en_US - enabled: true Sylius\Component\Core\Model\Customer: customer_oliver: diff --git a/tests/Responses/Expected/cart/add_to_cart_hard_available_item_response.json b/tests/Responses/Expected/cart/add_to_cart_hard_available_item_response.json index 8bc172f668e..711a14ff45f 100644 --- a/tests/Responses/Expected/cart/add_to_cart_hard_available_item_response.json +++ b/tests/Responses/Expected/cart/add_to_cart_hard_available_item_response.json @@ -7,54 +7,42 @@ "adjustments": [], "adjustments_total": 0, "total": 20, - "state": "cart", - "created_at": "@string@.isDateTime()", - "updated_at": "@string@.isDateTime()", - "channel": { + "customer": { "id": @integer@, + "email": "oliver.queen@star-city.com", + "first_name": "Oliver", + "last_name": "Queen", + "_links": { + "self": { + "href": "@string@" + } + } + }, + "channel": { "code": "WEB", - "name": "Web Channel", - "description": "Lorem ipsum", - "hostname": "localhost", - "color": "black", - "created_at": "@string@.isDateTime()", - "updated_at": "@string@.isDateTime()", - "enabled": true, - "tax_calculation_strategy": "order_items_based", "_links": { "self": { "href": "@string@" } } }, - "payments": [], - "shipments": [], + "currency_code": "USD", "checkout_state": "cart" }, "quantity": 1, "unit_price": 20, "total": 20, - "immutable": false, "units": [ { "id": @integer@, "adjustments": [], - "adjustments_total": 0, - "_links": { - "order": { - "href": "@string@" - } - } + "adjustments_total": 0 } ], "units_total": 20, "adjustments": [], "adjustments_total": 0, - "variant": { - "id": @integer@, - "on_hold": 0, - "tracked": true - }, + "variant": {}, "_links": { "product": { "href": "@string@" diff --git a/tests/Responses/Expected/cart/add_to_cart_response.json b/tests/Responses/Expected/cart/add_to_cart_response.json index bac52af1a2a..711a14ff45f 100644 --- a/tests/Responses/Expected/cart/add_to_cart_response.json +++ b/tests/Responses/Expected/cart/add_to_cart_response.json @@ -1,66 +1,54 @@ { "id": @integer@, "order": { - "id": @integer@, - "items": [], - "items_total": 20, - "adjustments": [], - "adjustments_total": 0, - "total": 20, - "state": "cart", - "created_at": "@string@.isDateTime()", - "updated_at": "@string@.isDateTime()", - "channel": { - "id": @integer@, - "code": "WEB", - "name": "Web Channel", - "description": "Lorem ipsum", - "hostname": "localhost", - "color": "black", - "created_at": "@string@.isDateTime()", - "updated_at": "@string@.isDateTime()", - "enabled": true, - "tax_calculation_strategy": "order_items_based", - "_links": { - "self": { - "href": "@string@" - } - } - }, - "payments": [], - "shipments": [], - "checkout_state": "cart" + "id": @integer@, + "items": [], + "items_total": 20, + "adjustments": [], + "adjustments_total": 0, + "total": 20, + "customer": { + "id": @integer@, + "email": "oliver.queen@star-city.com", + "first_name": "Oliver", + "last_name": "Queen", + "_links": { + "self": { + "href": "@string@" + } + } + }, + "channel": { + "code": "WEB", + "_links": { + "self": { + "href": "@string@" + } + } + }, + "currency_code": "USD", + "checkout_state": "cart" }, "quantity": 1, "unit_price": 20, "total": 20, - "immutable": false, "units": [ - { - "id": @integer@, - "adjustments": [], - "adjustments_total": 0, - "_links": { - "order": { - "href": "@string@" - } - } - } + { + "id": @integer@, + "adjustments": [], + "adjustments_total": 0 + } ], "units_total": 20, "adjustments": [], "adjustments_total": 0, - "variant": { - "id": @integer@, - "on_hold": 0, - "tracked": false - }, + "variant": {}, "_links": { - "product": { - "href": "@string@" - }, - "variant": { - "href": "@string@" - } + "product": { + "href": "@string@" + }, + "variant": { + "href": "@string@" + } } } diff --git a/tests/Responses/Expected/cart/add_to_cart_with_bigger_quantity_response.json b/tests/Responses/Expected/cart/add_to_cart_with_bigger_quantity_response.json index fa614687321..06b2d871bce 100644 --- a/tests/Responses/Expected/cart/add_to_cart_with_bigger_quantity_response.json +++ b/tests/Responses/Expected/cart/add_to_cart_with_bigger_quantity_response.json @@ -7,80 +7,58 @@ "adjustments": [], "adjustments_total": 0, "total": 60, - "state": "cart", - "created_at": "@string@.isDateTime()", - "updated_at": "@string@.isDateTime()", - "channel": { + "customer": { "id": @integer@, + "email": "oliver.queen@star-city.com", + "first_name": "Oliver", + "last_name": "Queen", + "_links": { + "self": { + "href": "@string@" + } + } + }, + "channel": { "code": "WEB", - "name": "Web Channel", - "description": "Lorem ipsum", - "hostname": "localhost", - "color": "black", - "created_at": "@string@.isDateTime()", - "updated_at": "@string@.isDateTime()", - "enabled": true, - "tax_calculation_strategy": "order_items_based", "_links": { "self": { "href": "@string@" } } }, - "payments": [], - "shipments": [], + "currency_code": "USD", "checkout_state": "cart" }, "quantity": 3, "unit_price": 20, "total": 60, - "immutable": false, "units": [ { "id": @integer@, "adjustments": [], - "adjustments_total": 0, - "_links": { - "order": { - "href": "@string@" - } - } + "adjustments_total": 0 }, { "id": @integer@, "adjustments": [], - "adjustments_total": 0, - "_links": { - "order": { - "href": "@string@" - } - } + "adjustments_total": 0 }, { "id": @integer@, "adjustments": [], - "adjustments_total": 0, - "_links": { - "order": { - "href": "@string@" - } - } + "adjustments_total": 0 } ], "units_total": 60, "adjustments": [], "adjustments_total": 0, - "variant": { - "id": @integer@, - "on_hold": 0, - "tracked": false - }, + "variant": {}, "_links": { - "product": { - "href": "@string@" - }, - "variant": { - "href": "@string@" - } + "product": { + "href": "@string@" + }, + "variant": { + "href": "@string@" + } } } diff --git a/tests/Responses/Expected/cart/create_response.json b/tests/Responses/Expected/cart/create_response.json deleted file mode 100644 index 69785632f32..00000000000 --- a/tests/Responses/Expected/cart/create_response.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "id": @integer@, - "items": [], - "items_total": 0, - "adjustments": [], - "adjustments_total": 0, - "total": 0, - "state": "cart", - "created_at": "@string@.isDateTime()", - "updated_at": "@string@.isDateTime()", - "channel": { - "id": @integer@, - "code": "WEB", - "name": "Web Channel", - "description": "Lorem ipsum", - "hostname": "localhost", - "color": "black", - "created_at": "@string@.isDateTime()", - "updated_at": "@string@.isDateTime()", - "enabled": true, - "tax_calculation_strategy": "order_items_based", - "_links": { - "self": { - "href": "@string@" - } - } - }, - "payments": [], - "shipments": [], - "checkout_state": "cart" -} diff --git a/tests/Responses/Expected/cart/increase_quantity_response.json b/tests/Responses/Expected/cart/increase_quantity_response.json new file mode 100644 index 00000000000..db8034ad3c7 --- /dev/null +++ b/tests/Responses/Expected/cart/increase_quantity_response.json @@ -0,0 +1,90 @@ +{ + "id": @integer@, + "items": [ + { + "id": @integer@, + "quantity": 3, + "unit_price": 20, + "total": 60, + "units": [ + { + "id": @integer@, + "adjustments": [], + "adjustments_total": 0 + }, + { + "id": @integer@, + "adjustments": [], + "adjustments_total": 0 + }, + { + "id": @integer@, + "adjustments": [], + "adjustments_total": 0 + } + ], + "units_total": 60, + "adjustments": [], + "adjustments_total": 0, + "variant": {}, + "_links": { + "product": { + "href": "@string@" + }, + "variant": { + "href": "@string@" + } + } + }, + { + "id": @integer@, + "quantity": 1, + "unit_price": 20, + "total": 20, + "units": [ + { + "id": @integer@, + "adjustments": [], + "adjustments_total": 0 + } + ], + "units_total": 20, + "adjustments": [], + "adjustments_total": 0, + "variant": {}, + "_links": { + "product": { + "href": "@string@" + }, + "variant": { + "href": "@string@" + } + } + } + ], + "items_total": 80, + "adjustments": [], + "adjustments_total": 0, + "total": 80, + "customer": { + "id": @integer@, + "email": "oliver.queen@star-city.com", + "first_name": "Oliver", + "last_name": "Queen", + "_links": { + "self": { + "href": "@string@" + } + } + }, + "channel": { + "code": "WEB", + "_links": { + "self": { + "href": "@string@" + } + } + }, + "currency_code": "USD", + "checkout_state": "cart" +} diff --git a/tests/Responses/Expected/cart/index_response.json b/tests/Responses/Expected/cart/index_response.json index c7b7d1754d7..4f44aad8372 100644 --- a/tests/Responses/Expected/cart/index_response.json +++ b/tests/Responses/Expected/cart/index_response.json @@ -23,28 +23,26 @@ "adjustments": [], "adjustments_total": 0, "total": 0, - "state": "cart", - "created_at": "@string@.isDateTime()", - "updated_at": "@string@.isDateTime()", - "channel": { + "customer": { "id": @integer@, + "email": "oliver.queen@star-city.com", + "first_name": "Oliver", + "last_name": "Queen", + "_links": { + "self": { + "href": "@string@" + } + } + }, + "channel": { "code": "WEB", - "name": "Web Channel", - "description": "Lorem ipsum", - "hostname": "localhost", - "color": "black", - "created_at": "@string@.isDateTime()", - "updated_at": "@string@.isDateTime()", - "enabled": true, - "tax_calculation_strategy": "order_items_based", "_links": { "self": { "href": "@string@" } } }, - "payments": [], - "shipments": [], + "currency_code": "USD", "checkout_state": "cart" } ] diff --git a/tests/Responses/Expected/cart/show_response.json b/tests/Responses/Expected/cart/show_response.json index 69785632f32..2e6ddd4f17f 100644 --- a/tests/Responses/Expected/cart/show_response.json +++ b/tests/Responses/Expected/cart/show_response.json @@ -5,27 +5,25 @@ "adjustments": [], "adjustments_total": 0, "total": 0, - "state": "cart", - "created_at": "@string@.isDateTime()", - "updated_at": "@string@.isDateTime()", - "channel": { + "customer": { "id": @integer@, + "email": "oliver.queen@star-city.com", + "first_name": "Oliver", + "last_name": "Queen", + "_links": { + "self": { + "href": "@string@" + } + } + }, + "channel": { "code": "WEB", - "name": "Web Channel", - "description": "Lorem ipsum", - "hostname": "localhost", - "color": "black", - "created_at": "@string@.isDateTime()", - "updated_at": "@string@.isDateTime()", - "enabled": true, - "tax_calculation_strategy": "order_items_based", "_links": { "self": { "href": "@string@" } } }, - "payments": [], - "shipments": [], + "currency_code": "USD", "checkout_state": "cart" } diff --git a/tests/Responses/Expected/checkout/addressed_order_response.json b/tests/Responses/Expected/checkout/addressed_order_response.json index 6201bc8d9d7..87a1fba0e3c 100644 --- a/tests/Responses/Expected/checkout/addressed_order_response.json +++ b/tests/Responses/Expected/checkout/addressed_order_response.json @@ -6,8 +6,17 @@ "adjustments_total": @integer@, "total": @integer@, "state": "cart", - "created_at": "@string@.isDateTime()", - "updated_at": "@string@.isDateTime()", + "customer": { + "id": @integer@, + "email": "john@doe.com", + "email_canonical": "john@doe.com", + "gender": "u", + "_links": { + "self": { + "href": "@string@" + } + } + }, "channel": { "id": @integer@, "code": "CHANNEL", @@ -49,5 +58,6 @@ }, "payments": @array@, "shipments": @array@, + "currency_code": "EUR", "checkout_state": "addressed" } diff --git a/tests/Responses/Expected/checkout/completed_order_response.json b/tests/Responses/Expected/checkout/completed_order_response.json index c511616a61d..fd9e54a08f6 100644 --- a/tests/Responses/Expected/checkout/completed_order_response.json +++ b/tests/Responses/Expected/checkout/completed_order_response.json @@ -8,8 +8,17 @@ "adjustments_total": @integer@, "total": @integer@, "state": "new", - "created_at": "@string@.isDateTime()", - "updated_at": "@string@.isDateTime()", + "customer": { + "id": @integer@, + "email": "john@doe.com", + "email_canonical": "john@doe.com", + "gender": "u", + "_links": { + "self": { + "href": "@string@" + } + } + }, "channel": { "id": @integer@, "code": "CHANNEL", @@ -31,5 +40,6 @@ "billing_address": @...@, "payments": @array@, "shipments": @array@, + "currency_code": "EUR", "checkout_state": "completed" } diff --git a/tests/Responses/Expected/checkout/payment_selected_order_response.json b/tests/Responses/Expected/checkout/payment_selected_order_response.json index 4d73114679e..29275adde78 100644 --- a/tests/Responses/Expected/checkout/payment_selected_order_response.json +++ b/tests/Responses/Expected/checkout/payment_selected_order_response.json @@ -6,8 +6,17 @@ "adjustments_total": @integer@, "total": @integer@, "state": "cart", - "created_at": "@string@.isDateTime()", - "updated_at": "@string@.isDateTime()", + "customer": { + "id": @integer@, + "email": "john@doe.com", + "email_canonical": "john@doe.com", + "gender": "u", + "_links": { + "self": { + "href": "@string@" + } + } + }, "channel": { "id": @integer@, "code": "CHANNEL", @@ -59,5 +68,6 @@ } ], "shipments": @array@, + "currency_code": "EUR", "checkout_state": "payment_selected" } diff --git a/tests/Responses/Expected/checkout/shipping_selected_order_response.json b/tests/Responses/Expected/checkout/shipping_selected_order_response.json index ae816d0b88a..8679daa4dd1 100644 --- a/tests/Responses/Expected/checkout/shipping_selected_order_response.json +++ b/tests/Responses/Expected/checkout/shipping_selected_order_response.json @@ -6,8 +6,17 @@ "adjustments_total": @integer@, "total": @integer@, "state": "cart", - "created_at": "@string@.isDateTime()", - "updated_at": "@string@.isDateTime()", + "customer": { + "id": @integer@, + "email": "john@doe.com", + "email_canonical": "john@doe.com", + "gender": "u", + "_links": { + "self": { + "href": "@string@" + } + } + }, "channel": { "id": @integer@, "code": "CHANNEL", @@ -67,5 +76,6 @@ } } ], + "currency_code": "EUR", "checkout_state": "shipping_selected" } diff --git a/tests/Responses/Expected/customer/create_response.json b/tests/Responses/Expected/customer/create_response.json index e064ecd012f..a29cad6c9ef 100644 --- a/tests/Responses/Expected/customer/create_response.json +++ b/tests/Responses/Expected/customer/create_response.json @@ -4,5 +4,10 @@ "email_canonical": "john.diggle@yahoo.com", "first_name": "John", "last_name": "Diggle", - "gender": "m" + "gender": "m", + "_links": { + "self": { + "href": "@string@" + } + } } diff --git a/tests/Responses/Expected/customer/create_with_user_response.json b/tests/Responses/Expected/customer/create_with_user_response.json index af64bdce9b1..6eec8cdc25e 100644 --- a/tests/Responses/Expected/customer/create_with_user_response.json +++ b/tests/Responses/Expected/customer/create_with_user_response.json @@ -13,5 +13,10 @@ "email_canonical": "john.diggle@yahoo.com", "first_name": "John", "last_name": "Diggle", - "gender": "m" + "gender": "m", + "_links": { + "self": { + "href": "@string@" + } + } } diff --git a/tests/Responses/Expected/customer/index_response.json b/tests/Responses/Expected/customer/index_response.json index 849d31b666d..55c99ee35b4 100644 --- a/tests/Responses/Expected/customer/index_response.json +++ b/tests/Responses/Expected/customer/index_response.json @@ -20,13 +20,23 @@ "id": @integer@, "email": "Joe@doe.com", "first_name": "Joe", - "last_name": "Doe" + "last_name": "Doe", + "_links": { + "self": { + "href": "@string@" + } + } }, { "id": @integer@, "email": "Barry@doe.com", "first_name": "Barry", - "last_name": "Doe" + "last_name": "Doe", + "_links": { + "self": { + "href": "@string@" + } + } }, { "id": @integer@, @@ -37,7 +47,12 @@ }, "email": "Thea@doe.com", "first_name": "Thea", - "last_name": "Doe" + "last_name": "Doe", + "_links": { + "self": { + "href": "@string@" + } + } }, { "id": @integer@, @@ -48,7 +63,12 @@ }, "email": "Roy@doe.com", "first_name": "Roy", - "last_name": "Doe" + "last_name": "Doe", + "_links": { + "self": { + "href": "@string@" + } + } }, { "id": @integer@, @@ -59,7 +79,12 @@ }, "email": "Oliver@doe.com", "first_name": "Oliver", - "last_name": "Doe" + "last_name": "Doe", + "_links": { + "self": { + "href": "@string@" + } + } } ] } diff --git a/tests/Responses/Expected/customer/partial_update_response.json b/tests/Responses/Expected/customer/partial_update_response.json index 2dbe311bf49..f0beeb8aa36 100644 --- a/tests/Responses/Expected/customer/partial_update_response.json +++ b/tests/Responses/Expected/customer/partial_update_response.json @@ -14,5 +14,10 @@ "first_name": "John", "last_name": "Doe", "gender": "u", - "birthday": "@string@.isDateTime()" + "birthday": "@string@.isDateTime()", + "_links": { + "self": { + "href": "@string@" + } + } } diff --git a/tests/Responses/Expected/customer/show_response.json b/tests/Responses/Expected/customer/show_response.json index c44e488024a..dbedbcb959e 100644 --- a/tests/Responses/Expected/customer/show_response.json +++ b/tests/Responses/Expected/customer/show_response.json @@ -5,5 +5,10 @@ "first_name": "Barry", "last_name": "Doe", "birthday": "@string@.isDateTime()", - "gender": "u" + "gender": "u", + "_links": { + "self": { + "href": "@string@" + } + } } diff --git a/tests/Responses/Expected/customer/show_with_user_response.json b/tests/Responses/Expected/customer/show_with_user_response.json index 1adb223d295..4081041d276 100644 --- a/tests/Responses/Expected/customer/show_with_user_response.json +++ b/tests/Responses/Expected/customer/show_with_user_response.json @@ -14,5 +14,10 @@ "first_name": "Roy", "last_name": "Doe", "birthday": "@string@.isDateTime()", - "gender": "u" + "gender": "u", + "_links": { + "self": { + "href": "@string@" + } + } } diff --git a/tests/Responses/Expected/customer/update_response.json b/tests/Responses/Expected/customer/update_response.json index 71b19eab205..4a12deb0f8c 100644 --- a/tests/Responses/Expected/customer/update_response.json +++ b/tests/Responses/Expected/customer/update_response.json @@ -13,5 +13,10 @@ "email_canonical": "john.diggle@example.com", "first_name": "John", "last_name": "Diggle", - "gender": "m" + "gender": "m", + "_links": { + "self": { + "href": "@string@" + } + } } diff --git a/tests/Responses/Expected/order/cart_show_response.json b/tests/Responses/Expected/order/cart_show_response.json index 69785632f32..6484da356d1 100644 --- a/tests/Responses/Expected/order/cart_show_response.json +++ b/tests/Responses/Expected/order/cart_show_response.json @@ -6,8 +6,20 @@ "adjustments_total": 0, "total": 0, "state": "cart", - "created_at": "@string@.isDateTime()", - "updated_at": "@string@.isDateTime()", + "customer": { + "id": @integer@, + "email": "oliver.queen@star-city.com", + "email_canonical": "oliver.queen@star-city.com", + "first_name": "Oliver", + "last_name": "Queen", + "birthday": "@string@.isDateTime()", + "gender": "u", + "_links": { + "self": { + "href": "@string@" + } + } + }, "channel": { "id": @integer@, "code": "WEB", @@ -25,6 +37,7 @@ } } }, + "currency_code": "USD", "payments": [], "shipments": [], "checkout_state": "cart" diff --git a/tests/Responses/Expected/order/order_show_response.json b/tests/Responses/Expected/order/order_show_response.json index 411c7bbfa29..e82d9b27d92 100644 --- a/tests/Responses/Expected/order/order_show_response.json +++ b/tests/Responses/Expected/order/order_show_response.json @@ -5,9 +5,21 @@ "adjustments": [], "adjustments_total": 0, "total": 0, - "state": "new", - "created_at": "@string@.isDateTime()", - "updated_at": "@string@.isDateTime()", + "state": "completed", + "customer": { + "id": @integer@, + "email": "oliver.queen@star-city.com", + "email_canonical": "oliver.queen@star-city.com", + "first_name": "Oliver", + "last_name": "Queen", + "birthday": "@string@.isDateTime()", + "gender": "u", + "_links": { + "self": { + "href": "@string@" + } + } + }, "channel": { "id": @integer@, "code": "WEB", @@ -27,5 +39,6 @@ }, "payments": [], "shipments": [], + "currency_code": "USD", "checkout_state": "cart" } From 1e38891401a5b059810443015832112f9d190321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz?= Date: Wed, 25 Jan 2017 11:47:46 +0100 Subject: [PATCH 08/11] [API] [Documentation] Cart api --- docs/api/carts.rst | 809 ++++++++++++++++++++++++++++++++++++++ docs/api/index.rst | 1 + docs/api/map.rst.inc | 1 + docs/api/orders.rst | 911 ++++++++++++++++++++----------------------- 4 files changed, 1238 insertions(+), 484 deletions(-) create mode 100644 docs/api/carts.rst diff --git a/docs/api/carts.rst b/docs/api/carts.rst new file mode 100644 index 00000000000..c5b772a9bc2 --- /dev/null +++ b/docs/api/carts.rst @@ -0,0 +1,809 @@ +Carts API +========= + +These endpoints will allow you to easily manage cart and cart items. Base URI is `/api/v1/carts/`. + +.. note:: + + If you still don't know the difference between Cart and Order concepts in Sylius, please read :doc:`this article ` carefully. + +Cart structure +-------------- + +Cart API response structure +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you request a cart, you will receive an object with the following fields: + ++-------------------+-------------------------------------------------------------------+ +| Field | Description | ++===================+===================================================================+ +| id | Id of cart | ++-------------------+-------------------------------------------------------------------+ +| items | List of items related to cart | ++-------------------+-------------------------------------------------------------------+ +| items_total | Sum of all items prices | ++-------------------+-------------------------------------------------------------------+ +| adjustments | List of adjustments related to cart | ++-------------------+-------------------------------------------------------------------+ +| adjustments_total | Sum of all order adjustments | ++-------------------+-------------------------------------------------------------------+ +| total | Sum of items total and adjustments total | ++-------------------+-------------------------------------------------------------------+ +| customer | :doc:`Customer detailed serialization ` for order | ++-------------------+-------------------------------------------------------------------+ +| channel | :doc:`Default channel serialization ` | ++-------------------+-------------------------------------------------------------------+ +| currency_code | Currency of the cart | ++-------------------+-------------------------------------------------------------------+ +| checkout_state | State of checkout process | ++-------------------+-------------------------------------------------------------------+ + +CartItem API response structure +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Each cart item will be build as follows: + ++-------------------+------------------------------------------+ +| Field | Description | ++===================+==========================================+ +| id | Id of cart item | ++-------------------+------------------------------------------+ +| quantity | Quantity of item units | ++-------------------+------------------------------------------+ +| unit_price | Price of each item unit | ++-------------------+------------------------------------------+ +| total | Sum of units total and adjustments total | ++-------------------+------------------------------------------+ +| units | List of units related to cart | ++-------------------+------------------------------------------+ +| units_total | Sum of all units prices | ++-------------------+------------------------------------------+ +| adjustments | List of adjustments related to item | ++-------------------+------------------------------------------+ +| adjustments_total | Sum of all item adjustments | ++-------------------+------------------------------------------+ +| variant | Default variant serialization | ++-------------------+------------------------------------------+ + +CartItemUnit API response structure +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Each cart item unit will be build as follows: + ++-------------------+-------------------------------------+ +| Field | Description | ++===================+=====================================+ +| id | Id of cart item unit | ++-------------------+-------------------------------------+ +| adjustments | List of adjustments related to unit | ++-------------------+-------------------------------------+ +| adjustments_total | Sum of all units adjustments | ++-------------------+-------------------------------------+ + +Adjustment API response structure +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +And each adjustment will be build as follows: + ++--------+---------------------------------------------------------+ +| Field | Description | ++========+=========================================================+ +| id | Id of cart item unit | ++--------+---------------------------------------------------------+ +| type | Type of an adjustment (E.g. *order_promotion* or *tax*) | ++--------+---------------------------------------------------------+ +| label | Label of adjustment | ++--------+---------------------------------------------------------+ +| amount | Amount of adjustment | ++--------+---------------------------------------------------------+ + +.. note:: + + If it is still confusing to you, learn more about :doc:`Carts (Orders) ` and :doc:`Adjustments `. + +Creating a Cart +--------------- + +To create a new cart you will need to call the ``/api/v1/carts/`` endpoint with ``POST`` method. + +Definition +^^^^^^^^^^ + +.. code-block:: text + + POST /api/v1/carts/ + ++--------------------+----------------+----------------------------------------------------+ +| Parameter | Parameter type | Description | ++====================+================+====================================================+ +| Authorization | header | Token received during authentication | ++--------------------+----------------+----------------------------------------------------+ +| customer | request | Email of related customer | ++--------------------+----------------+----------------------------------------------------+ +| channel | request | Code of related channel | ++--------------------+----------------+----------------------------------------------------+ +| locale_code | request | Code of locale in which the cart should be created | ++--------------------+----------------+----------------------------------------------------+ +| criteria[customer] | query | Code of locale in which the cart should be created | ++--------------------+----------------+----------------------------------------------------+ + +Example +^^^^^^^ + +To create a new cart for the ``shop@example.com`` user in the ``US_WEB`` channel in the ``en_US`` locale use the below method. + +.. warning:: + + Remember, that it doesn't replicate the environment of shop usage. It is more like an admin part of cart creation, which will allow you to manage + cart from admin perspective. ShopAPI is still an experimental concept. + +.. code-block:: bash + + $ curl http://demo.sylius.org/api/v1/carts/ \ + -H "Authorization: Bearer SampleToken" \ + -H "Content-Type: application/json" \ + -X POST \ + --data ' + { + "customer": "shop@example.com", + "channel": "US_WEB", + "locale_code": "en_US" + } + ' + +Example Response +^^^^^^^^^^^^^^^^ + +.. code-block:: text + + STATUS: 201 Created + +.. code-block:: json + + { + "id":21, + "items":[ + + ], + "items_total":0, + "adjustments":[ + + ], + "adjustments_total":0, + "total":0, + "customer":{ + "id":1, + "email":"shop@example.com", + "first_name":"John", + "last_name":"Doe", + "user":{ + "id":1, + "username":"shop@example.com", + "username_canonical":"shop@example.com" + } + }, + "channel":{ + "code":"US_WEB", + "_links":{ + "self":{ + "href":"\/api\/v1\/channels\/1" + } + } + }, + "currency_code":"USD", + "checkout_state":"cart" + } + +.. note:: + + A currency code will be added automatically based on a channel settings. :doc:`Read more about channels ` + +.. warning:: + + If you try to create a resource without name or code, you will receive a 400 Bad Request error. + +Example +^^^^^^^ + +.. code-block:: bash + + $ curl http://demo.sylius.org/api/v1/carts/ \ + -H "Authorization: Bearer SampleToken" \ + -H "Accept: application/json" \ + -X POST + +Example Response +^^^^^^^^^^^^^^^^ + +.. code-block:: text + + STATUS: 400 Bad Request + +.. code-block:: json + + { + "code":400, + "message":"Validation Failed", + "errors":{ + "children":{ + "customer":{ + "errors":[ + "This value should not be blank." + ] + }, + "localeCode":{ + "errors":[ + "This value should not be blank." + ] + }, + "channel":{ + "errors":[ + "This value should not be blank." + ] + } + } + } + } + +Collection of Carts +------------------- + +To retrieve the paginated list of carts you will need to call the ``/api/v1/carts/`` endpoint with ``GET`` method. + +Definition +^^^^^^^^^^ + +.. code-block:: text + + GET /api/v1/carts/ + ++---------------+----------------+------------------------------------------------------------------+ +| Parameter | Parameter type | Description | ++===============+================+==================================================================+ +| Authorization | header | Token received during authentication | ++---------------+----------------+------------------------------------------------------------------+ +| page | query | *(optional)* Number of the page, by default = 1 | ++---------------+----------------+------------------------------------------------------------------+ +| limit | query | *(optional)* Number of carts displayed per page, by default = 10 | ++---------------+----------------+------------------------------------------------------------------+ + +Example +^^^^^^^ + +To see the first page of all carts use the method below. + +.. code-block:: bash + + $ curl http://demo.sylius.org/api/v1/carts/ \ + -H "Authorization: Bearer SampleToken" \ + -H "Accept: application/json" + +Example Response +^^^^^^^^^^^^^^^^ + +.. code-block:: text + + STATUS: 200 OK + +.. code-block:: json + + { + "page":1, + "limit":10, + "pages":1, + "total":1, + "_links":{ + "self":{ + "href":"\/api\/v1\/carts\/?page=1&limit=10" + }, + "first":{ + "href":"\/api\/v1\/carts\/?page=1&limit=10" + }, + "last":{ + "href":"\/api\/v1\/carts\/?page=1&limit=10" + } + }, + "_embedded":{ + "items":[ + { + "id":21, + "items":[ + + ], + "items_total":0, + "adjustments":[ + + ], + "adjustments_total":0, + "total":0, + "customer":{ + "id":1, + "email":"shop@example.com", + "first_name":"John", + "last_name":"Doe", + "user":{ + "id":1, + "username":"shop@example.com", + "enabled":true + } + }, + "channel":{ + "code":"US_WEB", + "_links":{ + "self":{ + "href":"\/api\/v1\/channels\/1" + } + } + }, + "currency_code":"USD", + "checkout_state":"cart" + } + ] + } + } + +Getting a Single Cart +--------------------- + +To retrieve the details of the cart you will need to call the ``/api/v1/carts/{id}`` endpoint with ``GET`` method. + +Definition +^^^^^^^^^^ + +.. code-block:: text + + GET /api/v1/carts/{id} + ++---------------+----------------+--------------------------------------+ +| Parameter | Parameter type | Description | ++===============+================+======================================+ +| Authorization | header | Token received during authentication | ++---------------+----------------+--------------------------------------+ +| id | url attribute | Id of requested resource | ++---------------+----------------+--------------------------------------+ + +Example +^^^^^^^ + +To see the details of the cart with id 21 use the method below. + +.. code-block:: bash + + $ curl http://demo.sylius.org/api/v1/carts/21 \ + -H "Authorization: Bearer SampleToken" \ + -H "Accept: application/json" + +.. note:: + + The value *21* was taken from create response. Your value can be different. Check list of all carts if you are not sure which id should be used. + +Example Response +^^^^^^^^^^^^^^^^ + +.. code-block:: text + + STATUS: 200 OK + +.. code-block:: json + + { + "id":21, + "items":[ + + ], + "items_total":0, + "adjustments":[ + + ], + "adjustments_total":0, + "total":0, + "customer":{ + "id":1, + "email":"shop@example.com", + "first_name":"John", + "last_name":"Doe", + "user":{ + "id":1, + "username":"shop@example.com", + "username_canonical":"shop@example.com" + } + }, + "channel":{ + "code":"US_WEB", + "_links":{ + "self":{ + "href":"\/api\/v1\/channels\/1" + } + } + }, + "currency_code":"USD", + "checkout_state":"cart" + } + +Deleting a Cart +--------------- + +To delete a cart you will need to call the ``/api/v1/carts/{id}`` endpoint with ``DELETE`` method. + +Definition +^^^^^^^^^^ + +.. code-block:: text + + DELETE /api/v1/carts/{id} + ++---------------+----------------+-------------------------------------------+ +| Parameter | Parameter type | Description | ++===============+================+===========================================+ +| Authorization | header | Token received during authentication | ++---------------+----------------+-------------------------------------------+ +| id | url attribute | Id of requested resource | ++---------------+----------------+-------------------------------------------+ + +Example +^^^^^^^ + +To delete the cart with id 21 use the method below. + +.. code-block:: bash + + $ curl http://demo.sylius.org/api/v1/carts/21 \ + -H "Authorization: Bearer SampleToken" \ + -H "Accept: application/json" \ + -X DELETE + +.. note:: + + Remember the *21* value from the previous example. Here we are deleting a previously fetch cart, so it is the same id. + +Example Response +^^^^^^^^^^^^^^^^ + +.. code-block:: text + + STATUS: 204 No Content + +Creating a Cart Item +-------------------- + +To add a new cart item to the existing cart you will need to call the ``/api/v1/carts/{cartId}/items/`` endpoint with ``POST`` method. + +Definition +^^^^^^^^^^ + +.. code-block:: text + + POST /api/v1/carts/{cartId}/items/ + ++---------------+----------------+---------------------------------------------------------------------+ +| Parameter | Parameter type | Description | ++===============+================+=====================================================================+ +| Authorization | header | Token received during authentication | ++---------------+----------------+---------------------------------------------------------------------+ +| cartId | url attribute | Id of requested cart | ++---------------+----------------+---------------------------------------------------------------------+ +| variant | request | Code of item you want to add to cart | ++---------------+----------------+---------------------------------------------------------------------+ +| quantity | request | Amount of variants you want to add to cart (cannot be lower than 1) | ++---------------+----------------+---------------------------------------------------------------------+ + +Example +^^^^^^^ + +To add a new item with one variant with code MEDIUM_MUG_CUP the cart with id 21(assume, that we didn't remove it in a previous example) use the method below. + +.. code-block:: bash + + $ curl http://demo.sylius.org/api/v1/carts/21/items/ \ + -H "Authorization: Bearer SampleToken" \ + -H "Content-Type: application/json" \ + -X POST \ + --data ' + { + "variant": "MEDIUM_MUG_CUP", + "quantity": 1 + } + ' + +Example Response +^^^^^^^^^^^^^^^^ + +.. code-block:: text + + STATUS: 201 Created + +.. code-block:: json + + { + "id":58, + "order":{ + "id":21, + "items":[ + + ], + "items_total":175, + "adjustments":[ + { + + } + ], + "adjustments_total":7515, + "total":7690, + "customer":{ + "id":1, + "email":"shop@example.com", + "first_name":"John", + "last_name":"Doe", + "user":{ + "id":1, + "username":"shop@example.com", + "username_canonical":"shop@example.com" + }, + "_links":{ + "self":{ + "href":"\/api\/v1\/customers\/1" + } + } + }, + "channel":{ + "code":"US_WEB", + "_links":{ + "self":{ + "href":"\/api\/v1\/channels\/2" + } + } + }, + "currency_code":"USD", + "checkout_state":"cart" + }, + "quantity":1, + "unit_price":175, + "total":175, + "units":[ + { + "id":194, + "adjustments":[ + + ], + "adjustments_total":0 + } + ], + "units_total":175, + "adjustments":[ + + ], + "adjustments_total":0, + "variant":{ + + }, + "_links":{ + "product":{ + "href":"\/api\/v1\/products\/21" + }, + "variant":{ + "href":"\/api\/v1\/products\/21\/variants\/61" + } + } + } + +.. tip:: + + In Sylius the prices are stored as an integers. So in order to present a proper amount to the end user, you should divide price by 100 by default + +Updating a Cart Item +-------------------- + +To change the quantity of cart item you will need to call the ``/api/v1/carts/{cartId}/items/{cartItemId}`` endpoint with ``PUT`` method. + +Definition +^^^^^^^^^^ + +.. code-block:: text + + PUT /api/v1/carts/{cartId}/items/{id} + ++---------------+----------------+---------------------------------------------------------------------+ +| Parameter | Parameter type | Description | ++===============+================+=====================================================================+ +| Authorization | header | Token received during authentication | ++---------------+----------------+---------------------------------------------------------------------+ +| cartId | url attribute | Id of requested cart | ++---------------+----------------+---------------------------------------------------------------------+ +| cartItemId | url attribute | Id of requested cart item | ++---------------+----------------+---------------------------------------------------------------------+ +| quantity | request | Amount of variants you want to add to cart (cannot be lower than 1) | ++---------------+----------------+---------------------------------------------------------------------+ + +Example +^^^^^^^ + +.. code-block:: bash + +To change a quantity to 3 of the cart item with id 58 of cart 21 use the method below. + + $ curl http://demo.sylius.org/api/v1/carts/21/items/58 \ + -H "Authorization: Bearer SampleToken" \ + -H "Content-Type: application/json" \ + -X PUT \ + --data '{"quantity": 3}' + +.. note:: + + If you are not sure where did the value 58 came from, check the previous response, and look for cart item id + +.. tip:: + + This action can be send with *PATCH* method as well + +Example Response +^^^^^^^^^^^^^^^^ + +.. code-block:: text + + STATUS: 204 No Content + +Now we can check what does the cart look like after changing quality + +.. code-block:: bash + + $ curl http://demo.sylius.org/api/v1/carts/21 \ + -H "Authorization: Bearer SampleToken" \ + -H "Accept: application/json" + +Example Response +^^^^^^^^^^^^^^^^ + +.. code-block:: text + + STATUS: 200 OK + +.. code-block:: json + + { + "id":21, + "items":[ + { + "id":58, + "quantity":3, + "unit_price":175, + "total":73, + "units":[ + { + "id":194, + "adjustments":[ + { + "id":215, + "type":"order_promotion", + "label":"Christmas", + "amount":-151 + } + ], + "adjustments_total":-151 + }, + { + "id":195, + "adjustments":[ + { + "id":216, + "type":"order_promotion", + "label":"Christmas", + "amount":-151 + } + ], + "adjustments_total":-151 + }, + { + "id":196, + "adjustments":[ + { + "id":217, + "type":"order_promotion", + "label":"Christmas", + "amount":-150 + } + ], + "adjustments_total":-150 + } + ], + "units_total":73, + "adjustments":[ + + ], + "adjustments_total":0, + "variant":{ + + }, + "_links":{ + "product":{ + "href":"\/api\/v1\/products\/21" + }, + "variant":{ + "href":"\/api\/v1\/products\/21\/variants\/61" + } + } + } + ], + "items_total":73, + "adjustments":[ + { + "id":218, + "type":"shipping", + "label":"UPS", + "amount":7515 + } + ], + "adjustments_total":7515, + "total":7588, + "customer":{ + "id":1, + "email":"shop@example.com", + "first_name":"John", + "last_name":"Doe", + "user":{ + "id":1, + "username":"shop@example.com", + "username_canonical":"shop@example.com" + }, + "_links":{ + "self":{ + "href":"\/api\/v1\/customers\/1" + } + } + }, + "channel":{ + "code":"US_WEB", + "_links":{ + "self":{ + "href":"\/api\/v1\/channels\/2" + } + } + }, + "currency_code":"USD", + "checkout_state":"cart" + } + +.. tip:: + + In this response you can see that promotion and shipping have been taken into account to calculate the appropriate price + +Deleting a Cart Item +-------------------- + +To delete the cart item you will need to call the ``/api/v1/carts/{cartId}/items/{cartItemId}`` endpoint with ``DELETE`` method. + +Definition +^^^^^^^^^^ + +.. code-block:: text + +To delete the cart item with id 58 of cart 21 use the method below. + + DELETE /api/v1/carts/{cartId}/items/{id} + ++---------------+----------------+--------------------------------------+ +| Parameter | Parameter type | Description | ++===============+================+======================================+ +| Authorization | header | Token received during authentication | ++---------------+----------------+--------------------------------------+ +| cartId | url attribute | Id of requested cart | ++---------------+----------------+--------------------------------------+ +| cartItemId | url attribute | Id of requested cart item | ++---------------+----------------+--------------------------------------+ + +Example +^^^^^^^ + +.. code-block:: bash + + $ curl http://demo.sylius.org/api/v1/carts/21/items/58 \ + -H "Authorization: Bearer SampleToken" \ + -H "Accept: application/json" \ + -X DELETE + +Example Response +^^^^^^^^^^^^^^^^ + +.. code-block:: text + + STATUS: 204 No Content diff --git a/docs/api/index.rst b/docs/api/index.rst index 23d63e728b9..01a986c060c 100644 --- a/docs/api/index.rst +++ b/docs/api/index.rst @@ -7,6 +7,7 @@ The REST API Reference introduction authorization channels + carts orders checkouts products diff --git a/docs/api/map.rst.inc b/docs/api/map.rst.inc index 74ff39a57cd..329b154c9a5 100644 --- a/docs/api/map.rst.inc +++ b/docs/api/map.rst.inc @@ -2,6 +2,7 @@ * :doc:`/api/authorization` * :doc:`/api/channels` * :doc:`/api/orders` +* :doc:`/api/carts` * :doc:`/api/checkouts` * :doc:`/api/products` * :doc:`/api/product_variants` diff --git a/docs/api/orders.rst b/docs/api/orders.rst index 42740193a46..556c4a87941 100644 --- a/docs/api/orders.rst +++ b/docs/api/orders.rst @@ -3,165 +3,81 @@ Orders API Sylius orders API endpoint is `/api/v1/orders`. -Index of all orders -------------------- +If you request an order, you will receive an object with the following fields: + ++-----------------------+--------------------------------------------------------------------+ +| Field | Description | ++=======================+====================================================================+ +| id | Id of an order | ++-----------------------+--------------------------------------------------------------------+ +| items | List of items related to order | ++-----------------------+--------------------------------------------------------------------+ +| items_total | Sum of all items prices | ++-----------------------+--------------------------------------------------------------------+ +| adjustments | List of adjustments related to order | ++-----------------------+--------------------------------------------------------------------+ +| adjustments_total | Sum of all order adjustments | ++-----------------------+--------------------------------------------------------------------+ +| total | Sum of items total and adjustments total | ++-----------------------+--------------------------------------------------------------------+ +| customer | :doc:`Customer detailed serialization ` for order | ++-----------------------+--------------------------------------------------------------------+ +| channel | :doc:`Default channel serialization ` | ++-----------------------+--------------------------------------------------------------------+ +| currency_code | Currency of a order | ++-----------------------+--------------------------------------------------------------------+ +| checkout_state | :doc:`State of checkout process ` | ++-----------------------+--------------------------------------------------------------------+ +| state | :doc:`One of available order states ` | ++-----------------------+--------------------------------------------------------------------+ +| checkout_completed_at | Date when the checkout has been completed | ++-----------------------+--------------------------------------------------------------------+ +| number | Serial number of an order | ++-----------------------+--------------------------------------------------------------------+ +| shipping_address | Detailed address serialization | ++-----------------------+--------------------------------------------------------------------+ +| billing_address | Detailed address serialization | ++-----------------------+--------------------------------------------------------------------+ +| shipments | Detailed shipment serialization | ++-----------------------+--------------------------------------------------------------------+ +| payments | Detailed payment serialization | ++-----------------------+--------------------------------------------------------------------+ -You can retrieve the full list order by making the following request: - -.. code-block:: text - - GET /api/v1/orders/ - -Parameters -~~~~~~~~~~ - -page - Number of the page, by default = 1 -limit - Number of items to display per page +Getting a single order +---------------------- -Response -~~~~~~~~ +You can request detailed order information by executing the following request: -The response will contain the newly created order information. +Definition +^^^^^^^^^^ .. code-block:: text - STATUS: 200 OK + GET /api/v1/orders/{id} -.. code-block:: json - - { - "page":1, - "limit":10, - "pages":12, - "total":120, - "_links":{ - "self":{ - "href":"\/api\/orders\/?page=1" - }, - "first":{ - "href":"\/api\/orders\/?page=1" - }, - "last":{ - "href":"\/api\/orders\/?page=12" - }, - "next":{ - "href":"\/api\/orders\/?page=2" - } - }, - "_embedded":{ - "items":[ - { - "id":301, - "checkout_completed_at":"2014-11-26T23:00:33+0000", - "number":"000000048", - "items":[ - { - "id":1353, - "quantity":3, - "unit_price":9054, - "adjustments":[ ++---------------+----------------+--------------------------------------+ +| Parameter | Parameter type | Description | ++===============+================+======================================+ +| Authorization | header | Token received during authentication | ++---------------+----------------+--------------------------------------+ +| id | url attribute | Id of requested resource | ++---------------+----------------+--------------------------------------+ - ], - "adjustments_total":0, - "total":27162, - "immutable":false, - "variant":{ - "id":13099, - "object":{ - "id":2107, - "name":"T-Shirt \"voluptas\"", - "code": "T-SHIRT-VOLUPTAS", - "description":"Non molestias voluptas quae nemo omnis totam. Impedit ad perferendis quaerat sint numquam voluptate eum. Facilis sed accusamus enim repellendus officiis rerum at.", - "created_at":"2014-11-26T23:00:17+0000", - "updated_at":"2014-11-26T23:00:17+0000", - "short_description":"Quos in dignissimos in fugit culpa vitae." - }, - "created_at":"2014-11-26T23:00:17+0000", - "updated_at":"2014-11-26T23:00:34+0000", - "available_on":"2013-12-10T09:16:56+0000", - "sku":"8808" - }, - "inventory_units":[ - ], - "_links":{ - "product":{ - "href":"\/api\/products\/2107" - }, - "variant":{ - "href":"\/api\/products\/2107\/variants\/13099" - } - } - } - ], - "items_total":97783, - "adjustments":[ - ], - "comments":[ - - ], - "adjustments_total":24240, - "total":122023, - "confirmed":true, - "created_at":"2014-04-30T10:41:14+0000", - "updated_at":"2014-11-26T23:00:34+0000", - "state":"pending", - "email":"ygrant@example.com", - "expires_at":"2014-11-27T02:00:33+0000", - "user":{ - "id":476, - "username":"ygrant@example.com", - "username_canonical":"ygrant@example.com", - "email":"ygrant@example.com", - "email_canonical":"ygrant@example.com", - "enabled":false, - "group":[ - - ], - "locked":false, - "expired":false, - "roles":[ - - ], - "credentials_expired":false - }, - "channel":{ - "id":91, - "code":"WEB-UK", - "name":"UK Webstore", - "type":"web", - "color":"Red", - "enabled":true, - "created_at":"2014-11-26T23:00:15+0000", - "updated_at":"2014-11-26T23:00:15+0000", - }, - "shipping_address":{ - }, - "billing_address":{ - }, - "payments":[ - ], - "shipments":[ - ], - "currency":"GBP", - "checkout_state":"cart" - } - ] - } - } +Example +^^^^^^^ -Getting a single order ----------------------- +.. code-block:: bash -You can view a single order by executing the following request: + $ curl http://demo.sylius.org/api/v1/orders/21 \ + -H "Authorization: Bearer SampleToken" \ + -H "Accept: application/json" -.. code-block:: text +.. note:: - GET /api/v1/orders/24/ + The value *21* was taken from previous responses, where we managed a cart. Your value can be different. If you need more information about cart api please, check :doc:`this doc ` -Response -~~~~~~~~ +Example Response +~~~~~~~~~~~~~~~~ .. code-block:: text @@ -170,403 +86,430 @@ Response .. code-block:: json { - "id":301, - "checkout_completed_at":"2014-11-26T23:00:33+0000", - "number":"000000048", + "id":21, + "checkout_completed_at":"2017-01-27T16:15:08+0100", + "number":"000000020", "items":[ { - "id":1353, + "id":54, "quantity":3, - "unit_price":9054, + "unit_price":70, + "total":198, + "units":[ + { + "id":166, + "adjustments":[ + { + "id":139, + "type":"order_promotion", + "label":"Christmas", + "amount":-4 + } + ], + "adjustments_total":-4, + "_links":{ + "order":{ + "href":"\/api\/v1\/orders\/21" + } + } + }, + { + "id":167, + "adjustments":[ + { + "id":140, + "type":"order_promotion", + "label":"Christmas", + "amount":-4 + } + ], + "adjustments_total":-4, + "_links":{ + "order":{ + "href":"\/api\/v1\/orders\/21" + } + } + }, + { + "id":168, + "adjustments":[ + { + "id":141, + "type":"order_promotion", + "label":"Christmas", + "amount":-4 + } + ], + "adjustments_total":-4, + "_links":{ + "order":{ + "href":"\/api\/v1\/orders\/21" + } + } + } + ], + "units_total":198, "adjustments":[ ], "adjustments_total":0, - "total":27162, - "immutable":false, "variant":{ - "id":13099, - "object":{ - "id":2107, - "name":"T-Shirt \"voluptas\"", - "code": "T-SHIRT-VOLUPTAS" - "description":"Non molestias voluptas quae nemo omnis totam. Impedit ad perferendis quaerat sint numquam voluptate eum. Facilis sed accusamus enim repellendus officiis rerum at.", - "created_at":"2014-11-26T23:00:17+0000", - "updated_at":"2014-11-26T23:00:17+0000", - "short_description":"Quos in dignissimos in fugit culpa vitae." + "id":37, + "on_hold":0, + "tracked":false + }, + "_links":{ + "product":{ + "href":"\/api\/v1\/products\/13" }, - "created_at":"2014-11-26T23:00:17+0000", - "updated_at":"2014-11-26T23:00:34+0000", - "available_on":"2013-12-10T09:16:56+0000", - "sku":"8808" + "variant":{ + "href":"\/api\/v1\/products\/13\/variants\/37" + } + } + }, + { + "id":55, + "quantity":1, + "unit_price":818, + "total":769, + "units":[ + { + "id":169, + "adjustments":[ + { + "id":142, + "type":"order_promotion", + "label":"Christmas", + "amount":-49 + } + ], + "adjustments_total":-49, + "_links":{ + "order":{ + "href":"\/api\/v1\/orders\/21" + } + } + } + ], + "units_total":769, + "adjustments":[ + + ], + "adjustments_total":0, + "variant":{ + "id":289, + "on_hold":0, + "tracked":false }, - "inventory_units":[ + "_links":{ + "product":{ + "href":"\/api\/v1\/products\/58" + }, + "variant":{ + "href":"\/api\/v1\/products\/58\/variants\/289" + } + } + }, + { + "id":56, + "quantity":2, + "unit_price":338, + "total":635, + "units":[ { - "id":4061, - "inventory_state":"onhold", - "created_at":"2014-11-26T23:00:34+0000", - "updated_at":"2014-11-26T23:00:34+0000", + "id":170, + "adjustments":[ + { + "id":143, + "type":"order_promotion", + "label":"Christmas", + "amount":-21 + } + ], + "adjustments_total":-21, "_links":{ "order":{ - "href":"\/app_dev.php\/api\/orders\/301" + "href":"\/api\/v1\/orders\/21" } } }, { - "id":4062, - "inventory_state":"onhold", - "created_at":"2014-11-26T23:00:34+0000", - "updated_at":"2014-11-26T23:00:34+0000", + "id":171, + "adjustments":[ + { + "id":144, + "type":"order_promotion", + "label":"Christmas", + "amount":-20 + } + ], + "adjustments_total":-20, "_links":{ "order":{ - "href":"\/app_dev.php\/api\/orders\/301" + "href":"\/api\/v1\/orders\/21" + } + } + } + ], + "units_total":635, + "adjustments":[ + + ], + "adjustments_total":0, + "variant":{ + "id":12, + "on_hold":0, + "tracked":false + }, + "_links":{ + "product":{ + "href":"\/api\/v1\/products\/4" + }, + "variant":{ + "href":"\/api\/v1\/products\/4\/variants\/12" + } + } + }, + { + "id":57, + "quantity":3, + "unit_price":520, + "total":1466, + "units":[ + { + "id":172, + "adjustments":[ + { + "id":145, + "type":"order_promotion", + "label":"Christmas", + "amount":-32 + } + ], + "adjustments_total":-32, + "_links":{ + "order":{ + "href":"\/api\/v1\/orders\/21" } } }, { - "id":4063, - "inventory_state":"onhold", - "created_at":"2014-11-26T23:00:34+0000", - "updated_at":"2014-11-26T23:00:34+0000", + "id":173, + "adjustments":[ + { + "id":146, + "type":"order_promotion", + "label":"Christmas", + "amount":-31 + } + ], + "adjustments_total":-31, "_links":{ "order":{ - "href":"\/app_dev.php\/api\/orders\/301" + "href":"\/api\/v1\/orders\/21" + } + } + }, + { + "id":174, + "adjustments":[ + { + "id":147, + "type":"order_promotion", + "label":"Christmas", + "amount":-31 + } + ], + "adjustments_total":-31, + "_links":{ + "order":{ + "href":"\/api\/v1\/orders\/21" } } } ], + "units_total":1466, + "adjustments":[ + + ], + "adjustments_total":0, + "variant":{ + "id":56, + "on_hold":0, + "tracked":false + }, "_links":{ "product":{ - "href":"\/app_dev.php\/api\/products\/2107" + "href":"\/api\/v1\/products\/19" }, "variant":{ - "href":"\/app_dev.php\/api\/products\/2107\/variants\/13099" + "href":"\/api\/v1\/products\/19\/variants\/56" } } } ], - "items_total":97783, + "items_total":3068, "adjustments":[ { - "id":1011, - "type":"tax", - "description":"EU VAT (23%)", - "amount":22490, - "neutral":false, - "locked":false, - "created_at":"2014-11-26T23:00:33+0000", - "updated_at":"2014-11-26T23:00:34+0000" - }, - { - "id":1012, + "id":148, "type":"shipping", - "description":"UPS Ground", - "amount":2500, - "neutral":false, - "locked":false, - "created_at":"2014-11-26T23:00:33+0000", - "updated_at":"2014-11-26T23:00:34+0000" - }, - { - "id":1013, - "type":"promotion", - "description":"New Year Sale for 3 and more items.", - "amount":-500, - "neutral":false, - "locked":false, - "created_at":"2014-11-26T23:00:33+0000", - "updated_at":"2014-11-26T23:00:34+0000" - }, - { - "id":1014, - "type":"promotion", - "description":"Christmas Sale for orders over 100 EUR.", - "amount":-250, - "neutral":false, - "locked":false, - "created_at":"2014-11-26T23:00:33+0000", - "updated_at":"2014-11-26T23:00:34+0000" + "label":"DHL Express", + "amount":2160 } ], - "comments":[ - - ], - "adjustments_total":24240, - "total":122023, - "confirmed":true, - "created_at":"2014-04-30T10:41:14+0000", - "updated_at":"2014-11-26T23:00:34+0000", - "state":"pending", - "email":"ygrant@example.com", - "expires_at":"2014-11-27T02:00:33+0000", - "user":{ - "id":476, - "username":"ygrant@example.com", - "username_canonical":"ygrant@example.com", - "email":"ygrant@example.com", - "email_canonical":"ygrant@example.com", - "enabled":false, - "group":[ - - ], - "locked":false, - "expired":false, - "roles":[ - - ], - "credentials_expired":false + "adjustments_total":2160, + "total":5228, + "state":"new", + "customer":{ + "id":8, + "email":"eturner@senger.com", + "email_canonical":"eturner@senger.com", + "first_name":"Ricky", + "last_name":"Swift", + "gender":"u", + "user":{ + "id":8, + "username":"eturner@senger.com", + "username_canonical":"eturner@senger.com", + "roles":[ + "ROLE_USER" + ], + "enabled":true + }, + "_links":{ + "self":{ + "href":"\/api\/v1\/customers\/8" + } + } }, "channel":{ - "id":91, - "code":"WEB-UK", - "name":"UK Webstore", - "type":"web", - "color":"Red", + "id":1, + "code":"US_WEB", + "name":"US Web Store", + "hostname":"localhost:8000", + "color":"MediumSpringGreen", + "created_at":"2017-01-27T16:15:02+0100", + "updated_at":"2017-01-27T16:15:02+0100", "enabled":true, - "created_at":"2014-11-26T23:00:15+0000", - "updated_at":"2014-11-26T23:00:15+0000", + "tax_calculation_strategy":"order_items_based", + "_links":{ + "self":{ + "href":"\/api\/v1\/channels\/1" + } + } }, "shipping_address":{ + "id":58, + "first_name":"Mittie", + "last_name":"Schoen", + "country_code":"US", + "street":"Gutmann Parkways", + "city":"West Devonte", + "postcode":"68192-0107", + "created_at":"2017-01-27T16:15:08+0100", + "updated_at":"2017-01-27T16:15:08+0100" }, "billing_address":{ + "id":59, + "first_name":"Mittie", + "last_name":"Schoen", + "country_code":"US", + "street":"Gutmann Parkways", + "city":"West Devonte", + "postcode":"68192-0107", + "created_at":"2017-01-27T16:15:08+0100", + "updated_at":"2017-01-27T16:15:08+0100" }, "payments":[ - ], - "shipments":[ - ], - "currency":"GBP", - "checkout_state":"cart" - } - -Create an order ---------------- - -To create a new order (cart), you need to execute the following request: - -.. code-block:: text - - POST /api/v1/orders/ - -Parameters -~~~~~~~~~~ - -channel - Channel code (e.g. "US_WEB") -customer *(optional)* - The id of customer -localeCode - Locale code (e.g. "en_US") - -Response -~~~~~~~~ - -.. code-block:: text - - STATUS: 201 CREATED - -.. code-block:: json - - { - "id":304, - "items":[ - ], - "items_total":0, - "adjustments":[ - ], - "comments":[ - - ], - "adjustments_total":0, - "total":0, - "confirmed":true, - "created_at":"2014-11-29T12:29:07+0000", - "updated_at":"2014-11-29T12:29:08+0000", - "state":"cart", - "email":"chelsie.witting@example.com", - "expires_at":"2014-11-29T15:29:07+0000", - "user":{ - "id":481, - "username":"chelsie.witting@example.com", - "username_canonical":"chelsie.witting@example.com", - "email":"chelsie.witting@example.com", - "email_canonical":"chelsie.witting@example.com", - "enabled":true, - "group":[ - - ], - "locked":false, - "expired":false, - "roles":[ - - ], - "credentials_expired":false - }, - "channel":{ - "id":91, - "code":"WEB-UK", - "name":"UK Webstore", - "type":"web", - "color":"Red", - "enabled":true, - "created_at":"2014-11-26T23:00:15+0000", - "updated_at":"2014-11-26T23:00:15+0000", - }, - "payments":[ - ], - "shipments":[ - ], - "currency":"USD", - "checkout_state":"cart" - } - -Deleting a single order ------------------------ - -You can delete (soft) an order from the system by making the following DELETE call: - -.. code-block:: text - - DELETE /api/v1/orders/24 - -Response -~~~~~~~~ - -.. code-block:: text - - STATUS: 204 NO CONTENT - -Add an item to order --------------------- - -To add an item to order, you simply need to do a POST request: - -.. code-block:: text - - POST /api/v1/orders/305/items/ - -Parameters -~~~~~~~~~~ - -variant - The id of product variant -unit_price - Unit price of the item -quantity - Desired quantity - -Response -~~~~~~~~ - -Response will contain a representation of the newly created item. - -.. code-block:: text - - STATUS: 201 CREATED - -.. code-block:: json - - { - "_links": { - "product": { - "href": "/app_dev.php/api/v1/products/101" - }, - "variant": { - "href": "/app_dev.php/api/v1/products/101/variants/779" - } - }, - "adjustments": [], - "adjustments_total": 0, - "id": 277, - "immutable": false, - "inventory_units": [ { - "_links": { - "order": { - "href": "/app_dev.php/api/v1/orders/52" + "id":20, + "method":{ + "id":1, + "code":"cash_on_delivery", + "created_at":"2017-01-27T16:15:02+0100", + "updated_at":"2017-01-27T16:15:02+0100", + "channels":[ + { + "id":1, + "code":"US_WEB", + "name":"US Web Store", + "hostname":"localhost:8000", + "color":"MediumSpringGreen", + "created_at":"2017-01-27T16:15:02+0100", + "updated_at":"2017-01-27T16:15:02+0100", + "enabled":true, + "tax_calculation_strategy":"order_items_based", + "_links":{ + "self":{ + "href":"\/api\/v1\/channels\/1" + } + } + } + ], + "_links":{ + "self":{ + "href":"\/api\/v1\/payment-methods\/1" + } } }, - "created_at": "2014-12-15T13:18:48+0000", - "id": 828, - "inventory_state": "checkout", - "updated_at": "2014-12-15T13:18:48+0000" - }, - { - "_links": { - "order": { - "href": "/app_dev.php/api/v1/orders/52" + "amount":5228, + "state":"new", + "created_at":"2017-01-27T16:15:08+0100", + "updated_at":"2017-01-27T16:15:08+0100", + "_links":{ + "self":{ + "href":"\/api\/v1\/payments\/20" + }, + "payment-method":{ + "href":"\/api\/v1\/payment-methods\/1" + }, + "order":{ + "href":"\/api\/v1\/orders\/21" } - }, - "created_at": "2014-12-15T13:18:48+0000", - "id": 829, - "inventory_state": "checkout", - "updated_at": "2014-12-15T13:18:48+0000" - }, + } + } + ], + "shipments":[ { - "_links": { - "order": { - "href": "/app_dev.php/api/v1/orders/52" + "id":20, + "state":"ready", + "method":{ + "id":2, + "code":"dhl_express", + "category_requirement":1, + "calculator":"flat_rate", + "configuration":{ + "US_WEB":{ + "amount":2160 + } + }, + "created_at":"2017-01-27T16:15:02+0100", + "updated_at":"2017-01-27T16:15:02+0100", + "enabled":true, + "_links":{ + "self":{ + "href":"\/api\/v1\/shipping-methods\/dhl_express" + }, + "zone":{ + "href":"\/api\/v1\/zones\/US" + } } }, - "created_at": "2014-12-15T13:18:48+0000", - "id": 830, - "inventory_state": "checkout", - "updated_at": "2014-12-15T13:18:48+0000" - } - ], - "quantity": 3, - "total": 0, - "unit_price": 500000, - "variant": { - "available_on": "2014-04-01T06:43:02+0000", - "created_at": "2014-12-03T09:54:35+0000", - "id": 779, - "object": { - "attributes": [ - { - "id": 238, - "name": "Book author", - "presentation": "Author", - "value": "Marlen Yost" + "created_at":"2017-01-27T16:15:08+0100", + "updated_at":"2017-01-27T16:15:08+0100", + "_links":{ + "self":{ + "href":"\/api\/v1\/shipments\/20" }, - { - "id": 239, - "name": "Book ISBN", - "presentation": "ISBN", - "value": "326ccbc7-92d1-3aec-b3af-df8afdc5651d" + "method":{ + "href":"\/api\/v1\/shipping-methods\/dhl_express" }, - { - "id": 240, - "name": "Book pages", - "presentation": "Number of pages", - "value": "149" + "order":{ + "href":"\/api\/v1\/orders\/21" } - ], - "created_at": "2014-12-03T09:54:35+0000", - "description": "Et eveniet voluptas ut magni vero temporibus nihil. Omnis possimus accusantium quia corporis culpa. Et recusandae asperiores qui architecto culpa autem sint accusantium. Officiis iusto accusantium perferendis aliquid ducimus.", - "id": 101, - "name": "Book \"Quidem\" by \"Marlen Yost\"", - "options": [], - "short_description": "Distinctio quos est eaque fugit totam repellendus.", - "updated_at": "2014-12-03T09:54:35+0000" - }, - "options": [], - "sku": "326ccbc7-92d1-3aec-b3af-df8afdc5651d", - "updated_at": "2014-12-03T09:54:35+0000" - } + } + } + ], + "currency_code":"USD", + "checkout_state":"completed" } - -Removing an item from order ---------------------------- - -To remove an item from order, you can simply call a DELETE on its url. - -.. code-block:: text - - DELETE /api/v1/orders/49/items/245 - -Response -~~~~~~~~ - -.. code-block:: text - - STATUS: 204 NO CONTENT From 9fc6f7f37affccd763f7f1bfb2332af20aa0eeb7 Mon Sep 17 00:00:00 2001 From: Magdalena Banasiak Date: Mon, 6 Feb 2017 16:59:19 +0100 Subject: [PATCH 09/11] [Documentation][API] Carts & Orders - Review fixes --- docs/api/carts.rst | 288 ++++++++++++++++++++++---------------------- docs/api/orders.rst | 19 +-- 2 files changed, 157 insertions(+), 150 deletions(-) diff --git a/docs/api/carts.rst b/docs/api/carts.rst index c5b772a9bc2..91288d6b6cf 100644 --- a/docs/api/carts.rst +++ b/docs/api/carts.rst @@ -5,7 +5,9 @@ These endpoints will allow you to easily manage cart and cart items. Base URI is .. note:: - If you still don't know the difference between Cart and Order concepts in Sylius, please read :doc:`this article ` carefully. + Remember that a **Cart** in Sylius is an **Order** in the state ``cart``. + + If you don't understand the difference between Cart and Order concepts in Sylius yet, please read :doc:`this article ` carefully. Cart structure -------------- @@ -13,20 +15,20 @@ Cart structure Cart API response structure ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -If you request a cart, you will receive an object with the following fields: +If you request a cart via API, you will receive an object with the following fields: +-------------------+-------------------------------------------------------------------+ | Field | Description | +===================+===================================================================+ -| id | Id of cart | +| id | Id of the cart | +-------------------+-------------------------------------------------------------------+ -| items | List of items related to cart | +| items | List of items in the cart | +-------------------+-------------------------------------------------------------------+ | items_total | Sum of all items prices | +-------------------+-------------------------------------------------------------------+ -| adjustments | List of adjustments related to cart | +| adjustments | List of adjustments related to the cart | +-------------------+-------------------------------------------------------------------+ -| adjustments_total | Sum of all order adjustments | +| adjustments_total | Sum of all order adjustments values | +-------------------+-------------------------------------------------------------------+ | total | Sum of items total and adjustments total | +-------------------+-------------------------------------------------------------------+ @@ -36,71 +38,72 @@ If you request a cart, you will receive an object with the following fields: +-------------------+-------------------------------------------------------------------+ | currency_code | Currency of the cart | +-------------------+-------------------------------------------------------------------+ -| checkout_state | State of checkout process | +| checkout_state | State of the checkout process of the cart | +-------------------+-------------------------------------------------------------------+ CartItem API response structure ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Each cart item will be build as follows: +Each CartItem in an API response will be build as follows: + ++-------------------+------------------------------------------------------------+ +| Field | Description | ++===================+============================================================+ +| id | Id of the cart item | ++-------------------+------------------------------------------------------------+ +| quantity | Quantity of item units | ++-------------------+------------------------------------------------------------+ +| unit_price | Price of each item unit | ++-------------------+------------------------------------------------------------+ +| total | Sum of units total and adjustments total of that cart item | ++-------------------+------------------------------------------------------------+ +| units | A collection of units related to the cart item | ++-------------------+------------------------------------------------------------+ +| units_total | Sum of all units prices of the cart item | ++-------------------+------------------------------------------------------------+ +| adjustments | List of adjustments related to the cart item | ++-------------------+------------------------------------------------------------+ +| adjustments_total | Sum of all item adjustments related to that cart item | ++-------------------+------------------------------------------------------------+ +| variant | Default variant serialization | ++-------------------+------------------------------------------------------------+ + +CartItemUnit API response structure +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Each CartItemUnit API response will be build as follows: +-------------------+------------------------------------------+ | Field | Description | +===================+==========================================+ -| id | Id of cart item | -+-------------------+------------------------------------------+ -| quantity | Quantity of item units | -+-------------------+------------------------------------------+ -| unit_price | Price of each item unit | -+-------------------+------------------------------------------+ -| total | Sum of units total and adjustments total | +| id | Id of the cart item unit | +-------------------+------------------------------------------+ -| units | List of units related to cart | +| adjustments | List of adjustments related to the unit | +-------------------+------------------------------------------+ -| units_total | Sum of all units prices | +| adjustments_total | Sum of all units adjustments of the unit | +-------------------+------------------------------------------+ -| adjustments | List of adjustments related to item | -+-------------------+------------------------------------------+ -| adjustments_total | Sum of all item adjustments | -+-------------------+------------------------------------------+ -| variant | Default variant serialization | -+-------------------+------------------------------------------+ - -CartItemUnit API response structure -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Each cart item unit will be build as follows: - -+-------------------+-------------------------------------+ -| Field | Description | -+===================+=====================================+ -| id | Id of cart item unit | -+-------------------+-------------------------------------+ -| adjustments | List of adjustments related to unit | -+-------------------+-------------------------------------+ -| adjustments_total | Sum of all units adjustments | -+-------------------+-------------------------------------+ Adjustment API response structure ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -And each adjustment will be build as follows: +And each Adjustment will be build as follows: -+--------+---------------------------------------------------------+ -| Field | Description | -+========+=========================================================+ -| id | Id of cart item unit | -+--------+---------------------------------------------------------+ -| type | Type of an adjustment (E.g. *order_promotion* or *tax*) | -+--------+---------------------------------------------------------+ -| label | Label of adjustment | -+--------+---------------------------------------------------------+ -| amount | Amount of adjustment | -+--------+---------------------------------------------------------+ ++--------+----------------------------------------------------------+ +| Field | Description | ++========+==========================================================+ +| id | Id of the adjustment | ++--------+----------------------------------------------------------+ +| type | Type of the adjustment (E.g. *order_promotion* or *tax*) | ++--------+----------------------------------------------------------+ +| label | Label of the adjustment | ++--------+----------------------------------------------------------+ +| amount | Amount of the adjustment (value) | ++--------+----------------------------------------------------------+ .. note:: - If it is still confusing to you, learn more about :doc:`Carts (Orders) ` and :doc:`Adjustments `. + If it is confusing to you, learn more about :doc:`Carts (Orders) in the component docs ` + and :doc:`Adjustments concept `. Creating a Cart --------------- @@ -114,29 +117,27 @@ Definition POST /api/v1/carts/ -+--------------------+----------------+----------------------------------------------------+ -| Parameter | Parameter type | Description | -+====================+================+====================================================+ -| Authorization | header | Token received during authentication | -+--------------------+----------------+----------------------------------------------------+ -| customer | request | Email of related customer | -+--------------------+----------------+----------------------------------------------------+ -| channel | request | Code of related channel | -+--------------------+----------------+----------------------------------------------------+ -| locale_code | request | Code of locale in which the cart should be created | -+--------------------+----------------+----------------------------------------------------+ -| criteria[customer] | query | Code of locale in which the cart should be created | -+--------------------+----------------+----------------------------------------------------+ ++--------------------+----------------+----------------------------------------------------------+ +| Parameter | Parameter type | Description | ++====================+================+==========================================================+ +| Authorization | header | Token received during authentication | ++--------------------+----------------+----------------------------------------------------------+ +| customer | request | Email of the related customer | ++--------------------+----------------+----------------------------------------------------------+ +| channel | request | Code of the related channel | ++--------------------+----------------+----------------------------------------------------------+ +| locale_code | request | Code of the locale in which the cart should be created | ++--------------------+----------------+----------------------------------------------------------+ Example ^^^^^^^ -To create a new cart for the ``shop@example.com`` user in the ``US_WEB`` channel in the ``en_US`` locale use the below method. +To create a new cart for the ``shop@example.com`` user in the ``US_WEB`` channel with the ``en_US`` locale use the below method: .. warning:: Remember, that it doesn't replicate the environment of shop usage. It is more like an admin part of cart creation, which will allow you to manage - cart from admin perspective. ShopAPI is still an experimental concept. + cart from the admin perspective. ShopAPI is still an experimental concept. .. code-block:: bash @@ -152,8 +153,8 @@ To create a new cart for the ``shop@example.com`` user in the ``US_WEB`` channel } ' -Example Response -^^^^^^^^^^^^^^^^ +Exemplary Response +^^^^^^^^^^^^^^^^^^ .. code-block:: text @@ -197,11 +198,11 @@ Example Response .. note:: - A currency code will be added automatically based on a channel settings. :doc:`Read more about channels ` + A currency code will be added automatically based on the channel settings. Read more about channels :doc:`here `. .. warning:: - If you try to create a resource without name or code, you will receive a 400 Bad Request error. + If you try to create a resource without name or code, you will receive a ``400 Bad Request`` error, that will contain validation errors. Example ^^^^^^^ @@ -213,8 +214,8 @@ Example -H "Accept: application/json" \ -X POST -Example Response -^^^^^^^^^^^^^^^^ +Exemplary Response +^^^^^^^^^^^^^^^^^^ .. code-block:: text @@ -249,7 +250,7 @@ Example Response Collection of Carts ------------------- -To retrieve the paginated list of carts you will need to call the ``/api/v1/carts/`` endpoint with ``GET`` method. +To retrieve a paginated list of carts you will need to call the ``/api/v1/carts/`` endpoint with ``GET`` method. Definition ^^^^^^^^^^ @@ -271,7 +272,7 @@ Definition Example ^^^^^^^ -To see the first page of all carts use the method below. +To see the first page of the paginated carts collection use the below method: .. code-block:: bash @@ -279,8 +280,8 @@ To see the first page of all carts use the method below. -H "Authorization: Bearer SampleToken" \ -H "Accept: application/json" -Example Response -^^^^^^^^^^^^^^^^ +Exemplary Response +^^^^^^^^^^^^^^^^^^ .. code-block:: text @@ -346,7 +347,7 @@ Example Response Getting a Single Cart --------------------- -To retrieve the details of the cart you will need to call the ``/api/v1/carts/{id}`` endpoint with ``GET`` method. +To retrieve details of the cart you will need to call the ``/api/v1/carts/{id}`` endpoint with ``GET`` method. Definition ^^^^^^^^^^ @@ -360,13 +361,13 @@ Definition +===============+================+======================================+ | Authorization | header | Token received during authentication | +---------------+----------------+--------------------------------------+ -| id | url attribute | Id of requested resource | +| id | url attribute | Id of the requested resource | +---------------+----------------+--------------------------------------+ Example ^^^^^^^ -To see the details of the cart with id 21 use the method below. +To see details of the cart with ``id = 21`` use the below method: .. code-block:: bash @@ -376,10 +377,11 @@ To see the details of the cart with id 21 use the method below. .. note:: - The value *21* was taken from create response. Your value can be different. Check list of all carts if you are not sure which id should be used. + The *21* value was taken from the previous create response. Your value can be different. + Check in the list of all carts if you are not sure which id should be used. -Example Response -^^^^^^^^^^^^^^^^ +Exemplary Response +^^^^^^^^^^^^^^^^^^ .. code-block:: text @@ -424,7 +426,7 @@ Example Response Deleting a Cart --------------- -To delete a cart you will need to call the ``/api/v1/carts/{id}`` endpoint with ``DELETE`` method. +To delete a cart you will need to call the ``/api/v1/carts/{id}`` endpoint with the ``DELETE`` method. Definition ^^^^^^^^^^ @@ -433,18 +435,18 @@ Definition DELETE /api/v1/carts/{id} -+---------------+----------------+-------------------------------------------+ -| Parameter | Parameter type | Description | -+===============+================+===========================================+ -| Authorization | header | Token received during authentication | -+---------------+----------------+-------------------------------------------+ -| id | url attribute | Id of requested resource | -+---------------+----------------+-------------------------------------------+ ++---------------+----------------+--------------------------------------+ +| Parameter | Parameter type | Description | ++===============+================+======================================+ +| Authorization | header | Token received during authentication | ++---------------+----------------+--------------------------------------+ +| id | url attribute | Id of the requested resource | ++---------------+----------------+--------------------------------------+ Example ^^^^^^^ -To delete the cart with id 21 use the method below. +To delete the cart with ``id = 21`` use the below method: .. code-block:: bash @@ -455,10 +457,10 @@ To delete the cart with id 21 use the method below. .. note:: - Remember the *21* value from the previous example. Here we are deleting a previously fetch cart, so it is the same id. + Remember the *21* value from the previous example. Here we are deleting a previously fetched cart, so it is the same id. -Example Response -^^^^^^^^^^^^^^^^ +Exemplary Response +^^^^^^^^^^^^^^^^^^ .. code-block:: text @@ -467,7 +469,7 @@ Example Response Creating a Cart Item -------------------- -To add a new cart item to the existing cart you will need to call the ``/api/v1/carts/{cartId}/items/`` endpoint with ``POST`` method. +To add a new cart item to an existing cart you will need to call the ``/api/v1/carts/{cartId}/items/`` endpoint with ``POST`` method. Definition ^^^^^^^^^^ @@ -476,22 +478,23 @@ Definition POST /api/v1/carts/{cartId}/items/ -+---------------+----------------+---------------------------------------------------------------------+ -| Parameter | Parameter type | Description | -+===============+================+=====================================================================+ -| Authorization | header | Token received during authentication | -+---------------+----------------+---------------------------------------------------------------------+ -| cartId | url attribute | Id of requested cart | -+---------------+----------------+---------------------------------------------------------------------+ -| variant | request | Code of item you want to add to cart | -+---------------+----------------+---------------------------------------------------------------------+ -| quantity | request | Amount of variants you want to add to cart (cannot be lower than 1) | -+---------------+----------------+---------------------------------------------------------------------+ ++---------------+----------------+----------------------------------------------------------------+ +| Parameter | Parameter type | Description | ++===============+================+================================================================+ +| Authorization | header | Token received during authentication | ++---------------+----------------+----------------------------------------------------------------+ +| cartId | url attribute | Id of the requested cart | ++---------------+----------------+----------------------------------------------------------------+ +| variant | request | Code of the item you want to add to the cart | ++---------------+----------------+----------------------------------------------------------------+ +| quantity | request | Amount of variants you want to add to the cart (cannot be < 1) | ++---------------+----------------+----------------------------------------------------------------+ Example ^^^^^^^ -To add a new item with one variant with code MEDIUM_MUG_CUP the cart with id 21(assume, that we didn't remove it in a previous example) use the method below. +To add a new item of a variant with code ``MEDIUM_MUG_CUP`` +to the cart with id 21(assuming, that we didn't remove it in the previous example) use the below method: .. code-block:: bash @@ -506,8 +509,8 @@ To add a new item with one variant with code MEDIUM_MUG_CUP the cart with id 21( } ' -Example Response -^^^^^^^^^^^^^^^^ +Exemplary Response +^^^^^^^^^^^^^^^^^^ .. code-block:: text @@ -589,12 +592,13 @@ Example Response .. tip:: - In Sylius the prices are stored as an integers. So in order to present a proper amount to the end user, you should divide price by 100 by default + In Sylius the prices are stored as an integers (``1059`` represents ``10.59$``). + So in order to present a proper amount to the end user, you should divide price by 100 by default. Updating a Cart Item -------------------- -To change the quantity of cart item you will need to call the ``/api/v1/carts/{cartId}/items/{cartItemId}`` endpoint with ``PUT`` method. +To change the quantity of a cart item you will need to call the ``/api/v1/carts/{cartId}/items/{cartItemId}`` endpoint with the ``PUT`` method. Definition ^^^^^^^^^^ @@ -603,24 +607,25 @@ Definition PUT /api/v1/carts/{cartId}/items/{id} -+---------------+----------------+---------------------------------------------------------------------+ -| Parameter | Parameter type | Description | -+===============+================+=====================================================================+ -| Authorization | header | Token received during authentication | -+---------------+----------------+---------------------------------------------------------------------+ -| cartId | url attribute | Id of requested cart | -+---------------+----------------+---------------------------------------------------------------------+ -| cartItemId | url attribute | Id of requested cart item | -+---------------+----------------+---------------------------------------------------------------------+ -| quantity | request | Amount of variants you want to add to cart (cannot be lower than 1) | -+---------------+----------------+---------------------------------------------------------------------+ ++---------------+----------------+-------------------------------------------------------------+ +| Parameter | Parameter type | Description | ++===============+================+=============================================================+ +| Authorization | header | Token received during authentication | ++---------------+----------------+-------------------------------------------------------------+ +| cartId | url attribute | Id of the requested cart | ++---------------+----------------+-------------------------------------------------------------+ +| cartItemId | url attribute | Id of the requested cart item | ++---------------+----------------+-------------------------------------------------------------+ +| quantity | request | Amount of items you want to add to the cart (cannot be < 1) | ++---------------+----------------+-------------------------------------------------------------+ Example ^^^^^^^ -.. code-block:: bash +To change the quantity of the cart item with ``id = 58`` in the cart of ``id = 21`` to 3 use the below method: + -To change a quantity to 3 of the cart item with id 58 of cart 21 use the method below. +.. code-block:: bash $ curl http://demo.sylius.org/api/v1/carts/21/items/58 \ -H "Authorization: Bearer SampleToken" \ @@ -628,22 +633,23 @@ To change a quantity to 3 of the cart item with id 58 of cart 21 use the method -X PUT \ --data '{"quantity": 3}' -.. note:: +.. tip:: - If you are not sure where did the value 58 came from, check the previous response, and look for cart item id + If you are not sure where does the value **58** come from, check the previous response, and look for the cart item id. -.. tip:: - This action can be send with *PATCH* method as well +.. note:: + + This action can be sent with *PATCH* method as well. -Example Response -^^^^^^^^^^^^^^^^ +Exemplary Response +^^^^^^^^^^^^^^^^^^ .. code-block:: text STATUS: 204 No Content -Now we can check what does the cart look like after changing quality +Now we can check how does the cart look like after changing the quantity of a cart item. .. code-block:: bash @@ -651,8 +657,8 @@ Now we can check what does the cart look like after changing quality -H "Authorization: Bearer SampleToken" \ -H "Accept: application/json" -Example Response -^^^^^^^^^^^^^^^^ +Exemplary Response +^^^^^^^^^^^^^^^^^^ .. code-block:: text @@ -765,19 +771,19 @@ Example Response .. tip:: - In this response you can see that promotion and shipping have been taken into account to calculate the appropriate price + In this response you can see that promotion and shipping have been taken into account to calculate the appropriate price. Deleting a Cart Item -------------------- -To delete the cart item you will need to call the ``/api/v1/carts/{cartId}/items/{cartItemId}`` endpoint with ``DELETE`` method. +To delete a cart item from a cart you will need to call the ``/api/v1/carts/{cartId}/items/{cartItemId}`` endpoint with the ``DELETE`` method. Definition ^^^^^^^^^^ -.. code-block:: text +To delete the cart item with ``id = 58`` from the cart with ``id = 21`` use the below method: -To delete the cart item with id 58 of cart 21 use the method below. +.. code-block:: text DELETE /api/v1/carts/{cartId}/items/{id} @@ -786,9 +792,9 @@ To delete the cart item with id 58 of cart 21 use the method below. +===============+================+======================================+ | Authorization | header | Token received during authentication | +---------------+----------------+--------------------------------------+ -| cartId | url attribute | Id of requested cart | +| cartId | url attribute | Id of the requested cart | +---------------+----------------+--------------------------------------+ -| cartItemId | url attribute | Id of requested cart item | +| cartItemId | url attribute | Id of the requested cart item | +---------------+----------------+--------------------------------------+ Example @@ -801,8 +807,8 @@ Example -H "Accept: application/json" \ -X DELETE -Example Response -^^^^^^^^^^^^^^^^ +Exemplary Response +^^^^^^^^^^^^^^^^^^ .. code-block:: text diff --git a/docs/api/orders.rst b/docs/api/orders.rst index 556c4a87941..19a6378df7e 100644 --- a/docs/api/orders.rst +++ b/docs/api/orders.rst @@ -3,18 +3,18 @@ Orders API Sylius orders API endpoint is `/api/v1/orders`. -If you request an order, you will receive an object with the following fields: +If you request an order via API, you will receive an object with the following fields: +-----------------------+--------------------------------------------------------------------+ | Field | Description | +=======================+====================================================================+ -| id | Id of an order | +| id | Id of the order | +-----------------------+--------------------------------------------------------------------+ -| items | List of items related to order | +| items | List of items related to the order | +-----------------------+--------------------------------------------------------------------+ | items_total | Sum of all items prices | +-----------------------+--------------------------------------------------------------------+ -| adjustments | List of adjustments related to order | +| adjustments | List of adjustments related to the order | +-----------------------+--------------------------------------------------------------------+ | adjustments_total | Sum of all order adjustments | +-----------------------+--------------------------------------------------------------------+ @@ -24,15 +24,15 @@ If you request an order, you will receive an object with the following fields: +-----------------------+--------------------------------------------------------------------+ | channel | :doc:`Default channel serialization ` | +-----------------------+--------------------------------------------------------------------+ -| currency_code | Currency of a order | +| currency_code | Currency of the order | +-----------------------+--------------------------------------------------------------------+ -| checkout_state | :doc:`State of checkout process ` | +| checkout_state | :doc:`State of the checkout process ` | +-----------------------+--------------------------------------------------------------------+ -| state | :doc:`One of available order states ` | +| state | :doc:`State of the order ` | +-----------------------+--------------------------------------------------------------------+ | checkout_completed_at | Date when the checkout has been completed | +-----------------------+--------------------------------------------------------------------+ -| number | Serial number of an order | +| number | Serial number of the order | +-----------------------+--------------------------------------------------------------------+ | shipping_address | Detailed address serialization | +-----------------------+--------------------------------------------------------------------+ @@ -74,7 +74,8 @@ Example .. note:: - The value *21* was taken from previous responses, where we managed a cart. Your value can be different. If you need more information about cart api please, check :doc:`this doc ` + The value *21* was taken from previous responses, where we managed the cart. + Your value can be different. If you need more information about Cart API please, check :doc:`this article `. Example Response ~~~~~~~~~~~~~~~~ From 51ea70294fcb130dabf2f0e425438ddaffc046e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz?= Date: Tue, 7 Feb 2017 10:05:10 +0100 Subject: [PATCH 10/11] [API] Add locale to cart serialization + minor fixes --- docs/api/carts.rst | 22 +++++++++---------- docs/api/map.rst.inc | 2 +- .../ApiBundle/Resources/config/grids/cart.yml | 2 -- .../config/serializer/Model.Order.yml | 4 ++++ ..._to_cart_hard_available_item_response.json | 1 + .../Expected/cart/add_to_cart_response.json | 1 + ...to_cart_with_bigger_quantity_response.json | 1 + .../cart/increase_quantity_response.json | 1 + .../Expected/cart/index_response.json | 1 + .../Expected/cart/show_response.json | 1 + .../checkout/addressed_order_response.json | 1 + .../checkout/completed_order_response.json | 1 + .../payment_selected_order_response.json | 1 + .../shipping_selected_order_response.json | 1 + .../Expected/order/cart_show_response.json | 1 + .../Expected/order/order_show_response.json | 1 + 16 files changed, 28 insertions(+), 14 deletions(-) diff --git a/docs/api/carts.rst b/docs/api/carts.rst index 91288d6b6cf..ea663547661 100644 --- a/docs/api/carts.rst +++ b/docs/api/carts.rst @@ -607,17 +607,17 @@ Definition PUT /api/v1/carts/{cartId}/items/{id} -+---------------+----------------+-------------------------------------------------------------+ -| Parameter | Parameter type | Description | -+===============+================+=============================================================+ -| Authorization | header | Token received during authentication | -+---------------+----------------+-------------------------------------------------------------+ -| cartId | url attribute | Id of the requested cart | -+---------------+----------------+-------------------------------------------------------------+ -| cartItemId | url attribute | Id of the requested cart item | -+---------------+----------------+-------------------------------------------------------------+ -| quantity | request | Amount of items you want to add to the cart (cannot be < 1) | -+---------------+----------------+-------------------------------------------------------------+ ++---------------+----------------+--------------------------------------------------------------+ +| Parameter | Parameter type | Description | ++===============+================+==============================================================+ +| Authorization | header | Token received during authentication | ++---------------+----------------+--------------------------------------------------------------+ +| cartId | url attribute | Id of the requested cart | ++---------------+----------------+--------------------------------------------------------------+ +| cartItemId | url attribute | Id of the requested cart item | ++---------------+----------------+--------------------------------------------------------------+ +| quantity | request | Amount of items you want to have in the cart (cannot be < 1) | ++---------------+----------------+--------------------------------------------------------------+ Example ^^^^^^^ diff --git a/docs/api/map.rst.inc b/docs/api/map.rst.inc index 329b154c9a5..ac310e612b3 100644 --- a/docs/api/map.rst.inc +++ b/docs/api/map.rst.inc @@ -1,8 +1,8 @@ * :doc:`/api/introduction` * :doc:`/api/authorization` * :doc:`/api/channels` -* :doc:`/api/orders` * :doc:`/api/carts` +* :doc:`/api/orders` * :doc:`/api/checkouts` * :doc:`/api/products` * :doc:`/api/product_variants` diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/grids/cart.yml b/src/Sylius/Bundle/ApiBundle/Resources/config/grids/cart.yml index 3c8f81dc1fe..b6d730e2aac 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/grids/cart.yml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/grids/cart.yml @@ -26,8 +26,6 @@ sylius_grid: type: string sortable: customer.email path: customer.email - options: - template: "@SyliusAdmin/Order/Grid/Field/customer.html.twig" total: type: integer sortable: total diff --git a/src/Sylius/Bundle/CoreBundle/Resources/config/serializer/Model.Order.yml b/src/Sylius/Bundle/CoreBundle/Resources/config/serializer/Model.Order.yml index ad616b50571..86c4a3d3728 100644 --- a/src/Sylius/Bundle/CoreBundle/Resources/config/serializer/Model.Order.yml +++ b/src/Sylius/Bundle/CoreBundle/Resources/config/serializer/Model.Order.yml @@ -10,6 +10,10 @@ Sylius\Component\Core\Model\Order: expose: true type: string groups: [Default, Detailed, DetailedCart] + localeCode: + expose: true + type: string + groups: [Default, Detailed, DetailedCart] shippingAddress: expose: true groups: [Detailed] diff --git a/tests/Responses/Expected/cart/add_to_cart_hard_available_item_response.json b/tests/Responses/Expected/cart/add_to_cart_hard_available_item_response.json index 711a14ff45f..a01a6b1afda 100644 --- a/tests/Responses/Expected/cart/add_to_cart_hard_available_item_response.json +++ b/tests/Responses/Expected/cart/add_to_cart_hard_available_item_response.json @@ -27,6 +27,7 @@ } }, "currency_code": "USD", + "locale_code": "en_US", "checkout_state": "cart" }, "quantity": 1, diff --git a/tests/Responses/Expected/cart/add_to_cart_response.json b/tests/Responses/Expected/cart/add_to_cart_response.json index 711a14ff45f..a01a6b1afda 100644 --- a/tests/Responses/Expected/cart/add_to_cart_response.json +++ b/tests/Responses/Expected/cart/add_to_cart_response.json @@ -27,6 +27,7 @@ } }, "currency_code": "USD", + "locale_code": "en_US", "checkout_state": "cart" }, "quantity": 1, diff --git a/tests/Responses/Expected/cart/add_to_cart_with_bigger_quantity_response.json b/tests/Responses/Expected/cart/add_to_cart_with_bigger_quantity_response.json index 06b2d871bce..1ff06ecad1b 100644 --- a/tests/Responses/Expected/cart/add_to_cart_with_bigger_quantity_response.json +++ b/tests/Responses/Expected/cart/add_to_cart_with_bigger_quantity_response.json @@ -27,6 +27,7 @@ } }, "currency_code": "USD", + "locale_code": "en_US", "checkout_state": "cart" }, "quantity": 3, diff --git a/tests/Responses/Expected/cart/increase_quantity_response.json b/tests/Responses/Expected/cart/increase_quantity_response.json index db8034ad3c7..97eb610c112 100644 --- a/tests/Responses/Expected/cart/increase_quantity_response.json +++ b/tests/Responses/Expected/cart/increase_quantity_response.json @@ -86,5 +86,6 @@ } }, "currency_code": "USD", + "locale_code": "en_US", "checkout_state": "cart" } diff --git a/tests/Responses/Expected/cart/index_response.json b/tests/Responses/Expected/cart/index_response.json index 4f44aad8372..20d2db5d938 100644 --- a/tests/Responses/Expected/cart/index_response.json +++ b/tests/Responses/Expected/cart/index_response.json @@ -43,6 +43,7 @@ } }, "currency_code": "USD", + "locale_code": "en_US", "checkout_state": "cart" } ] diff --git a/tests/Responses/Expected/cart/show_response.json b/tests/Responses/Expected/cart/show_response.json index 2e6ddd4f17f..840fa23e925 100644 --- a/tests/Responses/Expected/cart/show_response.json +++ b/tests/Responses/Expected/cart/show_response.json @@ -25,5 +25,6 @@ } }, "currency_code": "USD", + "locale_code": "en_US", "checkout_state": "cart" } diff --git a/tests/Responses/Expected/checkout/addressed_order_response.json b/tests/Responses/Expected/checkout/addressed_order_response.json index 87a1fba0e3c..30d8258bacc 100644 --- a/tests/Responses/Expected/checkout/addressed_order_response.json +++ b/tests/Responses/Expected/checkout/addressed_order_response.json @@ -59,5 +59,6 @@ "payments": @array@, "shipments": @array@, "currency_code": "EUR", + "locale_code": "en_US", "checkout_state": "addressed" } diff --git a/tests/Responses/Expected/checkout/completed_order_response.json b/tests/Responses/Expected/checkout/completed_order_response.json index fd9e54a08f6..6b50dd2c80b 100644 --- a/tests/Responses/Expected/checkout/completed_order_response.json +++ b/tests/Responses/Expected/checkout/completed_order_response.json @@ -41,5 +41,6 @@ "payments": @array@, "shipments": @array@, "currency_code": "EUR", + "locale_code": "en_US", "checkout_state": "completed" } diff --git a/tests/Responses/Expected/checkout/payment_selected_order_response.json b/tests/Responses/Expected/checkout/payment_selected_order_response.json index 29275adde78..9140ee918ec 100644 --- a/tests/Responses/Expected/checkout/payment_selected_order_response.json +++ b/tests/Responses/Expected/checkout/payment_selected_order_response.json @@ -69,5 +69,6 @@ ], "shipments": @array@, "currency_code": "EUR", + "locale_code": "en_US", "checkout_state": "payment_selected" } diff --git a/tests/Responses/Expected/checkout/shipping_selected_order_response.json b/tests/Responses/Expected/checkout/shipping_selected_order_response.json index 8679daa4dd1..f236cb610af 100644 --- a/tests/Responses/Expected/checkout/shipping_selected_order_response.json +++ b/tests/Responses/Expected/checkout/shipping_selected_order_response.json @@ -77,5 +77,6 @@ } ], "currency_code": "EUR", + "locale_code": "en_US", "checkout_state": "shipping_selected" } diff --git a/tests/Responses/Expected/order/cart_show_response.json b/tests/Responses/Expected/order/cart_show_response.json index 6484da356d1..5faf4875c2e 100644 --- a/tests/Responses/Expected/order/cart_show_response.json +++ b/tests/Responses/Expected/order/cart_show_response.json @@ -38,6 +38,7 @@ } }, "currency_code": "USD", + "locale_code": "en_US", "payments": [], "shipments": [], "checkout_state": "cart" diff --git a/tests/Responses/Expected/order/order_show_response.json b/tests/Responses/Expected/order/order_show_response.json index e82d9b27d92..a6b518c65a5 100644 --- a/tests/Responses/Expected/order/order_show_response.json +++ b/tests/Responses/Expected/order/order_show_response.json @@ -40,5 +40,6 @@ "payments": [], "shipments": [], "currency_code": "USD", + "locale_code": "en_US", "checkout_state": "cart" } From 4e012cd0af7d73af2835fbf167a7f26ce5ed328b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz?= Date: Wed, 8 Feb 2017 11:55:24 +0100 Subject: [PATCH 11/11] [Documentation] [API] Cleanup and refactoring --- docs/api/carts.rst | 35 ++++---- docs/api/orders.rst | 6 +- .../Resources/config/routing/main.yml | 16 ++-- tests/Controller/CartApiTest.php | 89 ++++++++++++------- ...te_cart_item_validation_fail_response.json | 4 +- 5 files changed, 87 insertions(+), 63 deletions(-) diff --git a/docs/api/carts.rst b/docs/api/carts.rst index ea663547661..cd31d0adac1 100644 --- a/docs/api/carts.rst +++ b/docs/api/carts.rst @@ -28,11 +28,11 @@ If you request a cart via API, you will receive an object with the following fie +-------------------+-------------------------------------------------------------------+ | adjustments | List of adjustments related to the cart | +-------------------+-------------------------------------------------------------------+ -| adjustments_total | Sum of all order adjustments values | +| adjustments_total | Sum of all cart adjustments values | +-------------------+-------------------------------------------------------------------+ | total | Sum of items total and adjustments total | +-------------------+-------------------------------------------------------------------+ -| customer | :doc:`Customer detailed serialization ` for order | +| customer | :doc:`Customer detailed serialization ` for cart | +-------------------+-------------------------------------------------------------------+ | channel | :doc:`Default channel serialization ` | +-------------------+-------------------------------------------------------------------+ @@ -108,7 +108,7 @@ And each Adjustment will be build as follows: Creating a Cart --------------- -To create a new cart you will need to call the ``/api/v1/carts/`` endpoint with ``POST`` method. +To create a new cart you will need to call the ``/api/v1/carts/`` endpoint with the ``POST`` method. Definition ^^^^^^^^^^ @@ -137,7 +137,7 @@ To create a new cart for the ``shop@example.com`` user in the ``US_WEB`` channel .. warning:: Remember, that it doesn't replicate the environment of shop usage. It is more like an admin part of cart creation, which will allow you to manage - cart from the admin perspective. ShopAPI is still an experimental concept. + the cart from the admin perspective. ShopAPI is still an experimental concept. .. code-block:: bash @@ -193,6 +193,7 @@ Exemplary Response } }, "currency_code":"USD", + "locale_code": "en_US", "checkout_state":"cart" } @@ -202,7 +203,7 @@ Exemplary Response .. warning:: - If you try to create a resource without name or code, you will receive a ``400 Bad Request`` error, that will contain validation errors. + If you try to create a resource without localeCode, channel or customer, you will receive a ``400 Bad Request`` error, that will contain validation errors. Example ^^^^^^^ @@ -250,7 +251,7 @@ Exemplary Response Collection of Carts ------------------- -To retrieve a paginated list of carts you will need to call the ``/api/v1/carts/`` endpoint with ``GET`` method. +To retrieve a paginated list of carts you will need to call the ``/api/v1/carts/`` endpoint with the ``GET`` method. Definition ^^^^^^^^^^ @@ -266,7 +267,7 @@ Definition +---------------+----------------+------------------------------------------------------------------+ | page | query | *(optional)* Number of the page, by default = 1 | +---------------+----------------+------------------------------------------------------------------+ -| limit | query | *(optional)* Number of carts displayed per page, by default = 10 | +| paginate | query | *(optional)* Number of carts displayed per page, by default = 10 | +---------------+----------------+------------------------------------------------------------------+ Example @@ -338,6 +339,7 @@ Exemplary Response } }, "currency_code":"USD", + "locale_code": "en_US", "checkout_state":"cart" } ] @@ -361,7 +363,7 @@ Definition +===============+================+======================================+ | Authorization | header | Token received during authentication | +---------------+----------------+--------------------------------------+ -| id | url attribute | Id of the requested resource | +| id | url attribute | Id of the requested cart | +---------------+----------------+--------------------------------------+ Example @@ -420,6 +422,7 @@ Exemplary Response } }, "currency_code":"USD", + "locale_code": "en_US", "checkout_state":"cart" } @@ -440,7 +443,7 @@ Definition +===============+================+======================================+ | Authorization | header | Token received during authentication | +---------------+----------------+--------------------------------------+ -| id | url attribute | Id of the requested resource | +| id | url attribute | Id of the requested cart | +---------------+----------------+--------------------------------------+ Example @@ -457,7 +460,7 @@ To delete the cart with ``id = 21`` use the below method: .. note:: - Remember the *21* value from the previous example. Here we are deleting a previously fetched cart, so it is the same id. + Remember the *21* value comes from the previous example. Here we are deleting a previously fetched cart, so it is the same id. Exemplary Response ^^^^^^^^^^^^^^^^^^ @@ -494,7 +497,7 @@ Example ^^^^^^^ To add a new item of a variant with code ``MEDIUM_MUG_CUP`` -to the cart with id 21(assuming, that we didn't remove it in the previous example) use the below method: +to the cart with id = 21 (assuming, that we didn't remove it in the previous example) use the below method: .. code-block:: bash @@ -527,9 +530,7 @@ Exemplary Response ], "items_total":175, "adjustments":[ - { - } ], "adjustments_total":7515, "total":7690, @@ -558,6 +559,7 @@ Exemplary Response } }, "currency_code":"USD", + "locale_code": "en_US", "checkout_state":"cart" }, "quantity":1, @@ -598,7 +600,7 @@ Exemplary Response Updating a Cart Item -------------------- -To change the quantity of a cart item you will need to call the ``/api/v1/carts/{cartId}/items/{cartItemId}`` endpoint with the ``PUT`` method. +To change the quantity of a cart item you will need to call the ``/api/v1/carts/{cartId}/items/{cartItemId}`` endpoint with the ``PUT`` or ``PATCH`` method. Definition ^^^^^^^^^^ @@ -638,10 +640,6 @@ To change the quantity of the cart item with ``id = 58`` in the cart of ``id = 2 If you are not sure where does the value **58** come from, check the previous response, and look for the cart item id. -.. note:: - - This action can be sent with *PATCH* method as well. - Exemplary Response ^^^^^^^^^^^^^^^^^^ @@ -766,6 +764,7 @@ Exemplary Response } }, "currency_code":"USD", + "locale_code": "en_US", "checkout_state":"cart" } diff --git a/docs/api/orders.rst b/docs/api/orders.rst index 19a6378df7e..4feb710615a 100644 --- a/docs/api/orders.rst +++ b/docs/api/orders.rst @@ -38,9 +38,9 @@ If you request an order via API, you will receive an object with the following f +-----------------------+--------------------------------------------------------------------+ | billing_address | Detailed address serialization | +-----------------------+--------------------------------------------------------------------+ -| shipments | Detailed shipment serialization | +| shipments | Detailed serialization of all related shipments | +-----------------------+--------------------------------------------------------------------+ -| payments | Detailed payment serialization | +| payments | Detailed serialization of all related payments | +-----------------------+--------------------------------------------------------------------+ Getting a single order @@ -60,7 +60,7 @@ Definition +===============+================+======================================+ | Authorization | header | Token received during authentication | +---------------+----------------+--------------------------------------+ -| id | url attribute | Id of requested resource | +| id | url attribute | Id of requested order | +---------------+----------------+--------------------------------------+ Example diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/routing/main.yml b/src/Sylius/Bundle/ApiBundle/Resources/config/routing/main.yml index 029d5159c69..e499576c875 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/routing/main.yml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/routing/main.yml @@ -24,6 +24,14 @@ sylius_api_cart: resource: "@SyliusApiBundle/Resources/config/routing/cart.yml" prefix: /carts +sylius_api_adjustment: + resource: "@SyliusApiBundle/Resources/config/routing/adjustment.yml" + prefix: /orders/{orderId}/adjustments + +sylius_api_cart_item: + resource: "@SyliusApiBundle/Resources/config/routing/cart_item.yml" + prefix: /carts/{cartId}/items + sylius_api_order: resource: | alias: sylius.order @@ -33,14 +41,6 @@ sylius_api_order: serialization_version: $version type: sylius.resource_api -sylius_api_cart_item: - resource: "@SyliusApiBundle/Resources/config/routing/cart_item.yml" - prefix: /carts/{cartId}/items - -sylius_api_adjustment: - resource: "@SyliusApiBundle/Resources/config/routing/adjustment.yml" - prefix: /orders/{orderId}/adjustments - sylius_api_checkout: resource: "@SyliusApiBundle/Resources/config/routing/checkout.yml" prefix: /checkouts diff --git a/tests/Controller/CartApiTest.php b/tests/Controller/CartApiTest.php index 6fb7753fef5..d12eae44824 100644 --- a/tests/Controller/CartApiTest.php +++ b/tests/Controller/CartApiTest.php @@ -71,7 +71,7 @@ public function it_allows_to_get_cart() /** @var OrderInterface $cart */ $cart = $cartData['order_001']; - $this->client->request('GET', '/api/v1/carts/'.$cart->getId(), [], [], static::$authorizedHeaderWithAccept); + $this->client->request('GET', $this->getCartUrl($cart), [], [], static::$authorizedHeaderWithAccept); $response = $this->client->getResponse(); $this->assertResponse($response, 'cart/show_response', Response::HTTP_OK); @@ -87,7 +87,7 @@ public function it_does_not_show_orders_in_state_other_than_cart() /** @var OrderInterface $cart */ $order = $orderData['order_001']; - $this->client->request('GET', '/api/v1/carts/'.$order->getId(), [], [], static::$authorizedHeaderWithAccept); + $this->client->request('GET', $this->getCartUrl($order), [], [], static::$authorizedHeaderWithAccept); $response = $this->client->getResponse(); $this->assertResponse($response, 'error/not_found_response', Response::HTTP_NOT_FOUND); @@ -185,7 +185,12 @@ public function it_does_not_allow_to_list_carts_in_state_different_than_cart() */ public function it_denies_carts_deletion_for_non_authenticated_user() { - $this->client->request('DELETE', '/api/v1/carts/-1'); + $this->loadFixturesFromFile('authentication/api_administrator.yml'); + $carts = $this->loadFixturesFromFile('resources/cart.yml'); + /** @var OrderInterface $cart */ + $cart = $carts['order_001']; + + $this->client->request('DELETE', $this->getCartUrl($cart)); $response = $this->client->getResponse(); $this->assertResponse($response, 'authentication/access_denied_response', Response::HTTP_UNAUTHORIZED); @@ -212,16 +217,15 @@ public function it_allows_to_delete_cart() $this->loadFixturesFromFile('authentication/api_administrator.yml'); $carts = $this->loadFixturesFromFile('resources/cart.yml'); - /** @var OrderItemInterface $cart */ + /** @var OrderInterface $cart */ $cart = $carts['order_001']; - $url = '/api/v1/carts/'.$cart->getId(); - $this->client->request('DELETE', $url, [], [], static::$authorizedHeaderWithContentType); + $this->client->request('DELETE', $this->getCartUrl($cart), [], [], static::$authorizedHeaderWithContentType); $response = $this->client->getResponse(); $this->assertResponseCode($response, Response::HTTP_NO_CONTENT); - $this->client->request('GET', $url, [], [], static::$authorizedHeaderWithContentType); + $this->client->request('GET', $this->getCartUrl($cart), [], [], static::$authorizedHeaderWithContentType); $response = $this->client->getResponse(); $this->assertResponse($response, 'error/not_found_response', Response::HTTP_NOT_FOUND); @@ -233,12 +237,12 @@ public function it_allows_to_delete_cart() public function it_does_not_allow_to_delete_orders_in_state_different_than_cart() { $this->loadFixturesFromFile('authentication/api_administrator.yml'); - $carts = $this->loadFixturesFromFile('resources/order.yml'); + $orders = $this->loadFixturesFromFile('resources/order.yml'); - /** @var OrderItemInterface $cart */ - $cart = $carts['order_001']; + /** @var OrderItemInterface $order */ + $order = $orders['order_001']; - $this->client->request('DELETE', '/api/v1/carts/'.$cart->getId(), [], [], static::$authorizedHeaderWithContentType); + $this->client->request('DELETE', $this->getCartUrl($order), [], [], static::$authorizedHeaderWithContentType); $response = $this->client->getResponse(); $this->assertResponse($response, 'error/not_found_response', Response::HTTP_NOT_FOUND); @@ -265,9 +269,8 @@ public function it_does_not_allow_to_add_item_to_cart_without_providing_required /** @var OrderInterface $cart */ $cart = $carts['order_001']; - $url = sprintf('/api/v1/carts/%s/items/', $cart->getId()); - $this->client->request('POST', $url, [], [], static::$authorizedHeaderWithContentType); + $this->client->request('POST', $this->getCartItemListUrl($cart), [], [], static::$authorizedHeaderWithContentType); $response = $this->client->getResponse(); $this->assertResponse($response, 'cart/add_to_cart_validation_fail_response', Response::HTTP_BAD_REQUEST); @@ -283,7 +286,6 @@ public function it_adds_an_item_to_the_cart() /** @var OrderInterface $cart */ $cart = $carts['order_001']; - $url = sprintf('/api/v1/carts/%s/items/', $cart->getId()); $data = <<client->request('POST', $url, [], [], static::$authorizedHeaderWithContentType, $data); + $this->client->request('POST', $this->getCartItemListUrl($cart), [], [], static::$authorizedHeaderWithContentType, $data); $response = $this->client->getResponse(); $this->assertResponse($response, 'cart/add_to_cart_response', Response::HTTP_CREATED); @@ -308,7 +310,6 @@ public function it_does_not_allow_to_add_item_with_negative_quantity() /** @var OrderInterface $cart */ $cart = $carts['order_001']; - $url = sprintf('/api/v1/carts/%s/items/', $cart->getId()); $data = <<client->request('POST', $url, [], [], static::$authorizedHeaderWithContentType, $data); + $this->client->request('POST', $this->getCartItemListUrl($cart), [], [], static::$authorizedHeaderWithContentType, $data); $response = $this->client->getResponse(); $this->assertResponse($response, 'cart/add_to_cart_quantity_validation_fail_response', Response::HTTP_BAD_REQUEST); @@ -333,7 +334,6 @@ public function it_adds_an_item_to_the_cart_with_quantity_bigger_than_one() /** @var OrderInterface $cart */ $cart = $carts['order_001']; - $url = sprintf('/api/v1/carts/%s/items/', $cart->getId()); $data = <<client->request('POST', $url, [], [], static::$authorizedHeaderWithContentType, $data); + $this->client->request('POST', $this->getCartItemListUrl($cart), [], [], static::$authorizedHeaderWithContentType, $data); $response = $this->client->getResponse(); $this->assertResponse($response, 'cart/add_to_cart_with_bigger_quantity_response', Response::HTTP_CREATED); @@ -358,7 +358,6 @@ public function it_adds_an_item_with_tracked_variant_to_the_cart() /** @var OrderInterface $cart */ $cart = $carts['order_001']; - $url = sprintf('/api/v1/carts/%s/items/', $cart->getId()); $data = <<client->request('POST', $url, [], [], static::$authorizedHeaderWithContentType, $data); + $this->client->request('POST', $this->getCartItemListUrl($cart), [], [], static::$authorizedHeaderWithContentType, $data); $response = $this->client->getResponse(); $this->assertResponse($response, 'cart/add_to_cart_hard_available_item_response', Response::HTTP_CREATED); @@ -383,7 +382,6 @@ public function it_checks_if_requested_variant_is_available() /** @var OrderInterface $cart */ $cart = $carts['order_001']; - $url = sprintf('/api/v1/carts/%s/items/', $cart->getId()); $data = <<client->request('POST', $url, [], [], static::$authorizedHeaderWithContentType, $data); + $this->client->request('POST', $this->getCartItemListUrl($cart), [], [], static::$authorizedHeaderWithContentType, $data); $response = $this->client->getResponse(); $this->assertResponse($response, 'cart/add_to_cart_hard_available_item_validation_error_response', Response::HTTP_BAD_REQUEST); @@ -421,7 +419,6 @@ public function it_does_not_allow_to_update_items_variant() $cart = $fulfilledCart['fulfilled_cart']; /** @var OrderItemInterface $cartItem */ $cartItem = $fulfilledCart['sw_mug_item']; - $url = sprintf('/api/v1/carts/%s/items/%s', $cart->getId(), $cartItem->getId()); $data = <<client->request('PUT', $url, [], [], static::$authorizedHeaderWithContentType, $data); + $this->client->request('PUT', $this->getCartItemUrl($cart, $cartItem), [], [], static::$authorizedHeaderWithContentType, $data); $response = $this->client->getResponse(); $this->assertResponse($response, 'cart/update_cart_item_validation_fail_response', Response::HTTP_BAD_REQUEST); @@ -449,8 +446,6 @@ public function it_updates_item_quantity() $cart = $fulfilledCart['fulfilled_cart']; /** @var OrderItemInterface $cartItem */ $cartItem = $fulfilledCart['sw_mug_item']; - $updateUrl = sprintf('/api/v1/carts/%s/items/%s', $cart->getId(), $cartItem->getId()); - $orderUrl = sprintf('/api/v1/carts/%s', $cart->getId()); $data = <<client->request('PUT', $updateUrl, [], [], static::$authorizedHeaderWithContentType, $data); + $this->client->request('PUT', $this->getCartItemUrl($cart, $cartItem), [], [], static::$authorizedHeaderWithContentType, $data); $response = $this->client->getResponse(); $this->assertResponseCode($response, Response::HTTP_NO_CONTENT); - $this->client->request('GET', $orderUrl, [], [], static::$authorizedHeaderWithAccept); + $this->client->request('GET', $this->getCartUrl($cart), [], [], static::$authorizedHeaderWithAccept); $response = $this->client->getResponse(); $this->assertResponse($response, 'cart/increase_quantity_response', Response::HTTP_OK); @@ -481,7 +476,6 @@ public function it_checks_if_requested_variant_is_available_during_quantity_upda $cart = $fulfilledCart['fulfilled_cart']; /** @var OrderItemInterface $cartItem */ $cartItem = $fulfilledCart['hard_available_mug_item']; - $url = sprintf('/api/v1/carts/%s/items/%s', $cart->getId(), $cartItem->getId()); $data = <<client->request('PUT', $url, [], [], static::$authorizedHeaderWithContentType, $data); + $this->client->request('PUT', $this->getCartItemUrl($cart, $cartItem), [], [], static::$authorizedHeaderWithContentType, $data); $response = $this->client->getResponse(); $this->assertResponse($response, 'cart/update_hard_available_cart_item_validation_error_response', Response::HTTP_BAD_REQUEST); @@ -536,11 +530,40 @@ public function it_allows_to_delete_cart_item() $cart = $fulfilledCart['fulfilled_cart']; /** @var OrderItemInterface $cartItem */ $cartItem = $fulfilledCart['hard_available_mug_item']; - $url = sprintf('/api/v1/carts/%s/items/%s', $cart->getId(), $cartItem->getId()); - $this->client->request('DELETE', $url, [], [], static::$authorizedHeaderWithContentType); + $this->client->request('DELETE', $this->getCartItemUrl($cart, $cartItem), [], [], static::$authorizedHeaderWithContentType); $response = $this->client->getResponse(); $this->assertResponseCode($response, Response::HTTP_NO_CONTENT); } + + /** + * @param OrderInterface $cart + * + * @return string + */ + private function getCartUrl(OrderInterface $cart) + { + return '/api/v1/carts/' . $cart->getId(); + } + + /** + * @param OrderInterface $cart + * + * @return string + */ + private function getCartItemListUrl(OrderInterface $cart) + { + return sprintf('/api/v1/carts/%s/items/', $cart->getId()); + } + + /** + * @param OrderInterface $cart + * @param OrderItemInterface $cartItem + * @return string + */ + private function getCartItemUrl(OrderInterface $cart, OrderItemInterface $cartItem) + { + return sprintf('/api/v1/carts/%s/items/%s', $cart->getId(), $cartItem->getId()); + } } diff --git a/tests/Responses/Expected/cart/update_cart_item_validation_fail_response.json b/tests/Responses/Expected/cart/update_cart_item_validation_fail_response.json index 169aac9d672..276a084965e 100644 --- a/tests/Responses/Expected/cart/update_cart_item_validation_fail_response.json +++ b/tests/Responses/Expected/cart/update_cart_item_validation_fail_response.json @@ -6,7 +6,9 @@ "This form should not contain extra fields." ], "children": { - "quantity": {} + "quantity": { + + } } } }