Skip to content

Commit

Permalink
fix: pull from helix catalog
Browse files Browse the repository at this point in the history
  • Loading branch information
dylandepass committed Oct 17, 2024
1 parent 11bb8bc commit 918ca03
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 36 deletions.
15 changes: 10 additions & 5 deletions src/catalog/product.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,17 @@ export async function handleProductPutRequest(ctx, config, request) {
for (const key of matchedKeys) {
let path = key.replace('{{sku}}', product.sku);

if (key.includes('{{urlkey}}') && product.url_key) {
path = path.replace('{{urlkey}}', product.url_key);
if (key.includes('{{urlkey}}') && product.urlKey) {
path = path.replace('{{urlkey}}', product.urlKey);
}
const previewResponse = await callAdmin(config, 'preview', path);
if (!previewResponse.ok) {
return errorResponse(400, 'failed to preview product');
}
const publishResponse = await callAdmin(config, 'publish', path);
if (!publishResponse.ok) {
return errorResponse(400, 'failed to publish product');
}

await callAdmin(config, 'preview', path);
await callAdmin(config, 'publish', path);
}
}
return new Response(undefined, { status: 201 });
Expand Down
4 changes: 2 additions & 2 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,8 @@ export async function resolveConfig(ctx, overrides = {}) {
// Ensure that there are exactly 4 segments after 'catalog' (env, store, storeView, product)
if (catalogIndex !== -1 && pathSegments.length >= catalogIndex + 4) {
resolved.env = pathSegments[catalogIndex + 1];
resolved.store = pathSegments[catalogIndex + 2];
resolved.storeView = pathSegments[catalogIndex + 3];
resolved.storeCode = pathSegments[catalogIndex + 2];
resolved.storeViewCode = pathSegments[catalogIndex + 3];
resolved.subRoute = pathSegments[catalogIndex + 4];
resolved.sku = pathSegments[catalogIndex + 5];
} else {
Expand Down
34 changes: 32 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import getProductSKUQuery from './queries/core-product-sku.js';
import HTML_TEMPLATE from './templates/html.js';
import { resolveConfig } from './config.js';
import { handleProductGetRequest, handleProductPutRequest } from './catalog/product.js';
import { loadProductFromR2 } from './utils/r2.js';

const ALLOWED_METHODS = ['GET', 'PUT'];

Expand Down Expand Up @@ -171,7 +172,7 @@ async function lookupProductSKU(urlkey, config) {
* @param {Config} config
*/
// @ts-ignore
async function handlePDPRequest(ctx, config) {
async function handleMagentoPDPRequest(ctx, config) {
const { urlkey } = config.params;
let { sku } = config.params;

Expand Down Expand Up @@ -201,6 +202,30 @@ async function handlePDPRequest(ctx, config) {
});
}

/**
* @param {Context} ctx
* @param {Config} config
*/
// @ts-ignore
async function handleHelixPDPRequest(ctx, config) {
const { urlkey } = config.params;
const { sku } = config.params;

if (!sku && !urlkey) {
return errorResponse(404, 'missing sku or urlkey');
}

config.env = 'prod';
const product = await loadProductFromR2(ctx, config, sku);
const html = HTML_TEMPLATE(product, product.variants);
return new Response(html, {
status: 200,
headers: {
'content-type': 'text/html',
},
});
}

/**
* @type {Record<string, (ctx: Context, config: Config, request: Request) => Promise<Response>>}
*/
Expand All @@ -209,7 +234,12 @@ const handlers = {
if (config.pageType !== 'product') {
return errorResponse(404, 'page type not supported');
}
return handlePDPRequest(ctx, config);

if (config.catalogSource === 'helix') {
return handleHelixPDPRequest(ctx, config);
}

return handleMagentoPDPRequest(ctx, config);
},
catalog: async (ctx, config, request) => {
if (ctx.info.method !== 'PUT' && ctx.info.method !== 'GET') {
Expand Down
25 changes: 12 additions & 13 deletions src/templates/html.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ export default (product, variants) => {
} = product;

const image = findProductImage(product, variants);

return /* html */`\
<!DOCTYPE html>
<html>
Expand All @@ -64,14 +63,14 @@ export default (product, variants) => {
<meta property="og:image:secure_url" content="${image?.url}">
<meta property="og:type" content="product">
<meta property="product:availability" content="${inStock ? 'In stock' : 'Out of stock'}">
<meta property="product:price.amount" content="${prices.final.amount}">
<meta property="product:price.currency" content="${prices.final.currency}">
<meta property="product:price.amount" content="${prices?.final?.amount}">
<meta property="product:price.currency" content="${prices?.final?.currency}">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="${name}">
<meta name="twitter:image" content="${image?.url}">
<meta name="twitter:description" content="${metaDescription || description}">
<meta name="twitter:label1" content="Price">
<meta name="twitter:data1" content="${prices.final.amount}">
<meta name="twitter:data1" content="${prices?.final?.amount}">
<meta name="twitter:label2" content="Availability">
<meta name="twitter:data2" content="${inStock ? 'In stock' : 'Out of stock'}">
<meta name="keywords" content="${metaKeyword}">
Expand All @@ -96,7 +95,7 @@ export default (product, variants) => {
${description ? `<p>${description}</p>` : ''}
<div class="product-images">
<div>
${images.map((img) => `\
${images?.map((img) => `\
<div>
<picture>
<source type="image/webp" srcset="${img.url}" alt="" media="(min-width: 600px)">
Expand All @@ -109,7 +108,7 @@ ${images.map((img) => `\
</div>
<div class="product-attributes">
${attributes.map((attr) => `\
${attributes?.map((attr) => `\
<div>
<div>${attr.name}</div>
<div>${attr.label}</div>
Expand All @@ -118,7 +117,7 @@ ${attributes.map((attr) => `\
</div>
<div class="product-options">
${options.map((opt) => `\
${options?.map((opt) => `\
<div>
<div>${opt.id}</div>
<div>${opt.label}</div>
Expand All @@ -127,7 +126,7 @@ ${options.map((opt) => `\
<div>${opt.multiple ? 'multiple' : ''}</div>
<div>${opt.required === true ? 'required' : ''}</div>
</div>
${opt.items.map((item) => `\
${opt.items?.map((item) => `\
<div>
<div>option</div>
<div>${item.id}</div>
Expand All @@ -139,23 +138,23 @@ ${opt.items.map((item) => `\
</div>
<div class="product-variants">
${variants.map((v) => `\
${variants?.map((v) => `\
<div>
<div>${v.sku}</div>
<div>${v.name}</div>
<div>${v.inStock ? 'inStock' : ''}</div>
<div>Regular: ${v.prices.regular.amount} ${v.prices.regular.currency}${priceRange(v.prices.regular.minimumAmount, v.prices.regular.maximumAmount)}</div>
<div>Final: ${v.prices.final.amount} ${v.prices.final.currency}${priceRange(v.prices.final.minimumAmount, v.prices.final.maximumAmount)}</div>
<div>Regular: ${v.prices?.regular?.amount} ${v.prices?.regular?.currency}${priceRange(v.prices?.regular?.minimumAmount, v.prices?.regular?.maximumAmount)}</div>
<div>Final: ${v.prices?.final?.amount} ${v.prices?.final?.currency}${priceRange(v.prices?.final?.minimumAmount, v.prices?.final?.maximumAmount)}</div>
<div>
${v.images.map((img) => `\
${v.images?.map((img) => `\
<picture>
<source type="image/webp" srcset="${img.url}" alt="" media="(min-width: 600px)">
<source type="image/webp" srcset="${img.url}">
<source type="image/png" srcset="${img.url}" media="(min-width: 600px)">
<img loading="lazy" alt="${img.label}" src="${img.url}">
</picture>`).join('\n')}
</div>
<div>${v.selections.join(', ')}</div>
<div>${v.selections?.join(', ')}</div>
</div>`).join('\n')}
</div>
</main>
Expand Down
10 changes: 5 additions & 5 deletions src/templates/json-ld.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default (product, variants) => {
} = product;

const image = images?.[0]?.url ?? findProductImage(product, variants)?.url;
const brandName = attributes.find((attr) => attr.name === 'brand')?.value;
const brandName = attributes?.find((attr) => attr.name === 'brand')?.value;

return JSON.stringify(pruneUndefined({
'@context': 'http://schema.org',
Expand All @@ -51,17 +51,17 @@ export default (product, variants) => {
url,
image,
availability: inStock ? 'InStock' : 'OutOfStock',
price: prices.final.amount,
priceCurrency: prices.final.currency,
price: prices?.final?.amount,
priceCurrency: prices?.final?.currency,
},
...variants.map((v) => ({
'@type': 'Offer',
sku: v.sku,
url: v.url,
image: v.images?.[0]?.url ?? image,
availability: v.inStock ? 'InStock' : 'OutOfStock',
price: v.prices.final.amount,
priceCurrency: v.prices.final.currency,
price: v.prices?.final?.amount,
priceCurrency: v.prices?.final?.currency,

})),
],
Expand Down
1 change: 1 addition & 0 deletions src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ declare global {
storeViewCode: string;
storeCode: string;
coreEndpoint: string;
catalogSource: string;
catalogEndpoint?: string;
sku?: string;
confMap: Record<string, Config>;
Expand Down
18 changes: 9 additions & 9 deletions src/utils/r2.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export async function getSyncTimestamp(ctx, config) {

// Helper function to load product from R2 using SKU
export async function loadProductFromR2(ctx, config, sku) {
const key = `${config.org}/${config.site}/${config.env}/${config.store}/${config.storeView}/${sku}.json`;
const key = `${config.org}/${config.site}/${config.env}/${config.storeCode}/${config.storeViewCode}/${sku}.json`;
const object = await ctx.env.CATALOG_BUCKET.get(key);

if (!object) {
Expand All @@ -65,18 +65,18 @@ export async function saveProductsToR2(ctx, config, products) {
const storeProductsBatch = async (batch) => {
const storePromises = batch.map(async (product) => {
try {
const { sku, name, url_key } = product;
const key = `${config.org}/${config.site}/${config.env}/${config.store}/${config.storeView}/${sku}.json`;
const { sku, name, urlKey } = product;
const key = `${config.org}/${config.site}/${config.env}/${config.storeCode}/${config.storeViewCode}/${sku}.json`;
const body = JSON.stringify(product);
const customMetadata = { sku, name, url_key };
const customMetadata = { sku, name, urlKey };

const productPromise = ctx.env.CATALOG_BUCKET.put(key, body, {
httpMetadata: { contentType: 'application/json' },
customMetadata,
});

if (url_key) {
const metadataKey = `${config.org}/${config.site}/${config.env}/${config.store}/${config.storeView}/urlkeys/${url_key}`;
if (urlKey) {
const metadataKey = `${config.org}/${config.site}/${config.env}/${config.storeCode}/${config.storeViewCode}/urlkeys/${urlKey}`;
const metadataPromise = ctx.env.CATALOG_BUCKET.put(metadataKey, '', {
httpMetadata: { contentType: 'application/octet-stream' },
customMetadata,
Expand All @@ -103,14 +103,14 @@ export async function saveProductsToR2(ctx, config, products) {
export async function listAllProducts(ctx, config) {
const bucket = ctx.env.CATALOG_BUCKET;

console.log(`${config.org}/${config.site}/${config.env}/${config.store}/${config.storeView}/`);
const listResponse = await bucket.list({ prefix: `${config.org}/${config.site}/${config.env}/${config.store}/${config.storeView}/ ` });
console.log(`${config.org}/${config.site}/${config.env}/${config.storeCode}/${config.storeViewCode}/`);
const listResponse = await bucket.list({ prefix: `${config.org}/${config.site}/${config.env}/${config.storeCode}/${config.storeViewCode}/` });
const files = listResponse.objects;

const batchSize = 50; // Define the batch size
const customMetadataArray = [];

const excludeDirectory = `${config.org}/${config.site}/${config.env}/${config.store}/${config.storeView}/ urlkeys/`;
const excludeDirectory = `${config.org}/${config.site}/${config.env}/${config.storeCode}/${config.storeViewCode}/ urlkeys/`;

// Helper function to split the array into chunks of a specific size
function chunkArray(array, size) {
Expand Down

0 comments on commit 918ca03

Please sign in to comment.