Skip to content

Commit

Permalink
Merge pull request #3 from adobe-rnd/tweaks
Browse files Browse the repository at this point in the history
fix: moving to core query
  • Loading branch information
maxakuru authored Sep 10, 2024
2 parents e54b323 + 61196f1 commit beb6543
Show file tree
Hide file tree
Showing 7 changed files with 476 additions and 242 deletions.
293 changes: 52 additions & 241 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

// @ts-check

import { errorResponse, makeContext } from './util.js';
import getProductQueryCS from './queries/cs-product.js';
import getProductQueryCore from './queries/core-product.js';
import HTML_TEMPLATE from './templates/html.js';

/**
* @type {Record<string, Config>}
*/
Expand All @@ -21,54 +25,10 @@ const TENANT_CONFIGS = {
magentoEnvironmentId: '97034e45-43a5-48ab-91ab-c9b5a98623a8',
magentoWebsiteCode: 'base',
magentoStoreViewCode: 'default',
coreEndpoint: 'https://www.visualcomfort.com/graphql',
},
};

/**
* @param {TemplateStringsArray} strs
* @param {...any} params
* @returns {string}
*/
export function gql(strs, ...params) {
let res = '';
strs.forEach((s, i) => {
res += s;
if (i < params.length) {
res += params[i];
}
});
return res.replace(/(\\r\\n|\\n|\\r)/gm, ' ').replace(/\s+/g, ' ').trim();
}

/**
* @param {number} status
* @param {string} xError
* @param {string|Record<string,unknown>} [body='']
* @returns
*/
function errorResponse(status, xError, body = '') {
return new Response(typeof body === 'object' ? JSON.stringify(body) : body, {
status,
headers: { 'x-error': xError },
});
}

/**
* @param {import("@cloudflare/workers-types/experimental").ExecutionContext} pctx
* @param {Request} req
* @param {Record<string, string>} env
* @returns {Context}
*/
function makeContext(pctx, req, env) {
/** @type {Context} */
// @ts-ignore
const ctx = pctx;
ctx.env = env;
ctx.url = new URL(req.url);
ctx.log = console;
return ctx;
}

/**
* @param {string} tenant
* @param {Partial<Config>} [overrides={}]
Expand All @@ -89,140 +49,50 @@ function lookupConfig(tenant, overrides) {
* @param {string} sku
* @param {Config} config
*/
async function fetchProduct(sku, config) {
const query = gql`{
products(
skus: ["${sku}"]
) {
__typename
id
sku
name
metaTitle
metaDescription
metaKeyword
description
url
urlKey
shortDescription
url
addToCartAllowed
inStock
images(roles: []) {
url
label
roles
__typename
}
attributes(roles: []) {
name
label
value
roles
__typename
}
... on SimpleProductView {
price {
final {
amount {
value
currency
__typename
}
__typename
}
regular {
amount {
value
currency
__typename
}
__typename
}
roles
__typename
}
__typename
}
... on ComplexProductView {
options {
id
title
required
values {
id
title
... on ProductViewOptionValueProduct {
product {
sku
name
__typename
}
__typename
}
... on ProductViewOptionValueSwatch {
type
value
__typename
}
__typename
}
__typename
}
priceRange {
maximum {
final {
amount {
value
currency
__typename
}
__typename
}
regular {
amount {
value
currency
__typename
}
__typename
}
roles
__typename
}
minimum {
final {
amount {
value
currency
__typename
}
__typename
}
regular {
amount {
value
currency
__typename
}
__typename
}
roles
__typename
}
__typename
}
__typename
}
}
}`;
async function fetchProductCS(sku, config) {
const query = getProductQueryCS({ sku });

const resp = await fetch(`https://catalog-service.adobe.io/graphql?query=${encodeURIComponent(query)}`, {
// method: 'POST',
// body: query,
headers: {
origin: 'https://adobecommerce.live',
// 'content-type':'application/json',
origin: 'https://api.adobecommerce.live',
'x-api-key': config.apiKey,
'Magento-Environment-Id': config.magentoEnvironmentId,
'Magento-Website-Code': config.magentoWebsiteCode,
'Magento-Store-View-Code': config.magentoStoreViewCode,
},
});
if (!resp.ok) {
console.warn('failed to fetch product: ', resp.status);
return resp;
}

const json = await resp.json();
try {
const [product] = json.data.products;
if (!product) {
return errorResponse(404, 'could not find product', json.errors);
}
return product;
} catch (e) {
console.error('failed to parse product: ', e);
return errorResponse(500, 'failed to parse product response');
}
}

/**
* @param {{ urlKey: string } | { sku: string }} opt
* @param {Config} config
*/
// eslint-disable-next-line no-unused-vars
async function fetchProductCore(opt, config) {
const query = getProductQueryCore(opt);
if (!config.coreEndpoint) {
return errorResponse(400, 'coreEndpoint not configured');
}

const resp = await fetch(`${config.coreEndpoint}?query=${encodeURIComponent(query)}`, {
headers: {
origin: 'https://api.adobecommerce.live',
'x-api-key': config.apiKey,
'Magento-Environment-Id': config.magentoEnvironmentId,
'Magento-Website-Code': config.magentoWebsiteCode,
Expand All @@ -248,68 +118,7 @@ async function fetchProduct(sku, config) {
}

function resolvePDPTemplate(product) {
return /* html */`
<!DOCTYPE html>
<html>
<head>
<title>${product.metaTitle || product.name}</title>
<meta property="description" content="${product.metaDescription || product.description}">
<meta property="og:title" content="${product.metaTitle || product.name}">
<meta property="og:image" content="${product.images[0].url}">
<meta property="og:image:secure_url" content="${product.images[0].url}">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="${product.metaTitle || product.name}">
<meta name="twitter:image" content="${product.images[0].url}">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="/scripts/aem.js" type="module"></script>
<script src="/scripts/scripts.js" type="module"></script>
<link rel="stylesheet" href="/styles/styles.css">
</head>
<body>
<header></header>
<main>
<div>
<h1>${product.name}</h1>
<div class="product-gallery">
<div>
${product.images.map((img) => `
<div>
<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>
</div>`).join('\n')}
</div>
</div>
<div class="product-attributes">
${product.attributes.map((attr) => `
<div>
<div>${attr.name}</div>
<div>${attr.label}</div>
<div>${attr.value}</div>
</div>`).join('\n')}
</div>
<div class="product-options">
${product.options.map((opt) => `
<div>
<div>${opt.id}</div>
<div>${opt.title}</div>
<div>${opt.required === true ? 'required' : ''}</div>
</div>
${opt.values.map((val) => `
<div>
<div>${val.id}</div>
<div>${val.title}</div>
</div>`).join('\n')}`).join('\n')}
</div>
</div>
</main>
<footer></footer>
</body>
</html>
`;
return HTML_TEMPLATE(product);
}

/**
Expand All @@ -322,12 +131,14 @@ async function handlePDPRequest(ctx) {
return errorResponse(404, 'missing sku');
}

const config = lookupConfig(tenant, {}); // TODO: allow config overrides from query params
const overrides = Object.fromEntries(ctx.url.searchParams.entries());
const config = lookupConfig(tenant, overrides);
if (!config) {
return errorResponse(404, 'config not found');
}

const product = await fetchProduct(sku, config);
// const product = await fetchProductCore({ sku }, config);
const product = await fetchProductCS(sku, config);
const html = resolvePDPTemplate(product);
return new Response(html, {
status: 200,
Expand Down
56 changes: 56 additions & 0 deletions src/queries/core-product.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright 2024 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

import { gql } from '../util.js';

/**
* @param {{ urlKey?: string; sku?: string; }} param0
*/
export default ({ urlKey, sku }) => gql`{
products(
filter: { ${urlKey ? 'url_key' : 'sku'}: { eq: "${urlKey ?? sku}" } }
) {
items {
sku
name
meta_title
meta_keyword
meta_description
short_description {
html
}
description {
html
}
image {
url
label
disabled
}
thumbnail {
url
label
}
media_gallery {
url
label
}
categories {
category_seo_name
breadcrumbs {
category_name
category_level
}
}
}
}
}`;
Loading

0 comments on commit beb6543

Please sign in to comment.