Skip to content

Commit

Permalink
Merge pull request #59 from akeneo/CXP-1480
Browse files Browse the repository at this point in the history
CXP-1480: Update the Demo app JSON schema
  • Loading branch information
masacc authored May 30, 2023
2 parents b28d086 + f7a9d3b commit dc42ad3
Show file tree
Hide file tree
Showing 16 changed files with 393 additions and 131 deletions.
1 change: 0 additions & 1 deletion phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
colors="true"
bootstrap="tests/bootstrap.php"
convertDeprecationsToExceptions="false"
testdox="true"
executionOrder="random"
beStrictAboutTestsThatDoNotTestAnything="true"
>
Expand Down
5 changes: 4 additions & 1 deletion src/PimApi/Model/ProductValue.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@

class ProductValue
{
/**
* @param string|bool|int|float|array<string>|null $value
*/
public function __construct(
public readonly string $label,
public readonly string $type,
public readonly string|bool|int|float $value,
public readonly string|bool|int|float|array|null $value,
) {
}
}
33 changes: 30 additions & 3 deletions src/PimApi/PimCatalogApiClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>,
* 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(
Expand Down Expand Up @@ -191,7 +214,7 @@ public function getCatalogProduct(string $catalogId, string $productUuid): array
}

/**
* @return array<array-key, mixed>
* @return array<array-key, RawMappedProduct>
*
* @throws CatalogDisabledException
* @throws PimApiException
Expand Down Expand Up @@ -229,7 +252,7 @@ public function getMappedProducts(
}

/**
* @return array<mixed>
* @return RawMappedProduct
*
* @throws CatalogDisabledException
* @throws PimApiException
Expand All @@ -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;
}
}
34 changes: 18 additions & 16 deletions src/Query/FetchMappedProductQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -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<string>', $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);
Expand Down
12 changes: 4 additions & 8 deletions src/Query/FetchMappedProductsQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -26,12 +21,13 @@ public function __construct(private readonly PimCatalogApiClient $catalogApiClie
*/
public function fetch(string $catalogId): array
{
/** @var array<RawMappedProduct> $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);
Expand Down
121 changes: 93 additions & 28 deletions src/Service/InitializeAppData.php
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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;
}
Expand Down
25 changes: 17 additions & 8 deletions templates/macros/displayAttribute.html.twig
Original file line number Diff line number Diff line change
@@ -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') %}
<a href="{{ value }}" target="_blank">{{ value }}</a>
{% elseif type matches '{^array<.+>$}' %}
{% set itemType = type|slice(6, type|length - 7) %}
{% for itemValue in value %}
{{ _self.formatValue(itemValue, itemType) }}{% if not loop.last %}<br />{% endif %}
{% endfor %}
{% else %}
{{ value }}
{% endif %}
{% endif %}

{{ displayValue }}
{% endmacro %}
2 changes: 1 addition & 1 deletion templates/product.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
{% for attribute in product.attributes %}
<div class="attribute">
<h3 class="attribute__label">{{ attribute.label | trans }}</h3>
<p class="attribute__value">{{ displayAttribute.value(attribute) }}</p>
<p class="attribute__value">{{ displayAttribute.formatValue(attribute.value, attribute.type) }}</p>
</div>
{% endfor %}
</section>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
{
"uuid": "a5eed606-4f98-4d8c-b926-5b59f8fb0ee7"
"uuid": "a5eed606-4f98-4d8c-b926-5b59f8fb0ee7",
"name": "Kodak i2600 for Govt",
"sku": "1234567890317"
}
17 changes: 14 additions & 3 deletions tests/Fixtures/responses/get-catalogs-mapped-product-scanner.json
Original file line number Diff line number Diff line change
@@ -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
}
Loading

0 comments on commit dc42ad3

Please sign in to comment.