Skip to content

Commit

Permalink
Merge pull request #726 from jkanive/MERC-7797-1
Browse files Browse the repository at this point in the history
feat(other): MERC-7797 Added feature to render Widgets in the regions of supported page types.
  • Loading branch information
jkanive authored Jul 13, 2021
2 parents dae631a + 40c410b commit d9c0f3d
Show file tree
Hide file tree
Showing 7 changed files with 312 additions and 4 deletions.
97 changes: 97 additions & 0 deletions lib/content-api-client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
require('colors');
const NetworkUtils = require('./utils/NetworkUtils');
const {
renderedRegionsByPageTypeQuery,
renderedRegionsByPageTypeAndEntityIdQuery,
} = require('./graphql/query');

const networkUtils = new NetworkUtils();

/**
* @param {object} options
* @param {string} options.accessToken
* @param {string} options.storeUrl
* @param {string} pageType
* @returns {Promise<renderedRegions: array}>}
*/
async function getRenderedRegionsByPageType({ accessToken, storeUrl, pageType }) {
try {
const query = renderedRegionsByPageTypeQuery(pageType);

const response = await networkUtils.sendApiRequest({
url: `${storeUrl}/graphql`,
headers: {
'cache-control': 'no-cache',
'content-type': 'application/json',
Authorization: `Bearer ${accessToken}`,
},
method: 'POST',
data: JSON.stringify({
query,
}),
});

const {
site: {
content: {
renderedRegionsByPageType: { regions },
},
},
} = response.data.data;

return { renderedRegions: regions };
} catch (err) {
throw new Error(`Could not fetch the rendered regions for this page type: ${err.message}`);
}
}

/**
* @param {object} options
* @param {string} options.accessToken
* @param {string} options.storeUrl
* @param {string} pageType
* @param {number} entityId
* @returns {Promise<renderedRegions: array}>}
*/
async function getRenderedRegionsByPageTypeAndEntityId({
accessToken,
storeUrl,
pageType,
entityId,
}) {
try {
const query = renderedRegionsByPageTypeAndEntityIdQuery(pageType, entityId);

const response = await networkUtils.sendApiRequest({
url: `${storeUrl}/graphql`,
headers: {
'cache-control': 'no-cache',
'content-type': 'application/json',
Authorization: `Bearer ${accessToken}`,
},
method: 'POST',
data: JSON.stringify({
query,
}),
});

const {
site: {
content: {
renderedRegionsByPageTypeAndEntityId: { regions },
},
},
} = response.data.data;

return {
renderedRegions: regions,
};
} catch (err) {
throw new Error(`Could not fetch the rendered regions for this page type: ${err.message}`);
}
}

module.exports = {
getRenderedRegionsByPageType,
getRenderedRegionsByPageTypeAndEntityId,
};
42 changes: 42 additions & 0 deletions lib/graphql/query.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* @param {string} pageType
* @returns {string}>}
*/

const renderedRegionsByPageTypeQuery = (pageType) =>
`query {
site {
content {
renderedRegionsByPageType(pageType: ${pageType}) {
regions {
name
html
}
}
}
}
}`;

/**
* @param {string} pageType
* @param {number} entityId
* @returns {string}>}
*/
const renderedRegionsByPageTypeAndEntityIdQuery = (pageType, entityId) =>
`query {
site {
content {
renderedRegionsByPageTypeAndEntityId(entityPageType: ${pageType}, entityId: ${entityId}) {
regions {
name
html
}
}
}
}
}`;

module.exports = {
renderedRegionsByPageTypeQuery,
renderedRegionsByPageTypeAndEntityIdQuery,
};
5 changes: 3 additions & 2 deletions server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ function buildManifest(srcManifest, options) {

const parsedSecureUrl = new URL(options.dotStencilFile.storeUrl); // The url to a secure page (prompted as login page)
const parsedNormalUrl = new URL(options.dotStencilFile.normalStoreUrl); // The host url of the homepage;
const storeUrl = parsedSecureUrl.protocol + '//' + parsedSecureUrl.host;

resManifest.server.port = options.dotStencilFile.port;
pluginsByName['./plugins/router/router.module'].storeUrl =
parsedSecureUrl.protocol + '//' + parsedSecureUrl.host;
pluginsByName['./plugins/router/router.module'].storeUrl = storeUrl;
pluginsByName['./plugins/router/router.module'].normalStoreUrl =
parsedNormalUrl.protocol + '//' + parsedNormalUrl.host;
pluginsByName['./plugins/router/router.module'].apiKey = options.dotStencilFile.apiKey;
Expand All @@ -27,6 +27,7 @@ function buildManifest(srcManifest, options) {
pluginsByName['./plugins/renderer/renderer.module'].customLayouts =
options.dotStencilFile.customLayouts;
pluginsByName['./plugins/renderer/renderer.module'].themePath = options.themePath;
pluginsByName['./plugins/renderer/renderer.module'].storeUrl = storeUrl;
pluginsByName['./plugins/theme-assets/theme-assets.module'].themePath = options.themePath;

resManifest.register.plugins = _.reduce(
Expand Down
101 changes: 101 additions & 0 deletions server/lib/page-type-util.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
const PageTypes = {
PAGE: 'PAGE',
PRODUCT: 'PRODUCT',
CATEGORY: 'CATEGORY',
BRAND: 'BRAND',
ACCOUNT_RETURN_SAVED: 'ACCOUNT_RETURN_SAVED',
ACCOUNT_ADD_RETURN: 'ACCOUNT_ADD_RETURN',
ACCOUNT_RETURNS: 'ACCOUNT_RETURNS',
ACCOUNT_ADD_ADDRESS: 'ACCOUNT_ADD_ADDRESS',
ACCOUNT_ADD_WISHLIST: 'ACCOUNT_ADD_WISHLIST',
ACCOUNT_WISHLISTS: 'ACCOUNT_WISHLISTS',
ACCOUNT_WISHLIST_DETAILS: 'ACCOUNT_WISHLIST_DETAILS',
ACCOUNT_EDIT: 'ACCOUNT_EDIT',
ACCOUNT_ADDRESS: 'ACCOUNT_ADDRESS',
ACCOUNT_INBOX: 'ACCOUNT_INBOX',
ACCOUNT_DOWNLOAD_ITEM: 'ACCOUNT_DOWNLOAD_ITEM',
ACCOUNT_ORDERS_ALL: 'ACCOUNT_ORDERS_ALL',
ACCOUNT_ORDERS_INVOICE: 'ACCOUNT_ORDERS_INVOICE',
ACCOUNT_ORDERS_DETAILS: 'ACCOUNT_ORDERS_DETAILS',
ACCOUNT_ORDERS_COMPLETED: 'ACCOUNT_ORDERS_COMPLETED',
ACCOUNT_RECENT_ITEMS: 'ACCOUNT_RECENT_ITEMS',
AUTH_ACCOUNT_CREATED: 'AUTH_ACCOUNT_CREATED',
AUTH_LOGIN: 'AUTH_LOGIN',
AUTH_CREATE_ACC: 'AUTH_CREATE_ACC',
AUTH_FORGOT_PASS: 'AUTH_FORGOT_PASS',
AUTH_NEW_PASS: 'AUTH_NEW_PASS',
BLOG_POST: 'BLOG_POST',
BLOG: 'BLOG',
BRANDS: 'BRANDS',
CART: 'CART',
COMPARE: 'COMPARE',
CONTACT_US: 'CONTACT_US',
HOME: 'HOME',
GIFT_CERT_PURCHASE: 'GIFT_CERT_PURCHASE',
GIFT_CERT_REDEEM: 'GIFT_CERT_REDEEM',
GIFT_CERT_BALANCE: 'GIFT_CERT_BALANCE',
ORDER_INFO: 'ORDER_INFO',
SEARCH: 'SEARCH',
SITEMAP: 'SITEMAP',
SUBSCRIBED: 'SUBSCRIBED',
UNSUBSCRIBE: 'UNSUBSCRIBE',
};

const templateFileToPageTypeMap = {
'pages/page': PageTypes.PAGE,
'pages/product': PageTypes.PRODUCT,
'pages/category': PageTypes.CATEGORY,
'pages/brand': PageTypes.BRAND,
'pages/account/return-saved': PageTypes.ACCOUNT_RETURN_SAVED,
'pages/account/add-return': PageTypes.ACCOUNT_ADD_RETURN,
'pages/account/returns': PageTypes.ACCOUNT_RETURNS,
'pages/account/add-address': PageTypes.ACCOUNT_ADD_ADDRESS,
'pages/account/add-wishlist': PageTypes.ACCOUNT_ADD_WISHLIST,
'pages/account/wishlists': PageTypes.ACCOUNT_WISHLISTS,
'pages/account/wishlist-details': PageTypes.ACCOUNT_WISHLIST_DETAILS,
'pages/account/edit': PageTypes.ACCOUNT_EDIT,
'pages/account/addresses': PageTypes.ACCOUNT_ADDRESS,
'pages/account/inbox': PageTypes.ACCOUNT_INBOX,
'pages/account/download-item': PageTypes.ACCOUNT_DOWNLOAD_ITEM,
'pages/account/orders/all': PageTypes.ACCOUNT_ORDERS_ALL,
'pages/account/orders/invoice': PageTypes.ACCOUNT_ORDERS_INVOICE,
'pages/account/orders/details': PageTypes.ACCOUNT_ORDERS_DETAILS,
'pages/account/orders/completed': PageTypes.ACCOUNT_ORDERS_COMPLETED,
'pages/account/recent-items': PageTypes.ACCOUNT_RECENT_ITEMS,
'pages/auth/account-created': PageTypes.AUTH_ACCOUNT_CREATED,
'pages/auth/login': PageTypes.AUTH_LOGIN,
'pages/auth/create-account': PageTypes.AUTH_CREATE_ACC,
'pages/auth/forgot-password': PageTypes.AUTH_FORGOT_PASS,
'pages/auth/new-password': PageTypes.AUTH_NEW_PASS,
'pages/blog-post': PageTypes.BLOG_POST,
'pages/blog': PageTypes.BLOG,
'pages/brands': PageTypes.BRANDS,
'pages/cart': PageTypes.CART,
'pages/compare': PageTypes.COMPARE,
'pages/contact-us': PageTypes.CONTACT_US,
'pages/home': PageTypes.HOME,
'pages/gift-certificate/purchase': PageTypes.GIFT_CERT_PURCHASE,
'pages/gift-certificate/redeem': PageTypes.GIFT_CERT_REDEEM,
'pages/gift-certificate/balance': PageTypes.GIFT_CERT_BALANCE,
'pages/order-confirmation': PageTypes.ORDER_INFO,
'pages/search': PageTypes.SEARCH,
'pages/sitemap': PageTypes.SITEMAP,
'pages/subscribed': PageTypes.SUBSCRIBED,
'pages/unsubscribe': PageTypes.UNSUBSCRIBE,
};

/**
* Convert a templateFile to pageType
*
* @param {string} templateFile
* @returns {string | null}
*/
function getPageType(templateFile) {
const pageType = templateFileToPageTypeMap[templateFile];

return pageType;
}

module.exports = {
getPageType,
};
14 changes: 14 additions & 0 deletions server/lib/page-type-util.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const { getPageType } = require('./page-type-util');

describe('page-type-util', () => {
describe('getPageType', () => {
it('should return a string pageType value', () => {
expect(getPageType('pages/page')).toEqual('PAGE');
expect(getPageType('pages/brand')).toEqual('BRAND');
});

it('should should return a null value', () => {
expect(getPageType('pages/something')).toBeUndefined();
});
});
});
55 changes: 53 additions & 2 deletions server/plugins/renderer/renderer.module.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@ const templateAssembler = require('../../../lib/template-assembler');
const utils = require('../../lib/utils');
const { readFromStream } = require('../../../lib/utils/asyncUtils');
const NetworkUtils = require('../../../lib/utils/NetworkUtils');
const contentApiClient = require('../../../lib/content-api-client');
const { getPageType } = require('../../lib/page-type-util');

const networkUtils = new NetworkUtils();

const internals = {
options: {},
cacheTTL: 1000 * 15, // 15 seconds
graphQLCacheTTL: 1000 * 300, // 5 minutes
validCustomTemplatePageTypes: ['brand', 'category', 'page', 'product'],
};

Expand Down Expand Up @@ -198,7 +201,53 @@ internals.parseResponse = async (bcAppData, request, response, responseArgs) =>
cache.put(dataRequestSignature, { response2 }, internals.cacheTTL);
}

return internals.getPencilResponse(response2.data, request, response2, configuration);
const templateFile = response2.data.template_file;
const entityId = response2.data.entity_id;

const pageType = getPageType(templateFile);
let regionResponse = [];

if (pageType) {
// create request signature and use cache, if available
const graphQLUrlSignature = internals.sha1sum(internals.options.storeUrl + '/graphql');
const graphQLQuerySignature = internals.sha1sum(pageType + entityId);
const graphQLDataReqSignature = `graphql:${graphQLUrlSignature + graphQLQuerySignature}`;
const cachedGraphQLResponse = cache.get(graphQLDataReqSignature);

if (internals.options.useCache && cachedGraphQLResponse) {
({ regionResponse } = cachedGraphQLResponse);
} else {
if (typeof entityId === 'number') {
regionResponse = await contentApiClient.getRenderedRegionsByPageTypeAndEntityId({
accessToken: response2.data.context.settings.storefront_api.token,
storeUrl: internals.options.storeUrl,
pageType,
entityId,
});
} else {
regionResponse = await contentApiClient.getRenderedRegionsByPageType({
accessToken: response2.data.context.settings.storefront_api.token,
storeUrl: internals.options.storeUrl,
pageType,
});
}

cache.put(graphQLDataReqSignature, { regionResponse }, internals.graphQLCacheTTL);
}
}

const formattedRegions = {};
regionResponse.renderedRegions.forEach((region) => {
formattedRegions[region.name] = region.html;
});

return internals.getPencilResponse(
response2.data,
request,
response2,
configuration,
formattedRegions,
);
};

/**
Expand Down Expand Up @@ -319,9 +368,10 @@ internals.getTemplatePath = (requestPath, data) => {
* @param request
* @param response
* @param configuration
* @param renderedRegions
* @returns {*}
*/
internals.getPencilResponse = (data, request, response, configuration) => {
internals.getPencilResponse = (data, request, response, configuration, renderedRegions = {}) => {
const context = {
...data.context,
theme_settings: configuration.settings,
Expand All @@ -343,6 +393,7 @@ internals.getPencilResponse = (data, request, response, configuration) => {
templates: data.templates,
remote: data.remote,
remote_data: data.remote_data,
renderedRegions,
context,
translations: data.translations,
method: request.method,
Expand Down
2 changes: 2 additions & 0 deletions server/plugins/renderer/responses/pencil-response.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ class PencilResponse {
* @param data.acceptLanguage
* @param {{[string]: string[]}} data.headers
* @param data.statusCode
* @param data.renderedRegions
* @param assembler
*/
constructor(data, assembler) {
Expand Down Expand Up @@ -137,6 +138,7 @@ class PencilResponse {
this.data.context.in_production = false;

paper.addDecorator(makeDecorator(request, this.data.context));
paper.setContent(this.data.renderedRegions);

// Plugins have the opportunity to add/modify the response by using decorators
_.each(request.app.decorators, (decorator) => {
Expand Down

0 comments on commit d9c0f3d

Please sign in to comment.