Vue Storefront is now Alokai! Learn More
Attributes normalizer

Attributes normalizer

Attributes are Unified Data Layer representation of Magento option. The normalizeAttribute function maps the variant's attributes into an array of SfAttribute.

Parameters

NameTypeDefault valueDescription
contextNormalizerContextcontext needed for the normalizer
attributeSelectedConfigurableOption, OrderItemOption or ConfigurableAttributeOptionMagento option

Extending

The SfAttribute is returned as a part of SfProduct, SfOrder and SfCart. If you want to extend the SfAttribute with custom fields, you should use the addCustomFields API.

import { normalizers } from "@vsf-enterprise/unified-api-magento";

export const unifiedApiExtension = createUnifiedExtension({
  normalizers: {
    addCustomFields: [
      {
        normalizeAttribute: (context, rawAttribute) => {
          const normalizedAttribute = normalizers.normalizeAttribute(context, rawAttribute);
          
          if (normalizedAttribute) {
            return {
              someNewField: "someValue",
            };
          }
          
          return null;
        },
      },
    ],
  },
  config: {
    ...
  },
});

Source

Please note that the normalizeAttribute has a different type depending on the context in which it is used. We use a type guard to determine the type of the attribute.

attributes.ts
/* eslint-disable complexity */
import type {
  ConfigurableProductOptions,
  ConfigurableVariant,
  OrderItemOption,
  SelectedConfigurableOption,
} from "@vue-storefront/magento-types";
import type { SfAttribute, SfProduct } from "@vue-storefront/unified-data-model";
import { defineNormalizer } from "../defineNormalizer";
import type { NormalizeAttributeInput, NormalizerContext } from "../types";

const isSelectedConfigurableOption = (
  input: NormalizeAttributeInput,
): input is SelectedConfigurableOption =>
  !!(input as SelectedConfigurableOption).configurable_product_option_value_uid;

const isOrderItemOption = (input: NormalizeAttributeInput): input is OrderItemOption =>
  !isSelectedConfigurableOption(input) && !!(input as OrderItemOption).label;

export const normalizeAttribute = defineNormalizer.normalizeAttribute((context, attribute) => {
  if (isSelectedConfigurableOption(attribute)) {
    return normalizeConfigurableOption(attribute);
  } else if (isOrderItemOption(attribute)) {
    return normalizeOrderAttribute(attribute);
  }

  const { attributeOption, configurableOptions } = attribute;
  const configurableAttribute = configurableOptions.find(
    (option) => option.attribute_code === attributeOption?.code,
  );

  if (!configurableAttribute) {
    return null;
  }

  return {
    name: configurableAttribute.attribute_code!,
    label: configurableAttribute.label ?? configurableAttribute.uid!,
    value: attributeOption.uid,
    valueLabel: attributeOption.label ?? attributeOption.uid,
  };
});

export function normalizeAttributes<
  WithConfigurableAttributes extends Pick<ConfigurableVariant, "attributes">,
>(
  variant: WithConfigurableAttributes,
  configurableOptions: ConfigurableProductOptions[],
  ctx: NormalizerContext,
): SfProduct["attributes"] {
  const attributes = variant.attributes;
  if (!attributes || !configurableOptions?.length) {
    return [];
  }
  return attributes
    .filter(Boolean)
    .map((attributeOption) =>
      ctx.normalizers.normalizeAttribute({ attributeOption, configurableOptions }),
    )
    .filter(Boolean);
}

function normalizeConfigurableOption(option: SelectedConfigurableOption): SfAttribute {
  return {
    label: option.option_label,
    name: option.option_label,
    value: option.configurable_product_option_value_uid,
    valueLabel: option.value_label,
  };
}

function normalizeOrderAttribute(option: OrderItemOption): SfAttribute {
  return {
    name: option.label,
    value: option.value,
    label: option.label,
    valueLabel: option.value,
  };
}