From 98c5d71ec34a0c5b9fa8e78b90d721fa4c934b55 Mon Sep 17 00:00:00 2001 From: CSchulz Date: Sun, 22 Sep 2019 16:01:40 +0200 Subject: [PATCH 1/2] add simple product name and description search --- doc/swagger.yml | 29 ++ .../ShowProductCatalogByPhraseAction.php | 59 +++ src/Resources/config/routing.yml | 4 + .../config/routing/productByPhrase.yml | 7 + .../config/services/actions/product.xml | 8 + .../Product/ProductCatalogViewRepository.php | 22 + .../ProductCatalogViewRepositoryInterface.php | 2 + .../Product/ShowCatalogByNameApiTest.php | 76 ++++ tests/DataFixtures/ORM/shop.yml | 1 + .../product_list_latest_4_response.json | 1 + ...german_including_description_response.json | 242 +++++++++++ ...page_by_phrase_mug_in_german_response.json | 71 ++++ ...se_mug_including_description_response.json | 397 ++++++++++++++++++ ..._including_short_description_response.json | 35 ++ ...duct_list_page_by_phrase_mug_response.json | 72 ++++ .../product_without_taxons_details_page.json | 1 + 16 files changed, 1027 insertions(+) create mode 100644 src/Controller/Product/ShowProductCatalogByPhraseAction.php create mode 100644 src/Resources/config/routing/productByPhrase.yml create mode 100644 tests/Controller/Product/ShowCatalogByNameApiTest.php create mode 100644 tests/Responses/Expected/product/product_list_page_by_phrase_mug_in_german_including_description_response.json create mode 100644 tests/Responses/Expected/product/product_list_page_by_phrase_mug_in_german_response.json create mode 100644 tests/Responses/Expected/product/product_list_page_by_phrase_mug_including_description_response.json create mode 100644 tests/Responses/Expected/product/product_list_page_by_phrase_mug_including_short_description_response.json create mode 100644 tests/Responses/Expected/product/product_list_page_by_phrase_mug_response.json diff --git a/doc/swagger.yml b/doc/swagger.yml index 726ae85e1..7ee6a5cc3 100644 --- a/doc/swagger.yml +++ b/doc/swagger.yml @@ -553,6 +553,35 @@ paths: schema: $ref: "#/definitions/ProductDetails" + /products/by-phrase/{phrase}: + get: + tags: + - "products" + summary: "Show a product catalog." + description: "This endpoint will return a paginated list of products for given phrase." + operationId: "productCatalogByPhrase" + parameters: + - name: "phrase" + in: "path" + description: "Phrase to search for." + required: true + type: "string" + - name: "locale" + in: "query" + description: "Locale in which products should be shown." + required: false + type: "string" + - name: "includeDescription" + in: "query" + description: "Should the description included in search." + required: false + type: "boolean" + responses: + 200: + description: "Show a product with the given code." + schema: + $ref: "#/definitions/ProductDetails" + /products/by-slug/{slug}/reviews: parameters: - name: "slug" diff --git a/src/Controller/Product/ShowProductCatalogByPhraseAction.php b/src/Controller/Product/ShowProductCatalogByPhraseAction.php new file mode 100644 index 000000000..ff899705a --- /dev/null +++ b/src/Controller/Product/ShowProductCatalogByPhraseAction.php @@ -0,0 +1,59 @@ +viewHandler = $viewHandler; + $this->productCatalogViewRepository = $productLatestQuery; + $this->channelContext = $channelContext; + } + + public function __invoke(Request $request): Response + { + try { + $channel = $this->channelContext->getChannel(); + $phrase = $request->attributes->get('phrase'); + $queryParameters = $request->query->all(); + $queryParameters['phrase'] = $phrase; + + return $this->viewHandler->handle(View::create($this->productCatalogViewRepository->findByPhrase( + $phrase, + $channel->getCode(), + new PaginatorDetails($request->attributes->get('_route'), $queryParameters), + $request->query->getBoolean('includeDescription', false), + $request->query->get('locale') + ), Response::HTTP_OK)); + } catch (ChannelNotFoundException $exception) { + throw new NotFoundHttpException('Channel has not been found.'); + } catch (\InvalidArgumentException $exception) { + throw new NotFoundHttpException($exception->getMessage()); + } + } +} diff --git a/src/Resources/config/routing.yml b/src/Resources/config/routing.yml index 014cef2aa..206b3afce 100644 --- a/src/Resources/config/routing.yml +++ b/src/Resources/config/routing.yml @@ -10,6 +10,10 @@ sylius_shop_api_product_by_code: resource: "@SyliusShopApiPlugin/Resources/config/routing/productByCode.yml" prefix: /shop-api +sylius_shop_api_product_by_phrase: + resource: "@SyliusShopApiPlugin/Resources/config/routing/productByPhrase.yml" + prefix: /shop-api + sylius_shop_api_taxon: resource: "@SyliusShopApiPlugin/Resources/config/routing/taxon.yml" prefix: /shop-api diff --git a/src/Resources/config/routing/productByPhrase.yml b/src/Resources/config/routing/productByPhrase.yml new file mode 100644 index 000000000..b75f32043 --- /dev/null +++ b/src/Resources/config/routing/productByPhrase.yml @@ -0,0 +1,7 @@ +sylius_shop_api_product_show_by_phrase: + path: /products/by-phrase/{phrase} + methods: [GET] + defaults: + _controller: sylius.shop_api_plugin.controller.product.show_product_catalog_by_phrase_action + requirements: + phrase: .+ diff --git a/src/Resources/config/services/actions/product.xml b/src/Resources/config/services/actions/product.xml index 058952730..dacc5bbe4 100644 --- a/src/Resources/config/services/actions/product.xml +++ b/src/Resources/config/services/actions/product.xml @@ -36,6 +36,14 @@ + + + + + + diff --git a/src/ViewRepository/Product/ProductCatalogViewRepository.php b/src/ViewRepository/Product/ProductCatalogViewRepository.php index 00af80ab9..6bfd6d14c 100644 --- a/src/ViewRepository/Product/ProductCatalogViewRepository.php +++ b/src/ViewRepository/Product/ProductCatalogViewRepository.php @@ -4,6 +4,7 @@ namespace Sylius\ShopApiPlugin\ViewRepository\Product; +use Doctrine\ORM\QueryBuilder; use Pagerfanta\Adapter\DoctrineORMAdapter; use Pagerfanta\Pagerfanta; use Sylius\Component\Channel\Repository\ChannelRepositoryInterface; @@ -82,6 +83,22 @@ public function findByTaxonCode(string $taxonCode, string $channelCode, Paginato return $this->findByTaxon($taxon, $channel, $paginatorDetails, $localeCode); } + public function findByPhrase(string $phrase, string $channelCode, PaginatorDetails $paginatorDetails, ?bool $includeDescription, ?string $localeCode): PageView + { + $channel = $this->getChannel($channelCode); + $localeCode = $this->supportedLocaleProvider->provide($localeCode, $channel); + + $queryBuilder = $this->productRepository->createListQueryBuilder($localeCode); + $queryBuilder->andWhere('translation.name LIKE :phrase'); + if (true === $includeDescription) { + $queryBuilder->orWhere('translation.description LIKE :phrase'); + $queryBuilder->orWhere('translation.shortDescription LIKE :phrase'); + } + $queryBuilder->setParameter('phrase', '%' . $phrase . '%'); + + return $this->createPageView($queryBuilder, $paginatorDetails, $channel, $localeCode); + } + private function getChannel(string $channelCode): ChannelInterface { /** @var ChannelInterface $channel */ @@ -98,6 +115,11 @@ private function findByTaxon(TaxonInterface $taxon, ChannelInterface $channel, P $queryBuilder->addSelect('productTaxon'); $queryBuilder->addOrderBy('productTaxon.position'); + return $this->createPageView($queryBuilder, $paginatorDetails, $channel, $localeCode); + } + + private function createPageView(QueryBuilder $queryBuilder, PaginatorDetails $paginatorDetails, ChannelInterface $channel, string $localeCode): PageView + { $pagerfanta = new Pagerfanta(new DoctrineORMAdapter($queryBuilder)); $pagerfanta->setMaxPerPage($paginatorDetails->limit()); diff --git a/src/ViewRepository/Product/ProductCatalogViewRepositoryInterface.php b/src/ViewRepository/Product/ProductCatalogViewRepositoryInterface.php index e2e174bdf..1add9876b 100644 --- a/src/ViewRepository/Product/ProductCatalogViewRepositoryInterface.php +++ b/src/ViewRepository/Product/ProductCatalogViewRepositoryInterface.php @@ -12,4 +12,6 @@ interface ProductCatalogViewRepositoryInterface public function findByTaxonSlug(string $taxonSlug, string $channelCode, PaginatorDetails $paginatorDetails, ?string $localeCode): PageView; public function findByTaxonCode(string $taxonCode, string $channelCode, PaginatorDetails $paginatorDetails, ?string $localeCode): PageView; + + public function findByPhrase(string $phrase, string $channelCode, PaginatorDetails $paginatorDetails, ?bool $includeDescription, ?string $localeCode): PageView; } diff --git a/tests/Controller/Product/ShowCatalogByNameApiTest.php b/tests/Controller/Product/ShowCatalogByNameApiTest.php new file mode 100644 index 000000000..64dc540f3 --- /dev/null +++ b/tests/Controller/Product/ShowCatalogByNameApiTest.php @@ -0,0 +1,76 @@ +loadFixturesFromFiles(['shop.yml']); + + $this->client->request('GET', '/shop-api/products/by-phrase/mug', [], [], self::CONTENT_TYPE_HEADER); + $response = $this->client->getResponse(); + + $this->assertResponse($response, 'product/product_list_page_by_phrase_mug_response', Response::HTTP_OK); + } + + /** + * @test + */ + public function it_shows_paginated_products_for_phrase_including_description(): void + { + $this->loadFixturesFromFiles(['shop.yml']); + + $this->client->request('GET', '/shop-api/products/by-phrase/Lorem', ['includeDescription' => true], [], self::CONTENT_TYPE_HEADER); + $response = $this->client->getResponse(); + + $this->assertResponse($response, 'product/product_list_page_by_phrase_mug_including_description_response', Response::HTTP_OK); + } + + /** + * @test + */ + public function it_shows_paginated_products_for_phrase_including_short_description(): void + { + $this->loadFixturesFromFiles(['shop.yml']); + + $this->client->request('GET', '/shop-api/products/by-phrase/short', ['includeDescription' => true], [], self::CONTENT_TYPE_HEADER); + $response = $this->client->getResponse(); + + $this->assertResponse($response, 'product/product_list_page_by_phrase_mug_including_short_description_response', Response::HTTP_OK); + } + + /** + * @test + */ + public function it_shows_paginated_products_for_phrase_in_different_language(): void + { + $this->loadFixturesFromFiles(['shop.yml']); + + $this->client->request('GET', '/shop-api/products/by-phrase/becher?locale=de_DE', [], [], self::CONTENT_TYPE_HEADER); + $response = $this->client->getResponse(); + + $this->assertResponse($response, 'product/product_list_page_by_phrase_mug_in_german_response', Response::HTTP_OK); + } + + /** + * @test + */ + public function it_shows_paginated_products_for_phrase_in_different_language_including_description(): void + { + $this->loadFixturesFromFiles(['shop.yml']); + + $this->client->request('GET', '/shop-api/products/by-phrase/Beschreibung?locale=de_DE', ['includeDescription' => true], [], self::CONTENT_TYPE_HEADER); + $response = $this->client->getResponse(); + + $this->assertResponse($response, 'product/product_list_page_by_phrase_mug_in_german_including_description_response', Response::HTTP_OK); + } +} diff --git a/tests/DataFixtures/ORM/shop.yml b/tests/DataFixtures/ORM/shop.yml index 4e3780a72..602e75c06 100644 --- a/tests/DataFixtures/ORM/shop.yml +++ b/tests/DataFixtures/ORM/shop.yml @@ -109,6 +109,7 @@ Sylius\Component\Core\Model\ProductTranslation: locale: "en_GB" name: "Logan Shoes" description: "Some description Lorem ipsum dolor sit amet." + shortDescription: "Short description Lorem ipsum dolor sit amet." translatable: "@shoes" Sylius\Component\Core\Model\ProductVariant: diff --git a/tests/Responses/Expected/product/product_list_latest_4_response.json b/tests/Responses/Expected/product/product_list_latest_4_response.json index 37ce0dd69..7f75f1939 100644 --- a/tests/Responses/Expected/product/product_list_latest_4_response.json +++ b/tests/Responses/Expected/product/product_list_latest_4_response.json @@ -293,6 +293,7 @@ "slug": "logan-shoes", "channelCode": "WEB_GB", "description": "Some description Lorem ipsum dolor sit amet.", + "shortDescription": "Short description Lorem ipsum dolor sit amet.", "averageRating": 0, "taxons": { "others": [] diff --git a/tests/Responses/Expected/product/product_list_page_by_phrase_mug_in_german_including_description_response.json b/tests/Responses/Expected/product/product_list_page_by_phrase_mug_in_german_including_description_response.json new file mode 100644 index 000000000..1fa083e78 --- /dev/null +++ b/tests/Responses/Expected/product/product_list_page_by_phrase_mug_in_german_including_description_response.json @@ -0,0 +1,242 @@ +{ + "page": 1, + "limit": 10, + "pages": 1, + "total": 1, + "_links": { + "self": "\/shop-api\/products\/by-phrase\/Beschreibung?locale=de_DE&includeDescription=1&page=1&limit=10", + "first": "\/shop-api\/products\/by-phrase\/Beschreibung?locale=de_DE&includeDescription=1&page=1&limit=10", + "last": "\/shop-api\/products\/by-phrase\/Beschreibung?locale=de_DE&includeDescription=1&page=1&limit=10", + "next": "\/shop-api\/products\/by-phrase\/Beschreibung?locale=de_DE&includeDescription=1&page=1&limit=10" + }, + "items": [ + { + "code": "LOGAN_HAT_CODE", + "name": "Logan Hut", + "slug": "logan-hut", + "channelCode": "WEB_GB", + "description": "Einige Beschreibung Lorem ipsum dolor sit amet.", + "averageRating": 0, + "taxons": { + "main": "HAT", + "others": [ + "HAT", + "BRAND" + ] + }, + "variants": { + "SMALL_RED_LOGAN_HAT_CODE": { + "code": "SMALL_RED_LOGAN_HAT_CODE", + "name": "Klein Rot Logan Hut", + "axis": [ + "HAT_SIZE_S", + "HAT_COLOR_RED" + ], + "nameAxis": { + "HAT_SIZE_S": "Size S", + "HAT_COLOR_RED": "Farbe Rot" + }, + "price": { + "current": 500, + "currency": "GBP" + }, + "images": [] + }, + "LARGE_RED_LOGAN_HAT_CODE": { + "code": "LARGE_RED_LOGAN_HAT_CODE", + "name": "Large Red Logan Hat", + "axis": [ + "HAT_SIZE_L", + "HAT_COLOR_RED" + ], + "nameAxis": { + "HAT_SIZE_L": "Size L", + "HAT_COLOR_RED": "Farbe Rot" + }, + "price": { + "current": 999, + "currency": "GBP" + }, + "images": [] + }, + "SMALL_BLUE_LOGAN_HAT_CODE": { + "code": "SMALL_BLUE_LOGAN_HAT_CODE", + "name": "Klein Blau Logan Hut", + "axis": [ + "HAT_SIZE_S", + "HAT_COLOR_BLUE" + ], + "nameAxis": { + "HAT_SIZE_S": "Size S", + "HAT_COLOR_BLUE": "Farbe Blau" + }, + "price": { + "current": 1500, + "currency": "GBP" + }, + "images": [] + }, + "LARGE_BLUE_LOGAN_HAT_CODE": { + "code": "LARGE_BLUE_LOGAN_HAT_CODE", + "name": "Gro\u00dfes Blau Logan Hut", + "axis": [ + "HAT_SIZE_L", + "HAT_COLOR_BLUE" + ], + "nameAxis": { + "HAT_SIZE_L": "Size L", + "HAT_COLOR_BLUE": "Farbe Blau" + }, + "price": { + "current": 2599, + "currency": "GBP" + }, + "images": [] + } + }, + "attributes": [], + "associations": { + "CROSS_SELL_ASSOCIATION_TYPE": [ + { + "code": "LOGAN_MUG_CODE", + "name": "Logan Becher", + "slug": "logan-becher", + "channelCode": "WEB_GB", + "averageRating": 0, + "taxons": { + "main": "MUG", + "others": [ + "MUG", + "BRAND" + ] + }, + "variants": { + "LOGAN_MUG_CODE": { + "code": "LOGAN_MUG_CODE", + "name": "Logan Becher", + "axis": [], + "nameAxis": {}, + "price": { + "current": 1999, + "currency": "GBP" + }, + "originalPrice": { + "current": 2999, + "currency": "GBP" + }, + "images": [ + { + "code": "thumbnail", + "path": "\/pants.jpeg", + "cachedPath": "http:\/\/localhost\/media\/cache\/sylius_shop_api\/pants.jpeg" + } + ] + } + }, + "attributes": [ + { + "code": "MUG_MATERIAL_CODE", + "name": "Becher Material", + "value": "Wood" + } + ], + "associations": [], + "images": [ + { + "code": "thumbnail", + "path": "\/pants.jpeg", + "cachedPath": "http:\/\/localhost\/media\/cache\/sylius_shop_api\/pants.jpeg" + } + ], + "_links": { + "self": { + "href": "\/shop-api\/products\/by-slug\/logan-becher" + } + } + }, + { + "code": "LOGAN_T_SHIRT_CODE", + "name": "Logan T-Shirt", + "slug": "logan-t-shirt", + "channelCode": "WEB_GB", + "description": "Some description Lorem ipsum dolor sit amet.", + "averageRating": 0, + "taxons": { + "main": "T_SHIRTS", + "others": [ + "WOMEN_T_SHIRTS", + "BRAND" + ] + }, + "variants": { + "SMALL_LOGAN_T_SHIRT_CODE": { + "code": "SMALL_LOGAN_T_SHIRT_CODE", + "name": "Small Logan T-Shirt", + "axis": [], + "nameAxis": {}, + "price": { + "current": 1999, + "currency": "GBP" + }, + "images": [ + { + "code": "thumbnail", + "path": "\/pants.jpeg", + "cachedPath": "http:\/\/localhost\/media\/cache\/sylius_shop_api\/pants.jpeg" + } + ] + }, + "LARGE_LOGAN_T_SHIRT_CODE": { + "code": "LARGE_LOGAN_T_SHIRT_CODE", + "name": "Large Logan T-Shirt", + "axis": [], + "nameAxis": {}, + "price": { + "current": 2999, + "currency": "GBP" + }, + "images": [ + { + "code": "thumbnail", + "path": "\/pants.jpeg", + "cachedPath": "http:\/\/localhost\/media\/cache\/sylius_shop_api\/pants.jpeg" + } + ] + } + }, + "attributes": [], + "associations": [], + "images": [ + { + "code": "thumbnail", + "path": "\/pants.jpeg", + "cachedPath": "http:\/\/localhost\/media\/cache\/sylius_shop_api\/pants.jpeg" + }, + { + "code": "thumbnail", + "path": "\/pants.jpeg", + "cachedPath": "http:\/\/localhost\/media\/cache\/sylius_shop_api\/pants.jpeg" + }, + { + "code": "thumbnail", + "path": "\/pants.jpeg", + "cachedPath": "http:\/\/localhost\/media\/cache\/sylius_shop_api\/pants.jpeg" + } + ], + "_links": { + "self": { + "href": "\/shop-api\/products\/by-slug\/logan-t-shirt" + } + } + } + ] + }, + "images": [], + "_links": { + "self": { + "href": "\/shop-api\/products\/by-slug\/logan-hut" + } + } + } + ] +} diff --git a/tests/Responses/Expected/product/product_list_page_by_phrase_mug_in_german_response.json b/tests/Responses/Expected/product/product_list_page_by_phrase_mug_in_german_response.json new file mode 100644 index 000000000..f325d92bd --- /dev/null +++ b/tests/Responses/Expected/product/product_list_page_by_phrase_mug_in_german_response.json @@ -0,0 +1,71 @@ +{ + "page": 1, + "limit": 10, + "pages": 1, + "total": 1, + "_links": { + "self": "\/shop-api\/products\/by-phrase\/becher?locale=de_DE&page=1&limit=10", + "first": "\/shop-api\/products\/by-phrase\/becher?locale=de_DE&page=1&limit=10", + "last": "\/shop-api\/products\/by-phrase\/becher?locale=de_DE&page=1&limit=10", + "next": "\/shop-api\/products\/by-phrase\/becher?locale=de_DE&page=1&limit=10" + }, + "items": [ + { + "code": "LOGAN_MUG_CODE", + "name": "Logan Becher", + "slug": "logan-becher", + "channelCode": "WEB_GB", + "averageRating": 0, + "taxons": { + "main": "MUG", + "others": [ + "MUG", + "BRAND" + ] + }, + "variants": { + "LOGAN_MUG_CODE": { + "code": "LOGAN_MUG_CODE", + "name": "Logan Becher", + "axis": [], + "nameAxis": {}, + "price": { + "current": 1999, + "currency": "GBP" + }, + "originalPrice": { + "current": 2999, + "currency": "GBP" + }, + "images": [ + { + "code": "thumbnail", + "path": "\/pants.jpeg", + "cachedPath": "http:\/\/localhost\/media\/cache\/sylius_shop_api\/pants.jpeg" + } + ] + } + }, + "attributes": [ + { + "code": "MUG_MATERIAL_CODE", + "name": "Becher Material", + "value": "Wood" + } + ], + "associations": [], + "images": [ + { + "code": "thumbnail", + "path": "\/pants.jpeg", + "cachedPath": "http:\/\/localhost\/media\/cache\/sylius_shop_api\/pants.jpeg" + } + ], + "_links": { + "self": { + "href": "\/shop-api\/products\/by-slug\/logan-becher" + } + } + } + ] +} diff --git a/tests/Responses/Expected/product/product_list_page_by_phrase_mug_including_description_response.json b/tests/Responses/Expected/product/product_list_page_by_phrase_mug_including_description_response.json new file mode 100644 index 000000000..b9179f29a --- /dev/null +++ b/tests/Responses/Expected/product/product_list_page_by_phrase_mug_including_description_response.json @@ -0,0 +1,397 @@ +{ + "page": 1, + "limit": 10, + "pages": 1, + "total": 4, + "_links": { + "self": "\/shop-api\/products\/by-phrase\/Lorem?includeDescription=1&page=1&limit=10", + "first": "\/shop-api\/products\/by-phrase\/Lorem?includeDescription=1&page=1&limit=10", + "last": "\/shop-api\/products\/by-phrase\/Lorem?includeDescription=1&page=1&limit=10", + "next": "\/shop-api\/products\/by-phrase\/Lorem?includeDescription=1&page=1&limit=10" + }, + "items": [ + { + "code": "LOGAN_MUG_CODE", + "name": "Logan Mug", + "slug": "logan-mug", + "channelCode": "WEB_GB", + "description": "Some description Lorem ipsum dolor sit amet.", + "averageRating": 0, + "taxons": { + "main": "MUG", + "others": [ + "MUG", + "BRAND" + ] + }, + "variants": { + "LOGAN_MUG_CODE": { + "code": "LOGAN_MUG_CODE", + "name": "Logan Mug", + "axis": [], + "nameAxis": {}, + "price": { + "current": 1999, + "currency": "GBP" + }, + "originalPrice": { + "current": 2999, + "currency": "GBP" + }, + "images": [ + { + "code": "thumbnail", + "path": "\/pants.jpeg", + "cachedPath": "http:\/\/localhost\/media\/cache\/sylius_shop_api\/pants.jpeg" + } + ] + } + }, + "attributes": [ + { + "code": "MUG_MATERIAL_CODE", + "name": "Mug material", + "value": "Wood" + } + ], + "associations": [], + "images": [ + { + "code": "thumbnail", + "path": "\/pants.jpeg", + "cachedPath": "http:\/\/localhost\/media\/cache\/sylius_shop_api\/pants.jpeg" + } + ], + "_links": { + "self": { + "href": "\/shop-api\/products\/by-slug\/logan-mug" + } + } + }, + { + "code": "LOGAN_T_SHIRT_CODE", + "name": "Logan T-Shirt", + "slug": "logan-t-shirt", + "channelCode": "WEB_GB", + "description": "Some description Lorem ipsum dolor sit amet.", + "averageRating": 0, + "taxons": { + "main": "T_SHIRTS", + "others": [ + "WOMEN_T_SHIRTS", + "BRAND" + ] + }, + "variants": { + "SMALL_LOGAN_T_SHIRT_CODE": { + "code": "SMALL_LOGAN_T_SHIRT_CODE", + "name": "Small Logan T-Shirt", + "axis": [], + "nameAxis": {}, + "price": { + "current": 1999, + "currency": "GBP" + }, + "images": [ + { + "code": "thumbnail", + "path": "\/pants.jpeg", + "cachedPath": "http:\/\/localhost\/media\/cache\/sylius_shop_api\/pants.jpeg" + } + ] + }, + "LARGE_LOGAN_T_SHIRT_CODE": { + "code": "LARGE_LOGAN_T_SHIRT_CODE", + "name": "Large Logan T-Shirt", + "axis": [], + "nameAxis": {}, + "price": { + "current": 2999, + "currency": "GBP" + }, + "images": [ + { + "code": "thumbnail", + "path": "\/pants.jpeg", + "cachedPath": "http:\/\/localhost\/media\/cache\/sylius_shop_api\/pants.jpeg" + } + ] + } + }, + "attributes": [], + "associations": [], + "images": [ + { + "code": "thumbnail", + "path": "\/pants.jpeg", + "cachedPath": "http:\/\/localhost\/media\/cache\/sylius_shop_api\/pants.jpeg" + }, + { + "code": "thumbnail", + "path": "\/pants.jpeg", + "cachedPath": "http:\/\/localhost\/media\/cache\/sylius_shop_api\/pants.jpeg" + }, + { + "code": "thumbnail", + "path": "\/pants.jpeg", + "cachedPath": "http:\/\/localhost\/media\/cache\/sylius_shop_api\/pants.jpeg" + } + ], + "_links": { + "self": { + "href": "\/shop-api\/products\/by-slug\/logan-t-shirt" + } + } + }, + { + "code": "LOGAN_HAT_CODE", + "name": "Logan Hat", + "slug": "logan-hat", + "channelCode": "WEB_GB", + "description": "Some description Lorem ipsum dolor sit amet.", + "averageRating": 0, + "taxons": { + "main": "HAT", + "others": [ + "HAT", + "BRAND" + ] + }, + "variants": { + "SMALL_RED_LOGAN_HAT_CODE": { + "code": "SMALL_RED_LOGAN_HAT_CODE", + "name": "Small Red Logan Hat", + "axis": [ + "HAT_SIZE_S", + "HAT_COLOR_RED" + ], + "nameAxis": { + "HAT_SIZE_S": "Size S", + "HAT_COLOR_RED": "Color Red" + }, + "price": { + "current": 500, + "currency": "GBP" + }, + "images": [] + }, + "LARGE_RED_LOGAN_HAT_CODE": { + "code": "LARGE_RED_LOGAN_HAT_CODE", + "name": "Large Red Logan Hat", + "axis": [ + "HAT_SIZE_L", + "HAT_COLOR_RED" + ], + "nameAxis": { + "HAT_SIZE_L": "Size L", + "HAT_COLOR_RED": "Color Red" + }, + "price": { + "current": 999, + "currency": "GBP" + }, + "images": [] + }, + "SMALL_BLUE_LOGAN_HAT_CODE": { + "code": "SMALL_BLUE_LOGAN_HAT_CODE", + "name": "Small Blue Logan Hat", + "axis": [ + "HAT_SIZE_S", + "HAT_COLOR_BLUE" + ], + "nameAxis": { + "HAT_SIZE_S": "Size S", + "HAT_COLOR_BLUE": "Color Blue" + }, + "price": { + "current": 1500, + "currency": "GBP" + }, + "images": [] + }, + "LARGE_BLUE_LOGAN_HAT_CODE": { + "code": "LARGE_BLUE_LOGAN_HAT_CODE", + "name": "Large Blue Logan Hat", + "axis": [ + "HAT_SIZE_L", + "HAT_COLOR_BLUE" + ], + "nameAxis": { + "HAT_SIZE_L": "Size L", + "HAT_COLOR_BLUE": "Color Blue" + }, + "price": { + "current": 2599, + "currency": "GBP" + }, + "images": [] + } + }, + "attributes": [], + "associations": { + "CROSS_SELL_ASSOCIATION_TYPE": [ + { + "code": "LOGAN_MUG_CODE", + "name": "Logan Mug", + "slug": "logan-mug", + "channelCode": "WEB_GB", + "description": "Some description Lorem ipsum dolor sit amet.", + "averageRating": 0, + "taxons": { + "main": "MUG", + "others": [ + "MUG", + "BRAND" + ] + }, + "variants": { + "LOGAN_MUG_CODE": { + "code": "LOGAN_MUG_CODE", + "name": "Logan Mug", + "axis": [], + "nameAxis": {}, + "price": { + "current": 1999, + "currency": "GBP" + }, + "originalPrice": { + "current": 2999, + "currency": "GBP" + }, + "images": [ + { + "code": "thumbnail", + "path": "\/pants.jpeg", + "cachedPath": "http:\/\/localhost\/media\/cache\/sylius_shop_api\/pants.jpeg" + } + ] + } + }, + "attributes": [ + { + "code": "MUG_MATERIAL_CODE", + "name": "Mug material", + "value": "Wood" + } + ], + "associations": [], + "images": [ + { + "code": "thumbnail", + "path": "\/pants.jpeg", + "cachedPath": "http:\/\/localhost\/media\/cache\/sylius_shop_api\/pants.jpeg" + } + ], + "_links": { + "self": { + "href": "\/shop-api\/products\/by-slug\/logan-mug" + } + } + }, + { + "code": "LOGAN_T_SHIRT_CODE", + "name": "Logan T-Shirt", + "slug": "logan-t-shirt", + "channelCode": "WEB_GB", + "description": "Some description Lorem ipsum dolor sit amet.", + "averageRating": 0, + "taxons": { + "main": "T_SHIRTS", + "others": [ + "WOMEN_T_SHIRTS", + "BRAND" + ] + }, + "variants": { + "SMALL_LOGAN_T_SHIRT_CODE": { + "code": "SMALL_LOGAN_T_SHIRT_CODE", + "name": "Small Logan T-Shirt", + "axis": [], + "nameAxis": {}, + "price": { + "current": 1999, + "currency": "GBP" + }, + "images": [ + { + "code": "thumbnail", + "path": "\/pants.jpeg", + "cachedPath": "http:\/\/localhost\/media\/cache\/sylius_shop_api\/pants.jpeg" + } + ] + }, + "LARGE_LOGAN_T_SHIRT_CODE": { + "code": "LARGE_LOGAN_T_SHIRT_CODE", + "name": "Large Logan T-Shirt", + "axis": [], + "nameAxis": {}, + "price": { + "current": 2999, + "currency": "GBP" + }, + "images": [ + { + "code": "thumbnail", + "path": "\/pants.jpeg", + "cachedPath": "http:\/\/localhost\/media\/cache\/sylius_shop_api\/pants.jpeg" + } + ] + } + }, + "attributes": [], + "associations": [], + "images": [ + { + "code": "thumbnail", + "path": "\/pants.jpeg", + "cachedPath": "http:\/\/localhost\/media\/cache\/sylius_shop_api\/pants.jpeg" + }, + { + "code": "thumbnail", + "path": "\/pants.jpeg", + "cachedPath": "http:\/\/localhost\/media\/cache\/sylius_shop_api\/pants.jpeg" + }, + { + "code": "thumbnail", + "path": "\/pants.jpeg", + "cachedPath": "http:\/\/localhost\/media\/cache\/sylius_shop_api\/pants.jpeg" + } + ], + "_links": { + "self": { + "href": "\/shop-api\/products\/by-slug\/logan-t-shirt" + } + } + } + ] + }, + "images": [], + "_links": { + "self": { + "href": "\/shop-api\/products\/by-slug\/logan-hat" + } + } + }, + { + "code": "LOGAN_SHOES_CODE", + "name": "Logan Shoes", + "slug": "logan-shoes", + "channelCode": "WEB_GB", + "description": "Some description Lorem ipsum dolor sit amet.", + "shortDescription": "Short description Lorem ipsum dolor sit amet.", + "averageRating": 0, + "taxons": { + "others": [] + }, + "variants": [], + "attributes": [], + "associations": [], + "images": [], + "_links": { + "self": { + "href": "\/shop-api\/products\/by-slug\/logan-shoes" + } + } + } + ] +} diff --git a/tests/Responses/Expected/product/product_list_page_by_phrase_mug_including_short_description_response.json b/tests/Responses/Expected/product/product_list_page_by_phrase_mug_including_short_description_response.json new file mode 100644 index 000000000..cdff9f82d --- /dev/null +++ b/tests/Responses/Expected/product/product_list_page_by_phrase_mug_including_short_description_response.json @@ -0,0 +1,35 @@ +{ + "page": 1, + "limit": 10, + "pages": 1, + "total": 1, + "_links": { + "self": "\/shop-api\/products\/by-phrase\/short?includeDescription=1&page=1&limit=10", + "first": "\/shop-api\/products\/by-phrase\/short?includeDescription=1&page=1&limit=10", + "last": "\/shop-api\/products\/by-phrase\/short?includeDescription=1&page=1&limit=10", + "next": "\/shop-api\/products\/by-phrase\/short?includeDescription=1&page=1&limit=10" + }, + "items": [ + { + "code": "LOGAN_SHOES_CODE", + "name": "Logan Shoes", + "slug": "logan-shoes", + "channelCode": "WEB_GB", + "description": "Some description Lorem ipsum dolor sit amet.", + "shortDescription": "Short description Lorem ipsum dolor sit amet.", + "averageRating": 0, + "taxons": { + "others": [] + }, + "variants": [], + "attributes": [], + "associations": [], + "images": [], + "_links": { + "self": { + "href": "\/shop-api\/products\/by-slug\/logan-shoes" + } + } + } + ] +} diff --git a/tests/Responses/Expected/product/product_list_page_by_phrase_mug_response.json b/tests/Responses/Expected/product/product_list_page_by_phrase_mug_response.json new file mode 100644 index 000000000..2281fdfab --- /dev/null +++ b/tests/Responses/Expected/product/product_list_page_by_phrase_mug_response.json @@ -0,0 +1,72 @@ +{ + "page": 1, + "limit": 10, + "pages": 1, + "total": 1, + "_links": { + "self": "\/shop-api\/products\/by-phrase\/mug?page=1&limit=10", + "first": "\/shop-api\/products\/by-phrase\/mug?page=1&limit=10", + "last": "\/shop-api\/products\/by-phrase\/mug?page=1&limit=10", + "next": "\/shop-api\/products\/by-phrase\/mug?page=1&limit=10" + }, + "items": [ + { + "code": "LOGAN_MUG_CODE", + "name": "Logan Mug", + "slug": "logan-mug", + "channelCode": "WEB_GB", + "description": "Some description Lorem ipsum dolor sit amet.", + "averageRating": 0, + "taxons": { + "main": "MUG", + "others": [ + "MUG", + "BRAND" + ] + }, + "variants": { + "LOGAN_MUG_CODE": { + "code": "LOGAN_MUG_CODE", + "name": "Logan Mug", + "axis": [], + "nameAxis": {}, + "price": { + "current": 1999, + "currency": "GBP" + }, + "originalPrice": { + "current": 2999, + "currency": "GBP" + }, + "images": [ + { + "code": "thumbnail", + "path": "\/pants.jpeg", + "cachedPath": "http:\/\/localhost\/media\/cache\/sylius_shop_api\/pants.jpeg" + } + ] + } + }, + "attributes": [ + { + "code": "MUG_MATERIAL_CODE", + "name": "Mug material", + "value": "Wood" + } + ], + "associations": [], + "images": [ + { + "code": "thumbnail", + "path": "\/pants.jpeg", + "cachedPath": "http:\/\/localhost\/media\/cache\/sylius_shop_api\/pants.jpeg" + } + ], + "_links": { + "self": { + "href": "\/shop-api\/products\/by-slug\/logan-mug" + } + } + } + ] +} diff --git a/tests/Responses/Expected/product/product_without_taxons_details_page.json b/tests/Responses/Expected/product/product_without_taxons_details_page.json index e7602bd11..022bb44eb 100644 --- a/tests/Responses/Expected/product/product_without_taxons_details_page.json +++ b/tests/Responses/Expected/product/product_without_taxons_details_page.json @@ -5,6 +5,7 @@ "channelCode": "WEB_GB", "breadcrumb": "logan-shoes", "description": "Some description Lorem ipsum dolor sit amet.", + "shortDescription": "Short description Lorem ipsum dolor sit amet.", "averageRating": 0, "taxons": { "others": [] From 39f3d8ad581e326b17acc0ad96fac887dc12cdeb Mon Sep 17 00:00:00 2001 From: CSchulz Date: Mon, 23 Sep 2019 18:22:03 +0200 Subject: [PATCH 2/2] add additional tests --- ...est.php => ShowCatalogByPhraseApiTest.php} | 26 ++++++++++++++++++- ...ct_list_page_by_phrase_empty_response.json | 13 ++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) rename tests/Controller/Product/{ShowCatalogByNameApiTest.php => ShowCatalogByPhraseApiTest.php} (74%) create mode 100644 tests/Responses/Expected/product/product_list_page_by_phrase_empty_response.json diff --git a/tests/Controller/Product/ShowCatalogByNameApiTest.php b/tests/Controller/Product/ShowCatalogByPhraseApiTest.php similarity index 74% rename from tests/Controller/Product/ShowCatalogByNameApiTest.php rename to tests/Controller/Product/ShowCatalogByPhraseApiTest.php index 64dc540f3..72fc34f46 100644 --- a/tests/Controller/Product/ShowCatalogByNameApiTest.php +++ b/tests/Controller/Product/ShowCatalogByPhraseApiTest.php @@ -7,7 +7,7 @@ use Symfony\Component\HttpFoundation\Response; use Tests\Sylius\ShopApiPlugin\Controller\JsonApiTestCase; -final class ShowCatalogByNameApiTest extends JsonApiTestCase +final class ShowCatalogByPhraseApiTest extends JsonApiTestCase { /** * @test @@ -22,6 +22,19 @@ public function it_shows_paginated_products_for_phrase(): void $this->assertResponse($response, 'product/product_list_page_by_phrase_mug_response', Response::HTTP_OK); } + /** + * @test + */ + public function it_shows_empty_products_list_for_non_existing_products(): void + { + $this->loadFixturesFromFiles(['shop.yml']); + + $this->client->request('GET', '/shop-api/products/by-phrase/unknown', [], [], self::CONTENT_TYPE_HEADER); + $response = $this->client->getResponse(); + + $this->assertResponse($response, 'product/product_list_page_by_phrase_empty_response', Response::HTTP_OK); + } + /** * @test */ @@ -73,4 +86,15 @@ public function it_shows_paginated_products_for_phrase_in_different_language_inc $this->assertResponse($response, 'product/product_list_page_by_phrase_mug_in_german_including_description_response', Response::HTTP_OK); } + + /** + * @test + */ + public function it_throws_a_not_found_exception_if_channel_has_not_been_found(): void + { + $this->client->request('GET', '/shop-api/products/by-phrase/mug', [], [], self::CONTENT_TYPE_HEADER); + $response = $this->client->getResponse(); + + $this->assertResponse($response, 'product/channel_has_not_been_found_response', Response::HTTP_NOT_FOUND); + } } diff --git a/tests/Responses/Expected/product/product_list_page_by_phrase_empty_response.json b/tests/Responses/Expected/product/product_list_page_by_phrase_empty_response.json new file mode 100644 index 000000000..3ca74d9e4 --- /dev/null +++ b/tests/Responses/Expected/product/product_list_page_by_phrase_empty_response.json @@ -0,0 +1,13 @@ +{ + "page": 1, + "limit": 10, + "pages": 1, + "total": 0, + "_links": { + "self": "/shop-api/products/by-phrase/unknown?page=1&limit=10", + "first": "/shop-api/products/by-phrase/unknown?page=1&limit=10", + "last": "/shop-api/products/by-phrase/unknown?page=1&limit=10", + "next": "/shop-api/products/by-phrase/unknown?page=1&limit=10" + }, + "items": [] +}