From 5e40d5b2ead96ba5902d38e418d27f4be02711c1 Mon Sep 17 00:00:00 2001 From: Paula Date: Mon, 8 Nov 2021 14:55:15 +0100 Subject: [PATCH] Get product by slug api endpoint --- ...cessing_product_page_via_permalink.feature | 9 ++- .../Behat/Context/Api/Shop/ProductContext.php | 20 ++++++ .../Controller/GetProductBySlugAction.php | 69 +++++++++++++++++++ .../config/api_resources/Product.xml | 23 +++++++ .../ApiBundle/Resources/config/services.xml | 8 +++ .../Resources/config/services/swagger.xml | 9 +++ .../ProductSlugDocumentationNormalizer.php | 49 +++++++++++++ tests/Api/Shop/ProductsTest.php | 32 +++++++++ 8 files changed, 217 insertions(+), 2 deletions(-) create mode 100644 src/Sylius/Bundle/ApiBundle/Controller/GetProductBySlugAction.php create mode 100644 src/Sylius/Bundle/ApiBundle/Swagger/ProductSlugDocumentationNormalizer.php create mode 100644 tests/Api/Shop/ProductsTest.php diff --git a/features/product/viewing_products/accessing_product_page_via_permalink.feature b/features/product/viewing_products/accessing_product_page_via_permalink.feature index eb79f47283d..e3b75c61d63 100644 --- a/features/product/viewing_products/accessing_product_page_via_permalink.feature +++ b/features/product/viewing_products/accessing_product_page_via_permalink.feature @@ -6,10 +6,15 @@ Feature: Viewing a product details using permalink Background: Given the store operates on a single channel in "United States" + And the store has a product "T-shirt banana" - @ui + @ui @no-api Scenario: Accessing a detailed product page using permalink - Given the store has a product "T-shirt banana" When I open page "en_US/products/t-shirt-banana" Then I should be on "T-shirt banana" product detailed page And I should see the product name "T-shirt banana" + + @api + Scenario: Viewing a detailed page with product's slug + When I view product "T-shirt banana" using slug + Then I should be redirected to "T-shirt banana" product diff --git a/src/Sylius/Behat/Context/Api/Shop/ProductContext.php b/src/Sylius/Behat/Context/Api/Shop/ProductContext.php index 2ff710bb97e..6568201faa9 100644 --- a/src/Sylius/Behat/Context/Api/Shop/ProductContext.php +++ b/src/Sylius/Behat/Context/Api/Shop/ProductContext.php @@ -76,6 +76,26 @@ public function iOpenProductPage(ProductInterface $product): void $this->sharedStorage->set('product_variant', $productVariant); } + /** + * @When I view product :product using slug + */ + public function iViewProductUsingSlug(ProductInterface $product): void + { + $this->client->showByIri('/api/v2/shop/products-by-slug/'.$product->getSlug()); + + $this->sharedStorage->set('product', $product); + } + + /** + * @Then I should be redirected to :product product + */ + public function iShouldBeRedirectedToProduct(ProductInterface $product): void + { + $response = $this->client->getLastResponse(); + + Assert::eq($response->headers->get('Location'), '/api/v2/shop/products/'.$product->getCode()); + } + /** * @When I browse products from taxon :taxon */ diff --git a/src/Sylius/Bundle/ApiBundle/Controller/GetProductBySlugAction.php b/src/Sylius/Bundle/ApiBundle/Controller/GetProductBySlugAction.php new file mode 100644 index 00000000000..54e0207f6b5 --- /dev/null +++ b/src/Sylius/Bundle/ApiBundle/Controller/GetProductBySlugAction.php @@ -0,0 +1,69 @@ +channelContext = $channelContext; + $this->localeContext = $localeContext; + $this->productRepository = $productRepository; + $this->iriConverter = $iriConverter; + $this->requestStack = $requestStack; + } + + public function __invoke(string $slug): RedirectResponse + { + $channel = $this->channelContext->getChannel(); + $locale = $this->localeContext->getLocaleCode(); + + $product = $this->productRepository->findOneByChannelAndSlug($channel, $locale, $slug); + + if (null === $product) { + throw new NotFoundHttpException('Not Found'); + } + + $iri = $this->iriConverter->getIriFromItem($product); + + $request = $this->requestStack->getCurrentRequest(); + + $requestQuery = $request->getQueryString(); + if (null !== $requestQuery) { + $iri .= sprintf('?%s', $requestQuery); + } + + return new RedirectResponse($iri, Response::HTTP_MOVED_PERMANENTLY); + } +} diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/api_resources/Product.xml b/src/Sylius/Bundle/ApiBundle/Resources/config/api_resources/Product.xml index 920f18fcbd7..bfba4c9bfa0 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/api_resources/Product.xml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/api_resources/Product.xml @@ -84,6 +84,29 @@ + + GET + /shop/products-by-slug/{slug} + Sylius\Bundle\ApiBundle\Controller\GetProductBySlugAction + false + + Use slug to retrieve a product resource. + + + slug + path + true + + string + + + + + + shop:product:read + + + PUT /admin/products/{code} diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/services.xml b/src/Sylius/Bundle/ApiBundle/Resources/config/services.xml index 3e607913f28..79df47a2297 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/services.xml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/services.xml @@ -31,6 +31,14 @@ + + + + + + + + diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/services/swagger.xml b/src/Sylius/Bundle/ApiBundle/Resources/config/services/swagger.xml index ca1f865c2c9..238cc0ca09d 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/services/swagger.xml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/services/swagger.xml @@ -57,6 +57,15 @@ + + + + decoratedNormalizer = $decoratedNormalizer; + } + + public function supportsNormalization($data, $format = null): bool + { + return $this->decoratedNormalizer->supportsNormalization($data, $format); + } + + public function normalize($object, $format = null, array $context = []) + { + $docs = $this->decoratedNormalizer->normalize($object, $format, $context); + + $params = $docs['paths'][self::PRODUCT_SLUG_PATH]['get']['parameters']; + + foreach ($params as $index => $param) { + if ($param['name'] === 'code') { + unset($docs['paths'][self::PRODUCT_SLUG_PATH]['get']['parameters'][$index]); + } + } + + return $docs; + } +} diff --git a/tests/Api/Shop/ProductsTest.php b/tests/Api/Shop/ProductsTest.php new file mode 100644 index 00000000000..bab5cf325e7 --- /dev/null +++ b/tests/Api/Shop/ProductsTest.php @@ -0,0 +1,32 @@ +loadFixturesFromFile('product_variant_with_original_price.yaml'); + + $this->client->request('GET', '/api/v2/shop/products-by-slug/mug?paramName=paramValue', [], [], self::CONTENT_TYPE_HEADER); + $response = $this->client->getResponse(); + + $this->assertEquals('/api/v2/shop/products/MUG?paramName=paramValue', $response->headers->get(('Location'))); + $this->assertResponseCode($response, Response::HTTP_MOVED_PERMANENTLY); + } +}