From 9da5ededad5122ac540c843db344a93f5260ff0f Mon Sep 17 00:00:00 2001 From: Thomas Ramel Date: Tue, 16 May 2023 16:43:42 +0200 Subject: [PATCH 1/4] CXP-1480: Update the Demo app JSON schema --- phpunit.xml.dist | 1 - src/PimApi/Model/ProductValue.php | 5 +- src/PimApi/PimCatalogApiClient.php | 33 ++++- src/Query/FetchMappedProductQuery.php | 34 ++--- src/Query/FetchMappedProductsQuery.php | 12 +- src/Service/InitializeAppData.php | 121 ++++++++++++---- templates/macros/displayAttribute.html.twig | 25 ++-- templates/product.html.twig | 2 +- ...atalogs-mapped-product-missing-fields.json | 4 +- .../get-catalogs-mapped-product-scanner.json | 17 ++- .../get-catalogs-mapped-products.json | 59 ++++---- .../Controller/ShowProductActionTest.php | 51 +++++-- .../PimApi/PimCatalogApiClientTest.php | 17 ++- .../Query/FetchMappedProductQueryTest.php | 132 ++++++++++++++++-- .../Query/FetchMappedProductsQueryTest.php | 6 +- translations/messages.en_US.yaml | 17 ++- 16 files changed, 409 insertions(+), 127 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index d29f5d9..a5da810 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -7,7 +7,6 @@ colors="true" bootstrap="tests/bootstrap.php" convertDeprecationsToExceptions="false" - testdox="true" executionOrder="random" beStrictAboutTestsThatDoNotTestAnything="true" > diff --git a/src/PimApi/Model/ProductValue.php b/src/PimApi/Model/ProductValue.php index 1d467a6..959611d 100644 --- a/src/PimApi/Model/ProductValue.php +++ b/src/PimApi/Model/ProductValue.php @@ -6,10 +6,13 @@ class ProductValue { + /** + * @param string|bool|int|float|array|null $value + */ public function __construct( public readonly string $label, public readonly string $type, - public readonly string|bool|int|float $value, + public readonly null|string|bool|int|float|array $value, ) { } } diff --git a/src/PimApi/PimCatalogApiClient.php b/src/PimApi/PimCatalogApiClient.php index 207eb6c..77dec40 100644 --- a/src/PimApi/PimCatalogApiClient.php +++ b/src/PimApi/PimCatalogApiClient.php @@ -12,6 +12,29 @@ use App\Storage\PimURLStorageInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; +/** + * @phpstan-type RawMappedProduct array{ + * uuid: string, + * sku: string, + * name: string, + * type?: string, + * body_html?: string, + * main_image?: string, + * main_color?: string, + * colors?: array, + * available?: boolean, + * price?: int|float, + * publication_date?: string, + * certification_number?: string, + * size_letter?: string, + * size_number?: int|float, + * weight?: int|float, + * } + * @phpstan-type ErrorResponse array{ + * error?: string, + * message?: string, + * } + */ class PimCatalogApiClient { public function __construct( @@ -191,7 +214,7 @@ public function getCatalogProduct(string $catalogId, string $productUuid): array } /** - * @return array + * @return array * * @throws CatalogDisabledException * @throws PimApiException @@ -229,7 +252,7 @@ public function getMappedProducts( } /** - * @return array + * @return RawMappedProduct * * @throws CatalogDisabledException * @throws PimApiException @@ -245,12 +268,16 @@ public function getMappedProduct(string $catalogId, string $productUuid): array $this->throwOnErrorCode(200, $response->getStatusCode(), "Couldn't get mapped product"); + /** @var RawMappedProduct|ErrorResponse $response */ $response = $response->toArray(); if (isset($response['message']) || isset($response['error'])) { throw new CatalogDisabledException(); } - return $response; + /** @var RawMappedProduct $rawMappedProduct */ + $rawMappedProduct = $response; + + return $rawMappedProduct; } } diff --git a/src/Query/FetchMappedProductQuery.php b/src/Query/FetchMappedProductQuery.php index 883d6ee..3fdd650 100644 --- a/src/Query/FetchMappedProductQuery.php +++ b/src/Query/FetchMappedProductQuery.php @@ -11,12 +11,7 @@ use App\PimApi\PimCatalogApiClient; /** - * @phpstan-type RawMappedProduct array{ - * uuid: string, - * title?: string, - * description?: string, - * code?: string, - * } + * @phpstan-import-type RawMappedProduct from PimCatalogApiClient */ final class FetchMappedProductQuery { @@ -30,21 +25,28 @@ public function fetch(string $catalogId, string $productUuid): Product try { /** @var RawMappedProduct $rawMappedProduct */ $rawMappedProduct = $this->catalogApiClient->getMappedProduct($catalogId, $productUuid); - } catch (PimApiException $e) { + } catch (PimApiException) { throw new CatalogProductNotFoundException(); } - $label = !empty($rawMappedProduct['title']) ? $rawMappedProduct['title'] : $rawMappedProduct['uuid']; - $uuid = $rawMappedProduct['uuid']; - $title = $rawMappedProduct['title'] ?? ''; - $description = $rawMappedProduct['description'] ?? ''; - $code = $rawMappedProduct['code'] ?? ''; + $label = !empty($rawMappedProduct['name']) ? $rawMappedProduct['name'] : $rawMappedProduct['uuid']; $values = [ - new ProductValue('mapped_properties.uuid', 'string', $uuid), - new ProductValue('mapped_properties.title', 'string', $title), - new ProductValue('mapped_properties.description', 'string', $description), - new ProductValue('mapped_properties.code', 'string', $code), + new ProductValue('mapped_properties.uuid', 'string', $rawMappedProduct['uuid']), + new ProductValue('mapped_properties.sku', 'string', $rawMappedProduct['sku']), + new ProductValue('mapped_properties.name', 'string', $rawMappedProduct['name']), + new ProductValue('mapped_properties.type', 'string', $rawMappedProduct['type'] ?? null), + new ProductValue('mapped_properties.body_html', 'string', $rawMappedProduct['body_html'] ?? null), + new ProductValue('mapped_properties.main_image', 'string+uri', $rawMappedProduct['main_image'] ?? null), + new ProductValue('mapped_properties.main_color', 'string', $rawMappedProduct['main_color'] ?? null), + new ProductValue('mapped_properties.colors', 'array', $rawMappedProduct['colors'] ?? null), + new ProductValue('mapped_properties.available', 'boolean', $rawMappedProduct['available'] ?? null), + new ProductValue('mapped_properties.price', 'number', $rawMappedProduct['price'] ?? null), + new ProductValue('mapped_properties.publication_date', 'string', $rawMappedProduct['publication_date'] ?? null), + new ProductValue('mapped_properties.certification_number', 'string', $rawMappedProduct['certification_number'] ?? null), + new ProductValue('mapped_properties.size_letter', 'string', $rawMappedProduct['size_letter'] ?? null), + new ProductValue('mapped_properties.size_number', 'number', $rawMappedProduct['size_number'] ?? null), + new ProductValue('mapped_properties.weight', 'number', $rawMappedProduct['weight'] ?? null), ]; return new Product($productUuid, $label, $values); diff --git a/src/Query/FetchMappedProductsQuery.php b/src/Query/FetchMappedProductsQuery.php index 7697cdc..67b2b10 100644 --- a/src/Query/FetchMappedProductsQuery.php +++ b/src/Query/FetchMappedProductsQuery.php @@ -8,12 +8,7 @@ use App\PimApi\PimCatalogApiClient; /** - * @phpstan-type RawMappedProduct array{ - * uuid: string, - * title: string, - * description: string, - * code: string, - * } + * @phpstan-import-type RawMappedProduct from PimCatalogApiClient */ final class FetchMappedProductsQuery { @@ -26,12 +21,13 @@ public function __construct(private readonly PimCatalogApiClient $catalogApiClie */ public function fetch(string $catalogId): array { + /** @var array $rawMappedProducts */ $rawMappedProducts = $this->catalogApiClient->getMappedProducts($catalogId); $products = []; foreach ($rawMappedProducts as $rawMappedProduct) { - $label = isset($rawMappedProduct['title']) && '' !== $rawMappedProduct['title'] - ? $rawMappedProduct['title'] + $label = '' !== $rawMappedProduct['name'] + ? $rawMappedProduct['name'] : $rawMappedProduct['uuid']; $products[] = new Product($rawMappedProduct['uuid'], $label); diff --git a/src/Service/InitializeAppData.php b/src/Service/InitializeAppData.php index 5584d2c..7bbab04 100644 --- a/src/Service/InitializeAppData.php +++ b/src/Service/InitializeAppData.php @@ -21,7 +21,7 @@ public function __invoke(): void $attributeMappingCatalog = $this->findCatalogWithName($catalogs, Catalog::ATTRIBUTE_MAPPING_NAME); if (null === $valueFilterCatalog) { - $valueFilterCatalog = $this->pimCatalogApiClient->createCatalog(Catalog::PRODUCT_VALUE_FILTERS_NAME); + $this->pimCatalogApiClient->createCatalog(Catalog::PRODUCT_VALUE_FILTERS_NAME); } if (null === $attributeMappingCatalog) { @@ -48,33 +48,98 @@ private function getProductMappingSchema(): string { return <<<'JSON_WRAP' { - "$id":"https://example.com/product", - "$schema":"https://api.akeneo.com/mapping/product/0.0.2/schema", - "$comment":"We give you an example of product mapping schema!", - "title":"Demo app - Product Mapping Schema", - "description":"JSON Schema describing the structure of products expected by the Demo App", - "type":"object", - "properties":{ - "title":{ - "title":"Title", - "type":"string", - "description": "Used in the product grid and displayed in the product page details" - }, - "description":{ - "title":"Description", - "type":"string", - "description": "Only displayed in the product page details" - }, - "code":{ - "title":"Code", - "type":"string", - "description": "Only displayed in the product page details" - }, - "uuid":{ - "title":"Product UUID", - "type":"string" - } - } + "$id": "https://example.com/product", + "$schema": "https://api.akeneo.com/mapping/product/0.0.13/schema", + "$comment": "My schema !", + "title": "Product Mapping", + "description": "JSON Schema describing the structure of products expected by our application", + "type": "object", + "properties": { + "uuid": { + "title": "Product UUID", + "type": "string" + }, + "type": { + "title": "Product type", + "type": "string" + }, + "sku": { + "title": "SKU (Stock Keeping Unit)", + "description": "Selling Partner SKU (stock keeping unit) identifier for the listing. \n SKU uniquely identifies a listing for a Selling Partner.", + "type": "string" + }, + "name": { + "title": "Product name", + "type": "string" + }, + "body_html": { + "title": "Description (textarea)", + "description": "Product description in raw HTML", + "type": "string", + "minLength": 0, + "maxLength": 255 + }, + "main_image": { + "title": "Main image", + "description": "Format: URI/link", + "type": "string", + "format": "uri" + }, + "main_color": { + "title": "Main color", + "description": "The main color of the product, used by grid filters on your e-commerce website.", + "type": "string" + }, + "colors": { + "title": "Colors", + "description": "List of colors separated by a comma.", + "type": "array", + "items": { + "type": "string", + "enum": ["blue", "red", "green", "yellow"] + } + }, + "available": { + "title": "Is available", + "description": "Used to display when a product is out of stock on your e-commerce website.", + "type": "boolean" + }, + "price": { + "title": "Price (€)", + "type": "number", + "minimum": 0, + "maximum": 10000 + }, + "publication_date": { + "title": "Publication date", + "description": "Format: ISO 8601 standard. \nUsed to filter products that must be published on your e-commerce website depending on the current date.", + "type": "string", + "format": "date-time" + }, + "certification_number": { + "title": "Certification number", + "type": "string", + "pattern": "^([0-9]{5})-([0-9]):([0-9]{4})$" + }, + "size_letter": { + "title": "Size (letter)", + "type": "string", + "enum": ["S", "M", "L", "XL"] + }, + "size_number": { + "title": "Size", + "type": "number", + "enum": [36, 38, 40, 42] + }, + "weight": { + "title": "Weight (grams)", + "type": "number", + "minimum": 0 + } + }, + "required": [ + "sku", "name" + ] } JSON_WRAP; } diff --git a/templates/macros/displayAttribute.html.twig b/templates/macros/displayAttribute.html.twig index b3728da..7074904 100644 --- a/templates/macros/displayAttribute.html.twig +++ b/templates/macros/displayAttribute.html.twig @@ -1,10 +1,19 @@ -{% macro value(attribute) %} - {% set displayValue = attribute.value %} - {% if attribute.type is same as('pim_catalog_boolean') %} - {% set trueValueTrans = 'page.product.attributes.boolean_values.yes' | trans %} - {% set falseValueTrans = 'page.product.attributes.boolean_values.no' | trans %} - {% set displayValue = displayValue ? trueValueTrans : falseValueTrans %} +{% macro formatValue(value, type) %} + {% if value is not null %} + {% if type is same as('boolean') %} + {{ value + ? 'page.product.attributes.boolean_values.yes' | trans + : 'page.product.attributes.boolean_values.no' | trans + }} + {% elseif type is same as('string+uri') %} + {{ value }} + {% elseif type matches '{^array<.+>$}' %} + {% set itemType = type|slice(6, type|length - 7) %} + {% for itemValue in value %} + {{ _self.formatValue(itemValue, itemType) }}{% if not loop.last %}
{% endif %} + {% endfor %} + {% else %} + {{ value }} + {% endif %} {% endif %} - - {{ displayValue }} {% endmacro %} diff --git a/templates/product.html.twig b/templates/product.html.twig index 9fd1408..c0ec205 100644 --- a/templates/product.html.twig +++ b/templates/product.html.twig @@ -38,7 +38,7 @@ {% for attribute in product.attributes %}

{{ attribute.label | trans }}

-

{{ displayAttribute.value(attribute) }}

+

{{ displayAttribute.formatValue(attribute.value, attribute.type) }}

{% endfor %} diff --git a/tests/Fixtures/responses/get-catalogs-mapped-product-missing-fields.json b/tests/Fixtures/responses/get-catalogs-mapped-product-missing-fields.json index 97558e2..8e02d70 100644 --- a/tests/Fixtures/responses/get-catalogs-mapped-product-missing-fields.json +++ b/tests/Fixtures/responses/get-catalogs-mapped-product-missing-fields.json @@ -1,3 +1,5 @@ { - "uuid": "a5eed606-4f98-4d8c-b926-5b59f8fb0ee7" + "uuid": "a5eed606-4f98-4d8c-b926-5b59f8fb0ee7", + "name": "Kodak i2600 for Govt", + "sku": "1234567890317" } diff --git a/tests/Fixtures/responses/get-catalogs-mapped-product-scanner.json b/tests/Fixtures/responses/get-catalogs-mapped-product-scanner.json index fca74aa..fa042fb 100644 --- a/tests/Fixtures/responses/get-catalogs-mapped-product-scanner.json +++ b/tests/Fixtures/responses/get-catalogs-mapped-product-scanner.json @@ -1,6 +1,17 @@ { "uuid": "a5eed606-4f98-4d8c-b926-5b59f8fb0ee7", - "title": "Kodak i2600 for Govt", - "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", - "code": "" + "sku": "1234567890317", + "name": "Kodak i2600 for Govt", + "type": "scanner", + "body_html": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + "main_image": "https://www.example.com/kodak-i2600.jpg", + "main_color": "navy blue", + "colors": ["grey", "black", "navy blue"], + "available": true, + "price": "269", + "publication_date": "2023-02-01T14:41:36+02:00", + "certification_number": "213-451-2154-124", + "size_letter": "M", + "size_number": 36, + "weight": 1452 } diff --git a/tests/Fixtures/responses/get-catalogs-mapped-products.json b/tests/Fixtures/responses/get-catalogs-mapped-products.json index 99b0ce9..7ebd0b6 100644 --- a/tests/Fixtures/responses/get-catalogs-mapped-products.json +++ b/tests/Fixtures/responses/get-catalogs-mapped-products.json @@ -11,60 +11,63 @@ "items":[ { "uuid": "a5eed606-4f98-4d8c-b926-5b59f8fb0ee7", - "title": "Kodak i2600 for Govt", - "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", - "code": "" + "name": "Kodak i2600 for Govt", + "body_html": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + "sku": "1234567890317" }, { "uuid": "16467667-9a29-48c1-90b3-8a169b83e8e6", - "title": "Sunglasses", - "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer aliquam", - "code": "1234567890316" + "name": "Sunglasses", + "body_html": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer aliquam", + "sku": "1234567890316" }, { "uuid": "5b8381e2-a97a-4120-87da-1ef8b9c53988", - "title": "", - "description": "", - "code": "" + "name": "Sunglasses 2", + "body_html": "", + "sku": "1234567890318" }, { "uuid": "e5442b25-683d-4ea4-bab3-d31a240d3a1a", - "title": "", - "description": "", - "code": "10649473" + "name": "Sunglasses 3", + "body_html": "", + "sku": "10649473" }, { - "uuid": "08e92c31-375a-414f-86ce-f146dc180727" + "uuid": "08e92c31-375a-414f-86ce-f146dc180727", + "name": "Sunglasses 4", + "body_html": "", + "sku": "1234567890319" }, { "uuid": "bee7ad24-f08b-4603-9d88-1a80f099640e", - "title": "Canon imageFormula DR-C125", - "description": "The Canon imageFORMULA DR-C125 Document Scanner's innovative space-saving design makes it a standout in any office environment for improving information accessibility, management, and collaboration. Its flexibility and high image quality are only surpassed by its easy use, making it ideal for converting and capturing paper-based documents to digital format.\n\nThe DR-C125 scanner is ideal for general purpose distributed scanning of a variety of document types. In corporate or remote offices, this scanner is well-suited for use by individuals or small workgroups to modernize paper-based processes, increasing efficiency and productivity. Legal services, financial services, healthcare, government, and other industries can benefit from its small size and easy use.\n\n- Space Saving\n- Easy Use\n- Flexibility\n- High Image Quality\n- Environment In Mind\n- Customer Care", - "code": "10655470" + "name": "Canon imageFormula DR-C125", + "body_html": "The Canon imageFORMULA DR-C125 Document Scanner's innovative space-saving design makes it a standout in any office environment for improving information accessibility, management, and collaboration. Its flexibility and high image quality are only surpassed by its easy use, making it ideal for converting and capturing paper-based documents to digital format.\n\nThe DR-C125 scanner is ideal for general purpose distributed scanning of a variety of document types. In corporate or remote offices, this scanner is well-suited for use by individuals or small workgroups to modernize paper-based processes, increasing efficiency and productivity. Legal services, financial services, healthcare, government, and other industries can benefit from its small size and easy use.\n\n- Space Saving\n- Easy Use\n- Flexibility\n- High Image Quality\n- Environment In Mind\n- Customer Care", + "sku": "10655470" }, { "uuid": "554ed26b-b179-4058-9ff8-4e4a660dbd8a", - "title": "Kodak i2600 for Govt", - "description": "Energize your information for a competitive edge\n\nTodayu2019s business runs on information. But when that information is on paper, it can slow you down. Get things flowing faster with the KODAK i2600 Scanner. It lets you extract critical information from documents at the point of entry, for quick distribution to decision-makers who need it. So money to be gained isnu2019t left waiting for choices to be made.\n\nNo matter what size your business or department is, the reliable performance of the KODAK i2600 Scanner can enable collaboration right from your desktop. It decreases processing time for information that needs to be shared for review, reference, approval, and legal and compliance reasons. This helps increase both internal and external customer satisfaction, and saves your company time and money. Plus, if your business has branch offices or field agents, distributed capture allows document images to be immediately sent to a central location for easy access.\n\nHelp your company get ahead with a scanning solution that can bring you increased security, faster internal processes, better teamwork, and a host of other business benefitsu2014the KODAK i2600 Scanner.", - "code": "10661721" + "name": "Kodak i2600 for Govt", + "body_html": "Energize your information for a competitive edge\n\nTodayu2019s business runs on information. But when that information is on paper, it can slow you down. Get things flowing faster with the KODAK i2600 Scanner. It lets you extract critical information from documents at the point of entry, for quick distribution to decision-makers who need it. So money to be gained isnu2019t left waiting for choices to be made.\n\nNo matter what size your business or department is, the reliable performance of the KODAK i2600 Scanner can enable collaboration right from your desktop. It decreases processing time for information that needs to be shared for review, reference, approval, and legal and compliance reasons. This helps increase both internal and external customer satisfaction, and saves your company time and money. Plus, if your business has branch offices or field agents, distributed capture allows document images to be immediately sent to a central location for easy access.\n\nHelp your company get ahead with a scanning solution that can bring you increased security, faster internal processes, better teamwork, and a host of other business benefitsu2014the KODAK i2600 Scanner.", + "sku": "10661721" }, { "uuid": "032a9ced-134c-41eb-9bb2-ee2089e3496a", - "title": "Kodak Scanner upgrade kit I640 TO I660", - "description": "Through the years, Kodak has led the way with an abundance of new products and processes that have made photography simpler, more useful and more enjoyable. Today, the company's work increasingly involves digital technology, combining the power and convenience of electronics with the quality of traditional photography to produce systems that bring levels of utility and fun to the taking, making and utilization of images.", - "code": "1211614" + "name": "Kodak Scanner upgrade kit I640 TO I660", + "body_html": "Through the years, Kodak has led the way with an abundance of new products and processes that have made photography simpler, more useful and more enjoyable. Today, the company's work increasingly involves digital technology, combining the power and convenience of electronics with the quality of traditional photography to produce systems that bring levels of utility and fun to the taking, making and utilization of images.", + "sku": "1211614" }, { "uuid": "ab88bd1a-a944-49f8-b633-33a972a4efce", - "title": "HP Scanjet G4050", - "description": "Scan multiple photos, slides, film and negatives and get superior[1] colour accuracy with 6-colour, 96-bit scanning u2013 exclusive to HP. Capture every detail with 4800 x 9600-dpi hardware resolution[2]. Get infrared-based dust ", - "code": "12239052" + "name": "HP Scanjet G4050", + "body_html": "Scan multiple photos, slides, film and negatives and get superior[1] colour accuracy with 6-colour, 96-bit scanning u2013 exclusive to HP. Capture every detail with 4800 x 9600-dpi hardware resolution[2]. Get infrared-based dust ", + "sku": "12239052" }, { "uuid": "4c40f7c5-416d-48af-8635-e4e5f0915092", - "title": "HP Scanjet G4010", - "description": "World's first 6-colour photo scanner for industry-leading colour accuracy. Enjoy high-definition scan quality that delivers vivid and realistic images with each scan.\n\n- Capture true-to-original colour for your creative projects with HPu2019s exclusive, 6-colour, 96-bit photo scanning. Get high-definition detail with 4800 x 9600-dpi resolution[1].\n- Easily restore colours to old, faded photos, and remove dust or scratches with HP Real Life technologies' scanning software. Give photos a more vibrant, realistic quality with HP Adaptive Lighting which brighten darker areas and bring out details.\n- Easily convert documents and images to digital files using the 4 one-touch buttons: Scan, Scan Film, Copy and HP Photosmart Instant Share. Archive or share via email and edit text from scanned documents.\n\n[1] Not available for scanning negatives in 6-colour; maximum resolution may be affected by PC system factors", - "code": "12239064" + "name": "HP Scanjet G4010", + "body_html": "World's first 6-colour photo scanner for industry-leading colour accuracy. Enjoy high-definition scan quality that delivers vivid and realistic images with each scan.\n\n- Capture true-to-original colour for your creative projects with HPu2019s exclusive, 6-colour, 96-bit photo scanning. Get high-definition detail with 4800 x 9600-dpi resolution[1].\n- Easily restore colours to old, faded photos, and remove dust or scratches with HP Real Life technologies' scanning software. Give photos a more vibrant, realistic quality with HP Adaptive Lighting which brighten darker areas and bring out details.\n- Easily convert documents and images to digital files using the 4 one-touch buttons: Scan, Scan Film, Copy and HP Photosmart Instant Share. Archive or share via email and edit text from scanned documents.\n\n[1] Not available for scanning negatives in 6-colour; maximum resolution may be affected by PC system factors", + "sku": "12239064" } ] } diff --git a/tests/Integration/Controller/ShowProductActionTest.php b/tests/Integration/Controller/ShowProductActionTest.php index 53f9527..f39c8c3 100644 --- a/tests/Integration/Controller/ShowProductActionTest.php +++ b/tests/Integration/Controller/ShowProductActionTest.php @@ -187,19 +187,52 @@ public function itDisplaysAMappedProduct(): void $this->assertSelectorTextContains('h1.page-title', 'Kodak i2600 for Govt'); $foundAttributes = $crawler->filter('.attribute'); - $this->assertEquals(4, $foundAttributes->count()); + $this->assertEquals(15, $foundAttributes->count()); - $eanLabel = $foundAttributes->eq(0)->filter('.attribute__label')->text(); - $this->assertEquals('Product UUID', $eanLabel); + $label0 = $foundAttributes->eq(0)->filter('.attribute__label')->text(); + $this->assertEquals('Product UUID', $label0); - $eanLabel = $foundAttributes->eq(1)->filter('.attribute__label')->text(); - $this->assertEquals('Title', $eanLabel); + $label1 = $foundAttributes->eq(1)->filter('.attribute__label')->text(); + $this->assertEquals('SKU (Stock Keeping Unit)', $label1); - $eanLabel = $foundAttributes->eq(2)->filter('.attribute__label')->text(); - $this->assertEquals('Description', $eanLabel); + $label2 = $foundAttributes->eq(2)->filter('.attribute__label')->text(); + $this->assertEquals('Product name', $label2); + + $label3 = $foundAttributes->eq(3)->filter('.attribute__label')->text(); + $this->assertEquals('Product type', $label3); + + $label4 = $foundAttributes->eq(4)->filter('.attribute__label')->text(); + $this->assertEquals('Description', $label4); + + $label5 = $foundAttributes->eq(5)->filter('.attribute__label')->text(); + $this->assertEquals('Main image', $label5); + + $label6 = $foundAttributes->eq(6)->filter('.attribute__label')->text(); + $this->assertEquals('Main color', $label6); + + $label7 = $foundAttributes->eq(7)->filter('.attribute__label')->text(); + $this->assertEquals('Colors', $label7); + + $label8 = $foundAttributes->eq(8)->filter('.attribute__label')->text(); + $this->assertEquals('Is available', $label8); + + $label9 = $foundAttributes->eq(9)->filter('.attribute__label')->text(); + $this->assertEquals('Price (€)', $label9); + + $label10 = $foundAttributes->eq(10)->filter('.attribute__label')->text(); + $this->assertEquals('Publication date', $label10); + + $label11 = $foundAttributes->eq(11)->filter('.attribute__label')->text(); + $this->assertEquals('Certification number', $label11); + + $label12 = $foundAttributes->eq(12)->filter('.attribute__label')->text(); + $this->assertEquals('Size (letter)', $label12); + + $label13 = $foundAttributes->eq(13)->filter('.attribute__label')->text(); + $this->assertEquals('Size', $label13); - $eanLabel = $foundAttributes->eq(3)->filter('.attribute__label')->text(); - $this->assertEquals('Code', $eanLabel); + $label14 = $foundAttributes->eq(14)->filter('.attribute__label')->text(); + $this->assertEquals('Weight (grams)', $label14); } /** diff --git a/tests/Integration/PimApi/PimCatalogApiClientTest.php b/tests/Integration/PimApi/PimCatalogApiClientTest.php index 51d69f2..3aa1b13 100644 --- a/tests/Integration/PimApi/PimCatalogApiClientTest.php +++ b/tests/Integration/PimApi/PimCatalogApiClientTest.php @@ -301,9 +301,20 @@ public function itRetrievesMappedProduct(): void $this->assertEquals([ 'uuid' => 'a5eed606-4f98-4d8c-b926-5b59f8fb0ee7', - 'title' => 'Kodak i2600 for Govt', - 'description' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', - 'code' => '', + 'name' => 'Kodak i2600 for Govt', + 'body_html' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', + 'sku' => '1234567890317', + 'type' => 'scanner', + 'main_image' => 'https://www.example.com/kodak-i2600.jpg', + 'main_color' => 'navy blue', + 'colors' => ['grey', 'black', 'navy blue'], + 'available' => true, + 'price' => '269', + 'publication_date' => '2023-02-01T14:41:36+02:00', + 'certification_number' => '213-451-2154-124', + 'size_letter' => 'M', + 'size_number' => 36, + 'weight' => 1452, ], $result); } diff --git a/tests/Integration/Query/FetchMappedProductQueryTest.php b/tests/Integration/Query/FetchMappedProductQueryTest.php index 2a4f73d..6c1cde1 100644 --- a/tests/Integration/Query/FetchMappedProductQueryTest.php +++ b/tests/Integration/Query/FetchMappedProductQueryTest.php @@ -47,19 +47,74 @@ public function itFetchesAProduct(): void value: 'a5eed606-4f98-4d8c-b926-5b59f8fb0ee7', ), new ProductValue( - label: 'mapped_properties.title', + label: 'mapped_properties.sku', + type: 'string', + value: '1234567890317', + ), + new ProductValue( + label: 'mapped_properties.name', type: 'string', value: 'Kodak i2600 for Govt', ), new ProductValue( - label: 'mapped_properties.description', + label: 'mapped_properties.type', + type: 'string', + value: 'scanner', + ), + new ProductValue( + label: 'mapped_properties.body_html', type: 'string', value: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', ), new ProductValue( - label: 'mapped_properties.code', + label: 'mapped_properties.main_image', + type: 'string+uri', + value: 'https://www.example.com/kodak-i2600.jpg', + ), + new ProductValue( + label: 'mapped_properties.main_color', + type: 'string', + value: 'navy blue', + ), + new ProductValue( + label: 'mapped_properties.colors', + type: 'array', + value: ['grey', 'black', 'navy blue'], + ), + new ProductValue( + label: 'mapped_properties.available', + type: 'boolean', + value: true, + ), + new ProductValue( + label: 'mapped_properties.price', + type: 'number', + value: '269', + ), + new ProductValue( + label: 'mapped_properties.publication_date', + type: 'string', + value: '2023-02-01T14:41:36+02:00', + ), + new ProductValue( + label: 'mapped_properties.certification_number', + type: 'string', + value: '213-451-2154-124', + ), + new ProductValue( + label: 'mapped_properties.size_letter', type: 'string', - value: '', + value: 'M', + ), + new ProductValue( + label: 'mapped_properties.size_number', + type: 'number', + value: 36, + ), + new ProductValue( + label: 'mapped_properties.weight', + type: 'number', + value: 1452, ), ]); @@ -92,26 +147,81 @@ public function itFetchesAProductDespiteMissingFields(): void $result = $this->query->fetch('8a8494d2-05cc-4b8f-942e-f5ea7591e89c', 'a5eed606-4f98-4d8c-b926-5b59f8fb0ee7'); - $expected = new Product('a5eed606-4f98-4d8c-b926-5b59f8fb0ee7', 'a5eed606-4f98-4d8c-b926-5b59f8fb0ee7', [ + $expected = new Product('a5eed606-4f98-4d8c-b926-5b59f8fb0ee7', 'Kodak i2600 for Govt', [ new ProductValue( label: 'mapped_properties.uuid', type: 'string', value: 'a5eed606-4f98-4d8c-b926-5b59f8fb0ee7', ), new ProductValue( - label: 'mapped_properties.title', + label: 'mapped_properties.sku', type: 'string', - value: '', + value: '1234567890317', ), new ProductValue( - label: 'mapped_properties.description', + label: 'mapped_properties.name', type: 'string', - value: '', + value: 'Kodak i2600 for Govt', + ), + new ProductValue( + label: 'mapped_properties.type', + type: 'string', + value: null, + ), + new ProductValue( + label: 'mapped_properties.body_html', + type: 'string', + value: null, + ), + new ProductValue( + label: 'mapped_properties.main_image', + type: 'string+uri', + value: null, + ), + new ProductValue( + label: 'mapped_properties.main_color', + type: 'string', + value: null, + ), + new ProductValue( + label: 'mapped_properties.colors', + type: 'array', + value: null, ), new ProductValue( - label: 'mapped_properties.code', + label: 'mapped_properties.available', + type: 'boolean', + value: null, + ), + new ProductValue( + label: 'mapped_properties.price', + type: 'number', + value: null, + ), + new ProductValue( + label: 'mapped_properties.publication_date', + type: 'string', + value: null, + ), + new ProductValue( + label: 'mapped_properties.certification_number', type: 'string', - value: '', + value: null, + ), + new ProductValue( + label: 'mapped_properties.size_letter', + type: 'string', + value: null, + ), + new ProductValue( + label: 'mapped_properties.size_number', + type: 'number', + value: null, + ), + new ProductValue( + label: 'mapped_properties.weight', + type: 'number', + value: null, ), ]); diff --git a/tests/Integration/Query/FetchMappedProductsQueryTest.php b/tests/Integration/Query/FetchMappedProductsQueryTest.php index 2026393..ba767d7 100644 --- a/tests/Integration/Query/FetchMappedProductsQueryTest.php +++ b/tests/Integration/Query/FetchMappedProductsQueryTest.php @@ -36,9 +36,9 @@ public function itFetchesMappedProduct(): void $this->assertEquals([ new Product('a5eed606-4f98-4d8c-b926-5b59f8fb0ee7', 'Kodak i2600 for Govt'), new Product('16467667-9a29-48c1-90b3-8a169b83e8e6', 'Sunglasses'), - new Product('5b8381e2-a97a-4120-87da-1ef8b9c53988', '5b8381e2-a97a-4120-87da-1ef8b9c53988'), - new Product('e5442b25-683d-4ea4-bab3-d31a240d3a1a', 'e5442b25-683d-4ea4-bab3-d31a240d3a1a'), - new Product('08e92c31-375a-414f-86ce-f146dc180727', '08e92c31-375a-414f-86ce-f146dc180727'), + new Product('5b8381e2-a97a-4120-87da-1ef8b9c53988', 'Sunglasses 2'), + new Product('e5442b25-683d-4ea4-bab3-d31a240d3a1a', 'Sunglasses 3'), + new Product('08e92c31-375a-414f-86ce-f146dc180727', 'Sunglasses 4'), new Product('bee7ad24-f08b-4603-9d88-1a80f099640e', 'Canon imageFormula DR-C125'), new Product('554ed26b-b179-4058-9ff8-4e4a660dbd8a', 'Kodak i2600 for Govt'), new Product('032a9ced-134c-41eb-9bb2-ee2089e3496a', 'Kodak Scanner upgrade kit I640 TO I660'), diff --git a/translations/messages.en_US.yaml b/translations/messages.en_US.yaml index a99e5ae..9adafdd 100644 --- a/translations/messages.en_US.yaml +++ b/translations/messages.en_US.yaml @@ -94,7 +94,18 @@ page: description: The requested page couldn't be located. Checkout for any URL misspelling. mapped_properties: - title: Title - description: Description uuid: Product UUID - code: Code + sku: SKU (Stock Keeping Unit) + name: Product name + type: Product type + body_html: Description + main_image: Main image + main_color: Main color + colors: Colors + available: Is available + price: Price (€) + publication_date: Publication date + certification_number: Certification number + size_letter: Size (letter) + size_number: Size + weight: Weight (grams) From d783dbd72e3cf1b3695c7664caf3c35ee1605775 Mon Sep 17 00:00:00 2001 From: Thomas Ramel Date: Mon, 22 May 2023 10:46:38 +0200 Subject: [PATCH 2/4] CXP-1480: fix type hint order --- src/PimApi/Model/ProductValue.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PimApi/Model/ProductValue.php b/src/PimApi/Model/ProductValue.php index 959611d..66bdb3e 100644 --- a/src/PimApi/Model/ProductValue.php +++ b/src/PimApi/Model/ProductValue.php @@ -12,7 +12,7 @@ class ProductValue public function __construct( public readonly string $label, public readonly string $type, - public readonly null|string|bool|int|float|array $value, + public readonly string|bool|int|float|array|null $value, ) { } } From 1449e6a49d4e5ceae4dd50f17dd904e3def30a8e Mon Sep 17 00:00:00 2001 From: Thomas Ramel Date: Mon, 22 May 2023 15:09:45 +0200 Subject: [PATCH 3/4] CXP-1480: improve test readability --- .../Controller/ShowProductActionTest.php | 68 +++++++------------ 1 file changed, 24 insertions(+), 44 deletions(-) diff --git a/tests/Integration/Controller/ShowProductActionTest.php b/tests/Integration/Controller/ShowProductActionTest.php index f39c8c3..e49b351 100644 --- a/tests/Integration/Controller/ShowProductActionTest.php +++ b/tests/Integration/Controller/ShowProductActionTest.php @@ -189,50 +189,30 @@ public function itDisplaysAMappedProduct(): void $foundAttributes = $crawler->filter('.attribute'); $this->assertEquals(15, $foundAttributes->count()); - $label0 = $foundAttributes->eq(0)->filter('.attribute__label')->text(); - $this->assertEquals('Product UUID', $label0); - - $label1 = $foundAttributes->eq(1)->filter('.attribute__label')->text(); - $this->assertEquals('SKU (Stock Keeping Unit)', $label1); - - $label2 = $foundAttributes->eq(2)->filter('.attribute__label')->text(); - $this->assertEquals('Product name', $label2); - - $label3 = $foundAttributes->eq(3)->filter('.attribute__label')->text(); - $this->assertEquals('Product type', $label3); - - $label4 = $foundAttributes->eq(4)->filter('.attribute__label')->text(); - $this->assertEquals('Description', $label4); - - $label5 = $foundAttributes->eq(5)->filter('.attribute__label')->text(); - $this->assertEquals('Main image', $label5); - - $label6 = $foundAttributes->eq(6)->filter('.attribute__label')->text(); - $this->assertEquals('Main color', $label6); - - $label7 = $foundAttributes->eq(7)->filter('.attribute__label')->text(); - $this->assertEquals('Colors', $label7); - - $label8 = $foundAttributes->eq(8)->filter('.attribute__label')->text(); - $this->assertEquals('Is available', $label8); - - $label9 = $foundAttributes->eq(9)->filter('.attribute__label')->text(); - $this->assertEquals('Price (€)', $label9); - - $label10 = $foundAttributes->eq(10)->filter('.attribute__label')->text(); - $this->assertEquals('Publication date', $label10); - - $label11 = $foundAttributes->eq(11)->filter('.attribute__label')->text(); - $this->assertEquals('Certification number', $label11); - - $label12 = $foundAttributes->eq(12)->filter('.attribute__label')->text(); - $this->assertEquals('Size (letter)', $label12); - - $label13 = $foundAttributes->eq(13)->filter('.attribute__label')->text(); - $this->assertEquals('Size', $label13); - - $label14 = $foundAttributes->eq(14)->filter('.attribute__label')->text(); - $this->assertEquals('Weight (grams)', $label14); + $expectedAttributeLabels = [ + 'Product UUID', + 'SKU (Stock Keeping Unit)', + 'Product name', + 'Product type', + 'Description', + 'Main image', + 'Main color', + 'Colors', + 'Is available', + 'Price (€)', + 'Publication date', + 'Certification number', + 'Size (letter)', + 'Size', + 'Weight (grams)', + ]; + + foreach($expectedAttributeLabels as $index => $expectedAttributeLabel) { + $this->assertEquals( + $expectedAttributeLabel, + $foundAttributes->eq($index)->filter('.attribute__label')->text(), + ); + } } /** From f7a9d3ba75d845a59bcecef37596db8c201bfecf Mon Sep 17 00:00:00 2001 From: Thomas Ramel Date: Mon, 22 May 2023 15:15:08 +0200 Subject: [PATCH 4/4] CXP-1480: fix lint --- tests/Integration/Controller/ShowProductActionTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Integration/Controller/ShowProductActionTest.php b/tests/Integration/Controller/ShowProductActionTest.php index e49b351..5a44eab 100644 --- a/tests/Integration/Controller/ShowProductActionTest.php +++ b/tests/Integration/Controller/ShowProductActionTest.php @@ -207,7 +207,7 @@ public function itDisplaysAMappedProduct(): void 'Weight (grams)', ]; - foreach($expectedAttributeLabels as $index => $expectedAttributeLabel) { + foreach ($expectedAttributeLabels as $index => $expectedAttributeLabel) { $this->assertEquals( $expectedAttributeLabel, $foundAttributes->eq($index)->filter('.attribute__label')->text(),