Product normalizers
Product includes two normalizers:
normalizeProduct: This function is used to map MagentoProductintoSfProduct, which includes a full product detailsnormalizeProductCatalogItem: This function is used to map MagentoProductintoSfProductCatalogItem, which includes only basic product details, needed to display a product in a product catalog
Parameters
normalizeProduct
| Name | Type | Default value | Description |
|---|---|---|---|
context | NormalizeProductContext | Context needed for the normalizer. sku is added to specify a product variant | |
product | ProductWithTypeName ProductInterface with __typename | Magento Product |
normalizeProductCatalogItem
| Name | Type | Default value | Description |
|---|---|---|---|
context | NormalizerContext | context needed for the normalizer | |
product | ProductWithTypeName ProductInterface with __typename | Magento Product |
Extending
The SfProduct model is returned from GetProductDetails method. The SfProductCatalogItem model is returned from GetProducts method. If any of these models don't contain the information you need for your Storefront, you can extend its logic using the addCustomFields API. In the following example we extend the normalizeProduct and normalizeProductCatalogItem with countryOfManufacture field which is available on Magento Product.
import { normalizers } from "@vsf-enterprise/unified-api-magento";
export const unifiedApiExtension = createUnifiedExtension({
normalizers: {
addCustomFields: [
{
normalizeProduct: (context, product) => {
const { quantityLimit } = normalizers.normalizeProduct(context, product);
return {
countryOfManufacture: product.country_of_manufacture,
isSmallQuantityLimit: quantityLimit && quantityLimit <= 10,
}
},
normalizeProductCatalogItem: (context, product) => ({
countryOfManufacture: product.country_of_manufacture,
}),
},
],
},
config: {
...
},
});
Source
The normalizeProduct and normalizeProductCatalogItem function consists of several smaller normalizers such as normalizeImage, normalizeRating normalizeDiscountablePrice, and more, which you can override as well.
product.ts
import type { ConfigurableVariant, ProductWithTypeName } from "@/ecommerceTypes";
import sanitizeHtml from "sanitize-html";
import { isConfigurableProduct } from "@/ecommerceTypes";
import { defineNormalizer } from "../defineNormalizer";
import type { NormalizersTypes, NormalizerContext } from "../types";
import { normalizeProductInterface } from "./productCatalog";
import { normalizeProductVariant } from "./variant";
export const normalizeProduct = defineNormalizer.normalizeProduct((context, product) => {
const currentVariant = getVariant(product, context.sku);
const description = product.description?.html ? sanitizeHtml(product.description.html) : null;
const gallery =
currentVariant.product?.media_gallery
?.filter(Boolean)
.map((img) => context.normalizers.normalizeImage(img)) ?? [];
return {
...normalizeProductInterface(context, product, currentVariant?.product),
attributes: getAttributes(context, product, currentVariant),
description,
gallery,
variants: getVariants(context, product),
};
});
function getAttributes(
context: NormalizerContext,
product: ProductWithTypeName,
currentVariant: { attributes: ConfigurableVariant["attributes"] },
) {
if (!isConfigurableProduct(product)) {
return [];
}
const configurableOptions = (product.configurable_options ?? []).filter(Boolean);
const attributes = currentVariant.attributes;
if (!attributes || !configurableOptions?.length) {
return [];
}
return attributes
.filter(Boolean)
.map((attributeOption) =>
context.normalizers.normalizeAttribute({ attributeOption, configurableOptions }),
)
.filter(Boolean);
}
function getVariant(product: ProductWithTypeName, sku?: string) {
if (!isConfigurableProduct(product)) {
return { product, attributes: [] };
}
const variant = product.variants?.find((variant) => variant?.product?.sku === sku);
if (!variant?.product) {
return { product, attributes: [] };
}
return { product: variant.product, attributes: variant.attributes?.filter(Boolean) ?? [] };
}
function getVariants(context: NormalizerContext, product: NormalizersTypes.NormalizeProductInput) {
if (isConfigurableProduct(product)) {
const configurable_options = (product.configurable_options ?? []).filter(Boolean);
return (product.variants ?? [])
.filter(Boolean)
.map((variant) => normalizeProductVariant(context, variant, configurable_options));
}
return [];
}