Product normalizers
Product includes two normalizers:
normalizeProduct
: This function is used to map SFCCProduct
intoSfProduct
, which includes a full product detailsnormalizeProductCatalogItem
: This function is used to map SFCCProduct
intoSfProductCatalogItem
, which includes only basic product details, needed to display a product in a product catalog
Parameters
normalizeProduct
Name | Type | Default value | Description |
---|---|---|---|
context | NormalizerContext | Context needed for the normalizer. Context contain a currency field that contains a currency code | |
product | Product | SFCC Product |
normalizeProductCatalogItem
Name | Type | Default value | Description |
---|---|---|---|
context | NormalizerContext | Context needed for the normalizer. Context contain a currency field that contains a currency code | |
product | ProductSearchHit or Product | SFCC ProductSearchHit or 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
with manufacturerSku
field which is available on SFCC Product and normalizeProductCatalogItem
with productType
field.
import { normalizers } from "@vsf-enterprise/unified-api-sfcc";
export const unifiedApiExtension = createUnifiedExtension({
normalizers: {
addCustomFields: [
{
normalizeProduct: (context, product) => {
const { quantityLimit } = normalizers.normalizeProduct(context, product);
return {
manufacturerSku: product.manufacturerSku,
isSmallQuantityLimit: quantityLimit && quantityLimit <= 10,
}
},
normalizeProductCatalogItem: (context, product) => ({
productType: product.productType,
}),
},
],
},
config: {
...
},
});
Since both normalizers accept SFCC Product
as a second argument, you may also use the same approach to use same representation for both SfProduct
and SfProductCatalogItem
models.
However, in this case you should be aware that SfProductCatalogItem
will contain all the fields from SfProduct
model and, it will affect the performance of the catalog page.
Source
The normalizeProduct
and normalizeProductCatalogItem
function consists of several smaller normalizers such as normalizeImage
, normalizeDiscountablePrice
, and more, which you can override as well.
import { getImageGroupByViewType } from "@/normalizers/__internal__";
import type { ImageGroup, Product } from "@internal";
import type { NormalizerContext } from "../types";
import type { SfAttribute, SfImage } from "@vue-storefront/unified-data-model";
import sanitizeHtml from "sanitize-html";
import { defineNormalizer } from "../defineNormalizer";
import { normalizeProductCatalogItemFromProduct } from "./productCatalog";
import { normalizeProductVariant } from "./variant";
// eslint-disable-next-line complexity
export const normalizeProduct = defineNormalizer.normalizeProduct((context, product) => {
const baseFields = normalizeProductCatalogItemFromProduct(context, product);
const description = product.longDescription
? sanitizeHtml(product.longDescription)
: product.shortDescription
? sanitizeHtml(product.shortDescription)
: null;
let attributes: SfAttribute[] = [];
// variationValues exists only for type.variant or type.item
if (product.variationValues && product.variationAttributes) {
attributes = getAttributes(context, product);
}
return {
...baseFields,
attributes,
description,
gallery: getGallery(context, product.imageGroups ?? []),
variants:
product.variants?.map((variant) =>
normalizeProductVariant(context, {
variant,
variationAttributes: product.variationAttributes ?? [],
}),
) ?? [],
};
});
function getGallery(context: NormalizerContext, images: ImageGroup[]): SfImage[] {
if (!images?.length) {
return [];
}
const imageGroup = getImageGroupByViewType(images, "large") ?? images[0];
return imageGroup?.images.map((image) => context.normalizers.normalizeImage(image)) ?? [];
}
function getAttributes(context: NormalizerContext, product: Product): SfAttribute[] {
const { variationValues, variationAttributes } = product;
if (!variationValues || !variationAttributes) {
return [];
}
return Object.entries(variationValues)
.map(([key, value]) => {
const attribute = { key, value };
return context.normalizers.normalizeAttribute({ attribute, variationAttributes });
})
.filter(Boolean);
}