Skip to content

Commit dc42ad3

Browse files
authored
Merge pull request #59 from akeneo/CXP-1480
CXP-1480: Update the Demo app JSON schema
2 parents b28d086 + f7a9d3b commit dc42ad3

16 files changed

+393
-131
lines changed

phpunit.xml.dist

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
colors="true"
88
bootstrap="tests/bootstrap.php"
99
convertDeprecationsToExceptions="false"
10-
testdox="true"
1110
executionOrder="random"
1211
beStrictAboutTestsThatDoNotTestAnything="true"
1312
>

src/PimApi/Model/ProductValue.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@
66

77
class ProductValue
88
{
9+
/**
10+
* @param string|bool|int|float|array<string>|null $value
11+
*/
912
public function __construct(
1013
public readonly string $label,
1114
public readonly string $type,
12-
public readonly string|bool|int|float $value,
15+
public readonly string|bool|int|float|array|null $value,
1316
) {
1417
}
1518
}

src/PimApi/PimCatalogApiClient.php

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,29 @@
1212
use App\Storage\PimURLStorageInterface;
1313
use Symfony\Contracts\HttpClient\HttpClientInterface;
1414

15+
/**
16+
* @phpstan-type RawMappedProduct array{
17+
* uuid: string,
18+
* sku: string,
19+
* name: string,
20+
* type?: string,
21+
* body_html?: string,
22+
* main_image?: string,
23+
* main_color?: string,
24+
* colors?: array<string>,
25+
* available?: boolean,
26+
* price?: int|float,
27+
* publication_date?: string,
28+
* certification_number?: string,
29+
* size_letter?: string,
30+
* size_number?: int|float,
31+
* weight?: int|float,
32+
* }
33+
* @phpstan-type ErrorResponse array{
34+
* error?: string,
35+
* message?: string,
36+
* }
37+
*/
1538
class PimCatalogApiClient
1639
{
1740
public function __construct(
@@ -191,7 +214,7 @@ public function getCatalogProduct(string $catalogId, string $productUuid): array
191214
}
192215

193216
/**
194-
* @return array<array-key, mixed>
217+
* @return array<array-key, RawMappedProduct>
195218
*
196219
* @throws CatalogDisabledException
197220
* @throws PimApiException
@@ -229,7 +252,7 @@ public function getMappedProducts(
229252
}
230253

231254
/**
232-
* @return array<mixed>
255+
* @return RawMappedProduct
233256
*
234257
* @throws CatalogDisabledException
235258
* @throws PimApiException
@@ -245,12 +268,16 @@ public function getMappedProduct(string $catalogId, string $productUuid): array
245268

246269
$this->throwOnErrorCode(200, $response->getStatusCode(), "Couldn't get mapped product");
247270

271+
/** @var RawMappedProduct|ErrorResponse $response */
248272
$response = $response->toArray();
249273

250274
if (isset($response['message']) || isset($response['error'])) {
251275
throw new CatalogDisabledException();
252276
}
253277

254-
return $response;
278+
/** @var RawMappedProduct $rawMappedProduct */
279+
$rawMappedProduct = $response;
280+
281+
return $rawMappedProduct;
255282
}
256283
}

src/Query/FetchMappedProductQuery.php

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,7 @@
1111
use App\PimApi\PimCatalogApiClient;
1212

1313
/**
14-
* @phpstan-type RawMappedProduct array{
15-
* uuid: string,
16-
* title?: string,
17-
* description?: string,
18-
* code?: string,
19-
* }
14+
* @phpstan-import-type RawMappedProduct from PimCatalogApiClient
2015
*/
2116
final class FetchMappedProductQuery
2217
{
@@ -30,21 +25,28 @@ public function fetch(string $catalogId, string $productUuid): Product
3025
try {
3126
/** @var RawMappedProduct $rawMappedProduct */
3227
$rawMappedProduct = $this->catalogApiClient->getMappedProduct($catalogId, $productUuid);
33-
} catch (PimApiException $e) {
28+
} catch (PimApiException) {
3429
throw new CatalogProductNotFoundException();
3530
}
3631

37-
$label = !empty($rawMappedProduct['title']) ? $rawMappedProduct['title'] : $rawMappedProduct['uuid'];
38-
$uuid = $rawMappedProduct['uuid'];
39-
$title = $rawMappedProduct['title'] ?? '';
40-
$description = $rawMappedProduct['description'] ?? '';
41-
$code = $rawMappedProduct['code'] ?? '';
32+
$label = !empty($rawMappedProduct['name']) ? $rawMappedProduct['name'] : $rawMappedProduct['uuid'];
4233

4334
$values = [
44-
new ProductValue('mapped_properties.uuid', 'string', $uuid),
45-
new ProductValue('mapped_properties.title', 'string', $title),
46-
new ProductValue('mapped_properties.description', 'string', $description),
47-
new ProductValue('mapped_properties.code', 'string', $code),
35+
new ProductValue('mapped_properties.uuid', 'string', $rawMappedProduct['uuid']),
36+
new ProductValue('mapped_properties.sku', 'string', $rawMappedProduct['sku']),
37+
new ProductValue('mapped_properties.name', 'string', $rawMappedProduct['name']),
38+
new ProductValue('mapped_properties.type', 'string', $rawMappedProduct['type'] ?? null),
39+
new ProductValue('mapped_properties.body_html', 'string', $rawMappedProduct['body_html'] ?? null),
40+
new ProductValue('mapped_properties.main_image', 'string+uri', $rawMappedProduct['main_image'] ?? null),
41+
new ProductValue('mapped_properties.main_color', 'string', $rawMappedProduct['main_color'] ?? null),
42+
new ProductValue('mapped_properties.colors', 'array<string>', $rawMappedProduct['colors'] ?? null),
43+
new ProductValue('mapped_properties.available', 'boolean', $rawMappedProduct['available'] ?? null),
44+
new ProductValue('mapped_properties.price', 'number', $rawMappedProduct['price'] ?? null),
45+
new ProductValue('mapped_properties.publication_date', 'string', $rawMappedProduct['publication_date'] ?? null),
46+
new ProductValue('mapped_properties.certification_number', 'string', $rawMappedProduct['certification_number'] ?? null),
47+
new ProductValue('mapped_properties.size_letter', 'string', $rawMappedProduct['size_letter'] ?? null),
48+
new ProductValue('mapped_properties.size_number', 'number', $rawMappedProduct['size_number'] ?? null),
49+
new ProductValue('mapped_properties.weight', 'number', $rawMappedProduct['weight'] ?? null),
4850
];
4951

5052
return new Product($productUuid, $label, $values);

src/Query/FetchMappedProductsQuery.php

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,7 @@
88
use App\PimApi\PimCatalogApiClient;
99

1010
/**
11-
* @phpstan-type RawMappedProduct array{
12-
* uuid: string,
13-
* title: string,
14-
* description: string,
15-
* code: string,
16-
* }
11+
* @phpstan-import-type RawMappedProduct from PimCatalogApiClient
1712
*/
1813
final class FetchMappedProductsQuery
1914
{
@@ -26,12 +21,13 @@ public function __construct(private readonly PimCatalogApiClient $catalogApiClie
2621
*/
2722
public function fetch(string $catalogId): array
2823
{
24+
/** @var array<RawMappedProduct> $rawMappedProducts */
2925
$rawMappedProducts = $this->catalogApiClient->getMappedProducts($catalogId);
3026

3127
$products = [];
3228
foreach ($rawMappedProducts as $rawMappedProduct) {
33-
$label = isset($rawMappedProduct['title']) && '' !== $rawMappedProduct['title']
34-
? $rawMappedProduct['title']
29+
$label = '' !== $rawMappedProduct['name']
30+
? $rawMappedProduct['name']
3531
: $rawMappedProduct['uuid'];
3632

3733
$products[] = new Product($rawMappedProduct['uuid'], $label);

src/Service/InitializeAppData.php

Lines changed: 93 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public function __invoke(): void
2121
$attributeMappingCatalog = $this->findCatalogWithName($catalogs, Catalog::ATTRIBUTE_MAPPING_NAME);
2222

2323
if (null === $valueFilterCatalog) {
24-
$valueFilterCatalog = $this->pimCatalogApiClient->createCatalog(Catalog::PRODUCT_VALUE_FILTERS_NAME);
24+
$this->pimCatalogApiClient->createCatalog(Catalog::PRODUCT_VALUE_FILTERS_NAME);
2525
}
2626

2727
if (null === $attributeMappingCatalog) {
@@ -48,33 +48,98 @@ private function getProductMappingSchema(): string
4848
{
4949
return <<<'JSON_WRAP'
5050
{
51-
"$id":"https://example.com/product",
52-
"$schema":"https://api.akeneo.com/mapping/product/0.0.2/schema",
53-
"$comment":"We give you an example of product mapping schema!",
54-
"title":"Demo app - Product Mapping Schema",
55-
"description":"JSON Schema describing the structure of products expected by the Demo App",
56-
"type":"object",
57-
"properties":{
58-
"title":{
59-
"title":"Title",
60-
"type":"string",
61-
"description": "Used in the product grid and displayed in the product page details"
62-
},
63-
"description":{
64-
"title":"Description",
65-
"type":"string",
66-
"description": "Only displayed in the product page details"
67-
},
68-
"code":{
69-
"title":"Code",
70-
"type":"string",
71-
"description": "Only displayed in the product page details"
72-
},
73-
"uuid":{
74-
"title":"Product UUID",
75-
"type":"string"
76-
}
77-
}
51+
"$id": "https://example.com/product",
52+
"$schema": "https://api.akeneo.com/mapping/product/0.0.13/schema",
53+
"$comment": "My schema !",
54+
"title": "Product Mapping",
55+
"description": "JSON Schema describing the structure of products expected by our application",
56+
"type": "object",
57+
"properties": {
58+
"uuid": {
59+
"title": "Product UUID",
60+
"type": "string"
61+
},
62+
"type": {
63+
"title": "Product type",
64+
"type": "string"
65+
},
66+
"sku": {
67+
"title": "SKU (Stock Keeping Unit)",
68+
"description": "Selling Partner SKU (stock keeping unit) identifier for the listing. \n SKU uniquely identifies a listing for a Selling Partner.",
69+
"type": "string"
70+
},
71+
"name": {
72+
"title": "Product name",
73+
"type": "string"
74+
},
75+
"body_html": {
76+
"title": "Description (textarea)",
77+
"description": "Product description in raw HTML",
78+
"type": "string",
79+
"minLength": 0,
80+
"maxLength": 255
81+
},
82+
"main_image": {
83+
"title": "Main image",
84+
"description": "Format: URI/link",
85+
"type": "string",
86+
"format": "uri"
87+
},
88+
"main_color": {
89+
"title": "Main color",
90+
"description": "The main color of the product, used by grid filters on your e-commerce website.",
91+
"type": "string"
92+
},
93+
"colors": {
94+
"title": "Colors",
95+
"description": "List of colors separated by a comma.",
96+
"type": "array",
97+
"items": {
98+
"type": "string",
99+
"enum": ["blue", "red", "green", "yellow"]
100+
}
101+
},
102+
"available": {
103+
"title": "Is available",
104+
"description": "Used to display when a product is out of stock on your e-commerce website.",
105+
"type": "boolean"
106+
},
107+
"price": {
108+
"title": "Price (€)",
109+
"type": "number",
110+
"minimum": 0,
111+
"maximum": 10000
112+
},
113+
"publication_date": {
114+
"title": "Publication date",
115+
"description": "Format: ISO 8601 standard. \nUsed to filter products that must be published on your e-commerce website depending on the current date.",
116+
"type": "string",
117+
"format": "date-time"
118+
},
119+
"certification_number": {
120+
"title": "Certification number",
121+
"type": "string",
122+
"pattern": "^([0-9]{5})-([0-9]):([0-9]{4})$"
123+
},
124+
"size_letter": {
125+
"title": "Size (letter)",
126+
"type": "string",
127+
"enum": ["S", "M", "L", "XL"]
128+
},
129+
"size_number": {
130+
"title": "Size",
131+
"type": "number",
132+
"enum": [36, 38, 40, 42]
133+
},
134+
"weight": {
135+
"title": "Weight (grams)",
136+
"type": "number",
137+
"minimum": 0
138+
}
139+
},
140+
"required": [
141+
"sku", "name"
142+
]
78143
}
79144
JSON_WRAP;
80145
}
Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
1-
{% macro value(attribute) %}
2-
{% set displayValue = attribute.value %}
3-
{% if attribute.type is same as('pim_catalog_boolean') %}
4-
{% set trueValueTrans = 'page.product.attributes.boolean_values.yes' | trans %}
5-
{% set falseValueTrans = 'page.product.attributes.boolean_values.no' | trans %}
6-
{% set displayValue = displayValue ? trueValueTrans : falseValueTrans %}
1+
{% macro formatValue(value, type) %}
2+
{% if value is not null %}
3+
{% if type is same as('boolean') %}
4+
{{ value
5+
? 'page.product.attributes.boolean_values.yes' | trans
6+
: 'page.product.attributes.boolean_values.no' | trans
7+
}}
8+
{% elseif type is same as('string+uri') %}
9+
<a href="{{ value }}" target="_blank">{{ value }}</a>
10+
{% elseif type matches '{^array<.+>$}' %}
11+
{% set itemType = type|slice(6, type|length - 7) %}
12+
{% for itemValue in value %}
13+
{{ _self.formatValue(itemValue, itemType) }}{% if not loop.last %}<br />{% endif %}
14+
{% endfor %}
15+
{% else %}
16+
{{ value }}
17+
{% endif %}
718
{% endif %}
8-
9-
{{ displayValue }}
1019
{% endmacro %}

templates/product.html.twig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
{% for attribute in product.attributes %}
3939
<div class="attribute">
4040
<h3 class="attribute__label">{{ attribute.label | trans }}</h3>
41-
<p class="attribute__value">{{ displayAttribute.value(attribute) }}</p>
41+
<p class="attribute__value">{{ displayAttribute.formatValue(attribute.value, attribute.type) }}</p>
4242
</div>
4343
{% endfor %}
4444
</section>
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
{
2-
"uuid": "a5eed606-4f98-4d8c-b926-5b59f8fb0ee7"
2+
"uuid": "a5eed606-4f98-4d8c-b926-5b59f8fb0ee7",
3+
"name": "Kodak i2600 for Govt",
4+
"sku": "1234567890317"
35
}
Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
{
22
"uuid": "a5eed606-4f98-4d8c-b926-5b59f8fb0ee7",
3-
"title": "Kodak i2600 for Govt",
4-
"description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
5-
"code": ""
3+
"sku": "1234567890317",
4+
"name": "Kodak i2600 for Govt",
5+
"type": "scanner",
6+
"body_html": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
7+
"main_image": "https://www.example.com/kodak-i2600.jpg",
8+
"main_color": "navy blue",
9+
"colors": ["grey", "black", "navy blue"],
10+
"available": true,
11+
"price": "269",
12+
"publication_date": "2023-02-01T14:41:36+02:00",
13+
"certification_number": "213-451-2154-124",
14+
"size_letter": "M",
15+
"size_number": 36,
16+
"weight": 1452
617
}

0 commit comments

Comments
 (0)