Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin' into catalog-delete
Browse files Browse the repository at this point in the history
  • Loading branch information
dylandepass committed Nov 14, 2024
2 parents c71243b + 653d02a commit 69cc036
Show file tree
Hide file tree
Showing 17 changed files with 264 additions and 123 deletions.
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,31 @@
## [1.6.3](https://github.com/adobe-rnd/helix-commerce-api/compare/v1.6.2...v1.6.3) (2024-11-14)


### Bug Fixes

* shortdescription instead of description ([#47](https://github.com/adobe-rnd/helix-commerce-api/issues/47)) ([c325894](https://github.com/adobe-rnd/helix-commerce-api/commit/c325894ff56f6942faf44993d485399535ae1b5e))

## [1.6.2](https://github.com/adobe-rnd/helix-commerce-api/compare/v1.6.1...v1.6.2) (2024-11-13)


### Bug Fixes

* brand, itemCondition and hasMerchantReturnPolicy ([#46](https://github.com/adobe-rnd/helix-commerce-api/issues/46)) ([fe6970e](https://github.com/adobe-rnd/helix-commerce-api/commit/fe6970e5d8de48303fff33573bd24fc099b404c6))

## [1.6.1](https://github.com/adobe-rnd/helix-commerce-api/compare/v1.6.0...v1.6.1) (2024-11-12)


### Bug Fixes

* various stuff ([#45](https://github.com/adobe-rnd/helix-commerce-api/issues/45)) ([a77f3d5](https://github.com/adobe-rnd/helix-commerce-api/commit/a77f3d577ff30c10a18721def3630576b8424611))

# [1.6.0](https://github.com/adobe-rnd/helix-commerce-api/compare/v1.5.5...v1.6.0) (2024-11-11)


### Features

* add ratings to json-ld ([#44](https://github.com/adobe-rnd/helix-commerce-api/issues/44)) ([76858f4](https://github.com/adobe-rnd/helix-commerce-api/commit/76858f480bd826cdfebcbab3dff348e03787e8ae))

## [1.5.5](https://github.com/adobe-rnd/helix-commerce-api/compare/v1.5.4...v1.5.5) (2024-11-07)


Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "helix-commerce-api",
"version": "1.5.5",
"version": "1.6.3",
"private": true,
"description": "API for markup content and a commerce graphql commerce proxy",
"main": "src/index.js",
Expand Down
2 changes: 1 addition & 1 deletion src/content/adobe-commerce.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ async function fetchProduct(sku, config) {
if (!productData) {
throw errorWithResponse(404, 'could not find product', json.errors);
}
const product = productAdapter(productData);
const product = productAdapter(config, productData);
return product;
} catch (e) {
console.error('failed to parse product: ', e);
Expand Down
2 changes: 1 addition & 1 deletion src/content/handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export default async function contentHandler(ctx) {
if (!config.pageType) {
return errorResponse(400, 'invalid config for tenant site (missing pageType)');
}
console.log('config: ', JSON.stringify(config, null, 2));
ctx.log.debug('config: ', JSON.stringify(config, null, 2));

if (config.catalogSource === 'helix') {
return handleHelixCommerce(ctx);
Expand Down
20 changes: 16 additions & 4 deletions src/content/queries/cs-product.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@
*/

import { forceImagesHTTPS } from '../../utils/http.js';
import { gql, parseSpecialToDate } from '../../utils/product.js';
import { gql, parseRating, parseSpecialToDate } from '../../utils/product.js';

/**
* @param {Config} config
* @param {any} productData
* @returns {Product}
*/
export const adapter = (productData) => {
export const adapter = (config, productData) => {
let minPrice = productData.priceRange?.minimum ?? productData.price;
let maxPrice = productData.priceRange?.maximum ?? productData.price;

Expand All @@ -42,6 +43,8 @@ export const adapter = (productData) => {
externalId: productData.externalId,
images: forceImagesHTTPS(productData.images) ?? [],
attributes: productData.attributes ?? [],
attributeMap: Object.fromEntries((productData.attributes ?? [])
.map(({ name, value }) => [name, value])),
options: (productData.options ?? []).map((option) => ({
id: option.id,
label: option.title,
Expand Down Expand Up @@ -76,25 +79,34 @@ export const adapter = (productData) => {
currency: minPrice.regular.amount.currency,
maximumAmount: maxPrice.regular.amount.value,
minimumAmount: minPrice.regular.amount.value,
// TODO: add variant?
},
final: {
// TODO: determine whether to use min or max
amount: minPrice.final.amount.value,
currency: minPrice.final.amount.currency,
maximumAmount: maxPrice.final.amount.value,
minimumAmount: minPrice.final.amount.value,
// TODO: add variant?
},
visible: minPrice.roles?.includes('visible'),
} : null,
};

if (config.attributeOverrides?.product) {
Object.entries(config.attributeOverrides.product).forEach(([key, value]) => {
product.attributeMap[key] = product.attributeMap[value] ?? product[key];
});
}

const specialToDate = parseSpecialToDate(product);
if (specialToDate) {
product.specialToDate = specialToDate;
}

const rating = parseRating(product);
if (rating) {
product.rating = rating;
}

return product;
};

Expand Down
20 changes: 14 additions & 6 deletions src/content/queries/cs-variants.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@
*/

import { forceImagesHTTPS } from '../../utils/http.js';
import { gql, parseSpecialToDate } from '../../utils/product.js';
import { gql, parseRating, parseSpecialToDate } from '../../utils/product.js';

/**
* @param {Config} config
* @param {any} variants
* @returns {Variant[]}
*/
Expand All @@ -30,6 +31,8 @@ export const adapter = (config, variants) => variants.map(({ selections, product
inStock: product.inStock,
images: forceImagesHTTPS(product.images) ?? [],
attributes: product.attributes ?? [],
attributeMap: Object.fromEntries((product.attributes ?? [])
.map(({ name, value }) => [name, value])),
externalId: product.externalId,
prices: {
regular: {
Expand All @@ -50,15 +53,20 @@ export const adapter = (config, variants) => variants.map(({ selections, product
selections: (selections ?? []).sort(),
};

const specialToDate = parseSpecialToDate(product);
if (config.attributeOverrides?.variant) {
Object.entries(config.attributeOverrides.variant).forEach(([key, value]) => {
variant.attributeMap[key] = variant.attributeMap[value] ?? variant[key];
});
}

const specialToDate = parseSpecialToDate(variant);
if (specialToDate) {
variant.specialToDate = specialToDate;
}

if (config.attributeOverrides?.variant) {
Object.entries(config.attributeOverrides.variant).forEach(([key, value]) => {
variant[key] = product.attributes?.find((attr) => attr.name === value)?.value;
});
const rating = parseRating(variant);
if (rating) {
variant.rating = rating;
}

return variant;
Expand Down
4 changes: 2 additions & 2 deletions src/templates/html/overrides/wilson-ecommerce--wilson.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ export default class extends HTMLTemplate {
*/
constructor(ctx, product, variants) {
super(ctx, product, variants);
// use description field for meta description, if not explicitly set
this.product.metaDescription = this.product.metaDescription || this.product.description;
// use shortDescription field for meta description, if not explicitly set
this.product.metaDescription = this.product.metaDescription || this.product.shortDescription;
}

/**
Expand Down
133 changes: 76 additions & 57 deletions src/templates/json/JSONTemplate.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,17 @@ export class JSONTemplate {
* @param {Variant} [variant]
*/
constructMPN(variant) {
const { attributeMap: productAttrs } = this.product;
const { attributeMap: variantAttrs } = variant || {};

return variant
? variant.attributes.find((attr) => attr.name.toLowerCase() === 'mpn')?.value ?? this.constructMPN()
: this.product.attributes.find((attr) => attr.name.toLowerCase() === 'mpn')?.value ?? undefined;
? variantAttrs.mpn ?? this.constructMPN()
: productAttrs.mpn ?? undefined;
}

renderBrand() {
const { attributes } = this.product;
const brandName = attributes?.find((attr) => attr.name === 'brand')?.value;
const { attributeMap: attrs } = this.product;
const brandName = attrs.brand;
if (!brandName) {
return undefined;
}
Expand All @@ -91,53 +94,71 @@ export class JSONTemplate {
};
}

/**
* @param {Variant} [variant]
*/
renderRating(variant) {
const { rating } = variant || this.product;
if (!rating) {
return undefined;
}

const {
count,
reviews,
value,
best,
worst,
} = rating;
return pruneUndefined({
'@type': 'AggregateRating',
ratingValue: value,
ratingCount: count,
reviewCount: reviews,
bestRating: best,
worstRating: worst,
});
}

renderOffers() {
const image = this.product.images?.[0]?.url
?? findProductImage(this.product, this.variants)?.url;
const configurableProduct = this.variants?.length > 0;
const offers = configurableProduct ? this.variants : [this.product];
return {
offers: [
...offers.map((v) => {
const { prices: variantPrices } = v;
const offerUrl = this.constructProductURL(configurableProduct ? v : undefined);
const mpn = this.constructMPN(configurableProduct ? v : undefined);
const finalPrice = variantPrices?.final?.amount;
const regularPrice = variantPrices?.regular?.amount;

const offer = {
'@type': 'Offer',
sku: v.sku,
mpn,
url: offerUrl,
image: v.images?.[0]?.url ?? image,
availability: v.inStock ? 'InStock' : 'OutOfStock',
price: finalPrice,
priceCurrency: variantPrices.final?.currency,
};

if (finalPrice < regularPrice) {
offer.priceSpecification = this.renderOffersPriceSpecification(v);
}

if (v.gtin) {
offer.gtin = v.gtin;
}

if (v.specialToDate) {
offer.priceValidUntil = v.specialToDate;
}

return pruneUndefined(offer);
}).filter(Boolean),
],
};
return offers.map((v) => {
const { prices: variantPrices } = v;
const offerUrl = this.constructProductURL(configurableProduct ? v : undefined);
const mpn = this.constructMPN(configurableProduct ? v : undefined);
const finalPrice = variantPrices?.final?.amount;
const regularPrice = variantPrices?.regular?.amount;

const offer = {
'@type': 'Offer',
sku: v.sku,
mpn,
url: offerUrl,
image: v.images?.[0]?.url ?? image,
availability: v.inStock ? 'InStock' : 'OutOfStock',
price: finalPrice,
priceCurrency: variantPrices.final?.currency,
gtin: v.attributeMap.gtin,
priceValidUntil: v.specialToDate,
aggregateRating: this.renderRating(v),
};

if (finalPrice < regularPrice) {
offer.priceSpecification = this.renderOffersPriceSpecification(v);
}

return pruneUndefined(offer);
});
}

/**
* @param {Variant} variant
*/
renderOffersPriceSpecification(variant) {
const { prices } = variant;
const { regular } = prices;
const { amount, currency } = regular;
const { prices: { regular: { amount, currency } } } = variant;
return {
'@type': 'UnitPriceSpecification',
priceType: 'https://schema.org/ListPrice',
Expand All @@ -152,13 +173,21 @@ export class JSONTemplate {
name,
metaDescription,
images,
reviewCount,
ratingValue,
} = this.product;

const productUrl = this.constructProductURL();
const mpn = this.constructMPN();
const image = images?.[0]?.url ?? findProductImage(this.product, this.variants)?.url;
const offers = this.renderOffers();

// if offers don't have an aggregate rating
// the top-level product may have one that applies to all variants
const offersHaveRating = offers.some((o) => o.aggregateRating);
let aggregateRating;
if (!offersHaveRating) {
aggregateRating = this.renderRating();
}

return JSON.stringify(pruneUndefined({
'@context': 'http://schema.org',
'@type': 'Product',
Expand All @@ -170,19 +199,9 @@ export class JSONTemplate {
description: metaDescription,
image,
productID: sku,
...this.renderOffers(),
offers,
aggregateRating,
...(this.renderBrand() ?? {}),
...(typeof reviewCount === 'number'
&& typeof ratingValue === 'number'
&& reviewCount > 0
? {
aggregateRating: {
'@type': 'AggregateRating',
ratingValue,
reviewCount,
},
}
: {}),
}), undefined, 2);
}
}
Loading

0 comments on commit 69cc036

Please sign in to comment.