@mediarithmics/plugins-nodejs-sdk
Version:
This is the mediarithmics nodejs to help plugin developers bootstrapping their plugin without having to deal with most of the plugin boilerplate
152 lines • 7.44 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RecommendationsHandlebarsEngine = exports.HandlebarsEngine = exports.buildURLHandlebarsRootContext = void 0;
const handlebars_1 = __importDefault(require("handlebars"));
const lodash_1 = __importDefault(require("lodash"));
const numeral_1 = __importDefault(require("numeral"));
const index_1 = require("../../mediarithmics/plugins/ad-renderer/utils/index");
const formatPrice = (price, pattern) => {
const number = (0, numeral_1.default)(price);
return number.format(pattern);
};
const encodeClickUrl = () => (redirectUrls, clickUrl) => {
const urls = redirectUrls.slice(0);
urls.push({
url: clickUrl,
redirect_count: 0,
});
return (0, index_1.generateEncodedClickUrl)(urls);
};
const placeHolder = '{{MICS_AD_CONTENT_ID}}';
const uriEncodePlaceHolder = encodeURI(placeHolder);
const doubleEncodedUriPlaceHolder = encodeURI(encodeURI(placeHolder));
// Encode recommendation click url => contains the contentId of the recommendation that will be
// insrted into the campaign log
const encodeRecoClickUrlHelper = () => (idx, rootContext, recommendation) => {
rootContext.private.clickableContents.push({
item_id: recommendation.$id,
catalog_token: recommendation.$catalog_token,
$content_id: idx,
});
// recommendation.url replace placeHolder by idx
const filledRedirectUrls = rootContext.private.redirectUrls.map((clickUrlInfo) => {
const cloned = lodash_1.default.clone(clickUrlInfo);
const url1 = lodash_1.default.replace(cloned.url, placeHolder, idx.toString());
const url2 = lodash_1.default.replace(url1, uriEncodePlaceHolder, idx.toString());
cloned.url = lodash_1.default.replace(url2, doubleEncodedUriPlaceHolder, idx.toString());
return cloned;
});
const recommendationUrl = recommendation.$url ? recommendation.$url : '';
return encodeClickUrl()(filledRedirectUrls, recommendationUrl);
};
const buildURLHandlebarsRootContext = (adRenderRequest, instanceContext) => {
return {
REQUEST: adRenderRequest,
CREATIVE: {
CLICK_URL: instanceContext.creative_click_url,
WIDTH: instanceContext.width,
HEIGHT: instanceContext.height,
},
ORGANISATION_ID: instanceContext.displayAd.organisation_id, // Hack, it should come from the AdRendererRequest
AD_GROUP_ID: adRenderRequest.ad_group_id,
MEDIA_ID: adRenderRequest.media_id,
ENCODED_MEDIA_ID: adRenderRequest.media_id ? encodeURIComponent(adRenderRequest.media_id) : undefined,
CAMPAIGN_ID: adRenderRequest.campaign_id,
CREATIVE_ID: adRenderRequest.creative_id,
CACHE_BUSTER: Date.now().toString(),
IAS_CLIENT_ID: instanceContext.ias_client_id,
CB: Date.now().toString(),
};
};
exports.buildURLHandlebarsRootContext = buildURLHandlebarsRootContext;
class HandlebarsEngine {
// Initialisation of the engine. Done once at every InstanceContext rebuild.
init() {
this.engine = handlebars_1.default.create();
/* Generic Helpers */
this.engine.registerHelper('formatPrice', formatPrice);
this.engine.registerHelper('toJson', (object) => JSON.stringify(object));
}
enableProfileDataLayer() {
this.engine.registerHelper('profileData', (opts) => {
// See https://blog.osteele.com/2007/12/cheap-monads/
const $N = {};
// Check if we have the value in the DataLayer
if ((((((opts || $N).data || $N).root || $N).private || $N).profileData || $N)[opts.hash.fieldName]) {
return opts.data.root.private.profileData[opts.hash.fieldName];
}
else {
return opts.hash.defaultValue;
}
});
}
parse(template) {
return handlebars_1.default.parse(template);
}
// TODO: Test this thing
getMacros(internals) {
class MacroScanner extends handlebars_1.default.Visitor {
constructor() {
super(...arguments);
this.macros = [];
}
}
// The Handlebars Compiler library is documented there: https://github.com/wycats/handlebars.js/blob/master/docs/compiler-api.md
MacroScanner.prototype.MustacheStatement = function (macro) {
if (macro.type === 'MustacheStatement') {
const pathExpression = macro.path;
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
this.macros.push({ parts: pathExpression.parts });
}
// We're just here to visit, we don't want to break anything, so let's call the "default function" to process MustacheStatement
handlebars_1.default.Visitor.prototype.MustacheStatement.call(this, macro);
};
const scanner = new MacroScanner();
scanner.accept(internals);
return scanner.macros;
}
compile(template) {
// Handlebars.compile() can take a string or an AST
return this.engine.compile(template);
}
}
exports.HandlebarsEngine = HandlebarsEngine;
class RecommendationsHandlebarsEngine extends HandlebarsEngine {
constructor() {
super();
}
// Initialisation of the engine. Done once at every InstanceContext rebuild.
init() {
super.init();
/* URL Encoding witchcraft */
// We need to have 2 elements when doing the URL encoding:
// 1. The "click tracking" array passed in the rootContext (for click tracking)
// 2. The final URL (landing page, etc.) passed as a parameter of the helper
//
// In order to have both, we need to play smart and use an Handlebar partial
// This handlebar partial is just a way to add "@root" as a parameter of the helper before calling it
//
// This is how the encodeClickUrl partial should be used in templates:
// {{> encodeClickUrl url="http://www.mediarithmics.com/en/"}}
const encodeClickUrlPartial = '{{encodeClickUrlInternal @root.private.redirectUrls url}}';
this.engine.registerPartial('encodeClickUrl', encodeClickUrlPartial);
this.engine.registerHelper('encodeClickUrlInternal', encodeClickUrl());
// Same story than previously but this time the partial will inject:
// @index -> the index of the recommendation, which is used to include it in the URL
// @root -> the root context
// this -> the recommendation item
// Warning, this partial should only be used in a {{#each recommendations}}{{/each}} block
// The $url field of the recommendation will be used as the final URL
//
// This is how the partial should be used in templates:
// {{> encodeRecoClickUrl}}
const encodeRecoClickUrlPartial = '{{encodeRecoClickUrlInternal @index @root this}}';
this.engine.registerPartial('encodeRecoClickUrl', encodeRecoClickUrlPartial);
this.engine.registerHelper('encodeRecoClickUrlInternal', encodeRecoClickUrlHelper());
}
}
exports.RecommendationsHandlebarsEngine = RecommendationsHandlebarsEngine;
//# sourceMappingURL=HandlebarsEngine.js.map