From a9346215ed2542ac0a6ae8db82d7d00c1587edcf Mon Sep 17 00:00:00 2001 From: Pedro Bernardina Date: Wed, 29 Jan 2025 14:22:10 -0300 Subject: [PATCH] feat: add "advancedConfigs" to PDP and PLP loaders (#996) --- .../intelligentSearch/productDetailsPage.ts | 12 ++++++- .../intelligentSearch/productListingPage.ts | 8 +++++ vtex/loaders/intelligentSearch/suggestions.ts | 7 ++++ vtex/loaders/legacy/productDetailsPage.ts | 8 ++++- vtex/loaders/legacy/productListingPage.ts | 9 +++++ vtex/utils/pickAndOmit.ts | 30 ++++++++++++++++ vtex/utils/transform.ts | 36 ++++++++++++++++++- vtex/utils/types.ts | 7 ++++ 8 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 vtex/utils/pickAndOmit.ts diff --git a/vtex/loaders/intelligentSearch/productDetailsPage.ts b/vtex/loaders/intelligentSearch/productDetailsPage.ts index 9faf76c88..03f8339ba 100644 --- a/vtex/loaders/intelligentSearch/productDetailsPage.ts +++ b/vtex/loaders/intelligentSearch/productDetailsPage.ts @@ -11,7 +11,11 @@ import { pageTypesToSeo } from "../../utils/legacy.ts"; import { getSegmentFromBag, withSegmentCookie } from "../../utils/segment.ts"; import { withIsSimilarTo } from "../../utils/similars.ts"; import { pickSku, toProductPage } from "../../utils/transform.ts"; -import type { PageType, Product as VTEXProduct } from "../../utils/types.ts"; +import type { + AdvancedLoaderConfig, + PageType, + Product as VTEXProduct, +} from "../../utils/types.ts"; import PDPDefaultPath from "../paths/PDPDefaultPath.ts"; export interface Props { @@ -26,6 +30,11 @@ export interface Props { * @description Index of product pages with the `skuId` parameter */ indexingSkus?: boolean; + /** + * @title Advanced Configuration + * @description Further change loader behaviour + */ + advancedConfigs?: AdvancedLoaderConfig; } /** @@ -130,6 +139,7 @@ const loader = async ( const page = toProductPage(product, sku, kitItems, { baseUrl, priceCurrency: segment?.payload?.currencyCode ?? "BRL", + includeOriginalAttributes: props.advancedConfigs?.includeOriginalAttributes, }); const isPageProduct = pageType.pageType === "Product"; diff --git a/vtex/loaders/intelligentSearch/productListingPage.ts b/vtex/loaders/intelligentSearch/productListingPage.ts index 4c3bb6830..38bd24557 100644 --- a/vtex/loaders/intelligentSearch/productListingPage.ts +++ b/vtex/loaders/intelligentSearch/productListingPage.ts @@ -26,6 +26,7 @@ import { toProduct, } from "../../utils/transform.ts"; import type { + AdvancedLoaderConfig, Facet, Fuzzy, PageType, @@ -142,6 +143,11 @@ export interface Props { * @title Include price in facets */ priceFacets?: boolean; + /** + * @title Advanced Configuration + * @description Further change loader behaviour + */ + advancedConfigs?: AdvancedLoaderConfig; } const searchArgsOf = (props: Props, url: URL) => { const hideUnavailableItems = props.hideUnavailableItems; @@ -362,6 +368,8 @@ const loader = async ( toProduct(p, p.items.find(getFirstItemAvailable) || p.items[0], 0, { baseUrl: baseUrl, priceCurrency: segment?.payload?.currencyCode ?? "BRL", + includeOriginalAttributes: props.advancedConfigs + ?.includeOriginalAttributes, }) ) .map((product) => diff --git a/vtex/loaders/intelligentSearch/suggestions.ts b/vtex/loaders/intelligentSearch/suggestions.ts index 4fbc21dae..3b442d5b9 100644 --- a/vtex/loaders/intelligentSearch/suggestions.ts +++ b/vtex/loaders/intelligentSearch/suggestions.ts @@ -9,6 +9,7 @@ import { import { getSegmentFromBag, withSegmentCookie } from "../../utils/segment.ts"; import { withIsSimilarTo } from "../../utils/similars.ts"; import { toProduct } from "../../utils/transform.ts"; +import type { AdvancedLoaderConfig } from "../../utils/types.ts"; export interface Props { query?: string; @@ -23,6 +24,11 @@ export interface Props { * @deprecated Use product extensions instead */ similars?: boolean; + /** + * @title Advanced Configuration + * @description Further change loader behaviour + */ + advancedConfigs?: AdvancedLoaderConfig; } /** @@ -79,6 +85,7 @@ const loaders = async ( const options = { baseUrl: url, priceCurrency: segment?.payload?.currencyCode ?? "BRL", + includeOriginalAttributes: props.advancedConfigs?.includeOriginalAttributes, }; return { diff --git a/vtex/loaders/legacy/productDetailsPage.ts b/vtex/loaders/legacy/productDetailsPage.ts index b2eed191f..73941baa2 100644 --- a/vtex/loaders/legacy/productDetailsPage.ts +++ b/vtex/loaders/legacy/productDetailsPage.ts @@ -6,7 +6,7 @@ import { toSegmentParams } from "../../utils/legacy.ts"; import { getSegmentFromBag, withSegmentCookie } from "../../utils/segment.ts"; import { withIsSimilarTo } from "../../utils/similars.ts"; import { pickSku, toProductPage } from "../../utils/transform.ts"; -import type { LegacyProduct } from "../../utils/types.ts"; +import type { AdvancedLoaderConfig, LegacyProduct } from "../../utils/types.ts"; import PDPDefaultPath from "../paths/PDPDefaultPath.ts"; export interface Props { @@ -22,6 +22,11 @@ export interface Props { * @description Index of product pages with the `skuId` parameter */ indexingSkus?: boolean; + /** + * @title Advanced Configuration + * @description Further change loader behaviour + */ + advancedConfigs?: AdvancedLoaderConfig; } /** @@ -86,6 +91,7 @@ async function loader( const page = toProductPage(product, sku, kitItems, { baseUrl, priceCurrency: segment?.payload?.currencyCode ?? "BRL", + includeOriginalAttributes: props.advancedConfigs?.includeOriginalAttributes, }); return { diff --git a/vtex/loaders/legacy/productListingPage.ts b/vtex/loaders/legacy/productListingPage.ts index 64e4baeb2..0af37f355 100644 --- a/vtex/loaders/legacy/productListingPage.ts +++ b/vtex/loaders/legacy/productListingPage.ts @@ -16,6 +16,7 @@ import { withIsSimilarTo } from "../../utils/similars.ts"; import { parsePageType } from "../../utils/transform.ts"; import { legacyFacetToFilter, toProduct } from "../../utils/transform.ts"; import type { + AdvancedLoaderConfig, Item, LegacyFacet, LegacyProduct, @@ -92,6 +93,12 @@ export interface Props { * @title Ignore case by checking for selected filter */ ignoreCaseSelected?: boolean; + + /** + * @title Advanced Configuration + * @description Further change loader behaviour + */ + advancedConfigs?: AdvancedLoaderConfig; } export const sortOptions = [ @@ -277,6 +284,8 @@ const loader = async ( { baseUrl, priceCurrency: segment?.payload?.currencyCode ?? "BRL", + includeOriginalAttributes: props.advancedConfigs + ?.includeOriginalAttributes, }, ) ) diff --git a/vtex/utils/pickAndOmit.ts b/vtex/utils/pickAndOmit.ts new file mode 100644 index 000000000..b11431737 --- /dev/null +++ b/vtex/utils/pickAndOmit.ts @@ -0,0 +1,30 @@ +export function pick< + T extends object, + K extends keyof T = keyof T, +>( + keys: K[], + obj: T | null | undefined, +): Pick | null { + if (!keys.length || !obj) { + return null; + } + + const entries = keys.map((key) => [key, obj[key]]); + + return Object.fromEntries(entries); +} + +export function omit( + keys: K[], + obj: T | null | undefined, +): Omit | null { + if (!keys.length || !obj) { + return null; + } + + const pickedKeys = (Object.keys(obj) as K[]).filter( + (key) => !keys.includes(key), + ); + + return pick(pickedKeys, obj) as unknown as Omit; +} diff --git a/vtex/utils/transform.ts b/vtex/utils/transform.ts index 2686ce40d..0592c84e8 100644 --- a/vtex/utils/transform.ts +++ b/vtex/utils/transform.ts @@ -19,6 +19,7 @@ import type { import { DEFAULT_IMAGE } from "../../commerce/utils/constants.ts"; import { formatRange } from "../../commerce/utils/filters.ts"; import type { PickupPoint as PickupPointVCS } from "./openapi/vcs.openapi.gen.ts"; +import { pick } from "./pickAndOmit.ts"; import { slugify } from "./slugify.ts"; import type { Brand as BrandVTEX, @@ -29,7 +30,9 @@ import type { Item as SkuVTEX, LegacyFacet, LegacyItem as LegacySkuVTEX, + LegacyProduct, LegacyProduct as LegacyProductVTEX, + Maybe, OrderForm, PageType as PageTypeVTEX, PickupHolidays, @@ -81,6 +84,8 @@ interface ProductOptions { /** Price coded currency, e.g.: USD, BRL */ priceCurrency: string; imagesByKey?: Map; + /** Original attributes to be included in the transformed product */ + includeOriginalAttributes?: string[]; } /** Returns first available sku */ @@ -363,6 +368,11 @@ export const toProduct =

( const groupAdditionalProperty = isLegacyProduct(product) ? legacyToProductGroupAdditionalProperties(product) : toProductGroupAdditionalProperties(product); + const originalAttributesAdditionalProperties = + toOriginalAttributesAdditionalProperties( + options.includeOriginalAttributes, + product, + ); const specificationsAdditionalProperty = isLegacySku(sku) ? toAdditionalPropertiesLegacy(sku) : toAdditionalProperties(sku); @@ -383,7 +393,10 @@ export const toProduct =

( ), url: getProductGroupURL(baseUrl, product).href, name: product.productName, - additionalProperty: groupAdditionalProperty, + additionalProperty: [ + ...groupAdditionalProperty, + ...originalAttributesAdditionalProperties, + ], model: productReference, } satisfies ProductGroup) : undefined; @@ -554,6 +567,27 @@ const toProductGroupAdditionalProperties = ( ) ); +const toOriginalAttributesAdditionalProperties = ( + originalAttributes: Maybe, + product: ProductVTEX | LegacyProduct, +) => { + if (!originalAttributes) { + return []; + } + + const attributes = + pick(originalAttributes as Array, product) ?? {}; + + return Object.entries(attributes).map(([name, value]) => + ({ + "@type": "PropertyValue", + name, + value, + valueReference: "ORIGINAL_PROPERTY" as string, + }) as const + ) as unknown as PropertyValue[]; +}; + const toAdditionalProperties = (sku: SkuVTEX): PropertyValue[] => sku.variations?.flatMap(({ name, values }) => values.map((value) => toAdditionalPropertySpecification({ name, value })) diff --git a/vtex/utils/types.ts b/vtex/utils/types.ts index 057529113..c87b84357 100644 --- a/vtex/utils/types.ts +++ b/vtex/utils/types.ts @@ -1516,3 +1516,10 @@ export interface Promotion { maxNumberOfAffectedItems: number; maxNumberOfAffectedItemsGroupKey: string; } + +export interface AdvancedLoaderConfig { + /** @description Specifies an array of attribute names from the original object to be directly included in the transformed object. */ + includeOriginalAttributes: string[]; +} + +export type Maybe = T | null | undefined;