2 min read

Enrich contracts with custom data

This article based on Poq.ApiTemplate.Node, it would be easier to understand if you set up you environment according to the guide: Your first BFC

This type of customization is mostly used across projects and there are at least a few ways how to do that. Given examples will help you to understand when and how to do that.

First of all, let's define when and how question to describe the use case of contract customization:

When do we need to enrich existing contracts with custom data?

By default the Poq contracts carry essential information, however, in some cases extension is required to provide a great user experience for clients.

Let's imagine you want to add indicators to the product that depicts whether:

  • it contains lead;
  • it's certified according to some standard ISO-XXXX;

These specific requirements could be reached in several ways: we can add this information in the description to a product, but if the specific indication is necessary then the most suitable option would be to extend the contract with two fields accordingly.

Follow the next section to find out how to do that.

How to enrich/extend an existing contracts?

Let's explore the structure of product contract first:

export declare class Product extends PoqContract {
readonly id: string;
readonly appIdentifier: string;
readonly details: ProductDetails;
readonly variants: Map<string, ProductVariant>;
readonly categoryIds: string[] | undefined;
readonly forms: Form[] | undefined;
readonly meta: ProductMeta | undefined;
readonly promotion: Promotion | undefined;
readonly review: CatalogueReview | undefined;
readonly customData: Record<string, unknown> | undefined;
}

Mobile application (default configuration) expects data to be presented in the form above. In that case we can add new fields by:

  • Using standard OOP inheritance and extends the Product contract (further: Inheritance);
  • Using customData field to store necessary information in there (further: CustomData);
ApproachProsCons
InheritanceStandard OOPMobile client should be updated to support any new field
Explicit semantics
Explicit contract structure
CustomDataMobile client works with field out of the box (OOB)Implicit structure. No strict typing
Easy to extend with values

Both ways are fine, however, CustomData way is easier to utilize in practise.

Example

Task:

  1. As a user I want to see whether a product contains lead (on Product Details Page - PDP).
  2. As a user I want to see whether a product is certified by ISO-XXXX (PDP).

Let's assume your project is set up and ready for development, the ApiTemplate.Node is used to create the project.

In your IDE explore ./src/modules/product folder (Expected folder tree is below).

./src/modules/product
├── controllers
│   ├── __tests__
│   │   └── product-controller.integration-spec.ts
│   ├── index.ts
│   └── product.controller.ts
├── dependency.factories.ts
├── index.ts
├── mappers
│   ├── __tests__
│   │   └── product-mapper.spec.ts
│   ├── index.ts
│   ├── interfaces
│   │   ├── index.ts
│   │   └── product-mapper.interface.ts
│   └── product.mapper.ts
├── product.module.ts
└── services
├── __tests__
│   └── product-service.spec.ts
├── index.ts
├── interfaces
│   ├── index.ts
│   └── product-service.interface.ts
└── product.service.ts

The first step is to identify what contracts we are going to extend. The default set of contracts is provided by @poq/sdk package, see: SDK API. We're looking for a Product Contract and endpoints to check. Endpoints can be found in product.controller.ts file, the controller is using product.service.ts to return products to an end-user. If you open product.service.ts you'll find that there are product.mapper.ts in charge of forming product shape.

Open file of given product.mapper.ts and check section with creation of Product.

// This code is relevant to NODE ApiTemplate for SFCC-Based Apps.
// This is an example how-to add custom fields to custom data field
// and only gives you an example how it could look like.
export class ProductMapper implements IProductMapper {
...
// Method ProductMapper#convertVariantsToPoqProduct
convertVariantsToPoqProduct(appIdentifier: string, appConfig: AppConfig, sfccVariants: SfccProduct[]): Product {
// Here we assume if at least one product contains lead we can say it for all variants
const containsLead = sfccVariants.some((variant) => Boolean(variant.getPropertyValue('c_containsLead')));
// Here we assume if at least on product certified then all variants follow the same standard
const certified = sfccVariants.find((variant) => variant.getPropertyValue('c_certified')) ?? 'None';
...
return new Product({
...,
customData: {
containsLead,
certified
}
});
}
...
}

In this example we assume the custom fields are available on your Salesforce cloud instance.

That's it. You added new fields to the contract.

For Salesforce objects are common to use c_ prefix before the custom field.

In SDK SFCC to read/write custom field values we could use getPropertyValue/setPropertyValue accordingly.

Check more detail here: API SDK SFCC.