@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
167 lines (139 loc) • 4.64 kB
text/typescript
import * as express from "express";
import * as _ from "lodash";
import {
AdRendererRequest,
Creative,
CreativeResponse,
AdRendererBaseInstanceContext,
BasePlugin,
TemplatingEngine,
AdRendererPluginResponse,
PluginProperty,
DisplayAd
} from "../../../index";
export abstract class AdRendererBasePlugin<
T extends AdRendererBaseInstanceContext
> extends BasePlugin {
instanceContext: Promise<T>;
displayContextHeader = "x-mics-display-context";
// Helper to fetch the Display Ad resource with caching
async fetchDisplayAd(displayAdId: string): Promise<DisplayAd> {
const response = await super.requestGatewayHelper(
"GET",
`${this.outboundPlatformUrl}/v1/creatives/${displayAdId}`
);
this.logger.debug(
`Fetched Creative: ${displayAdId} - ${JSON.stringify(response.data)}`
);
if ((response.data as DisplayAd).type !== "DISPLAY_AD") {
throw new Error(
`crid: ${
displayAdId
} - When fetching DisplayAd, another creative type was returned!`
);
}
return response.data;
}
// Helper to fetch the Display Ad properties resource with caching
async fetchDisplayAdProperties(
displayAdId: string
): Promise<PluginProperty[]> {
const creativePropertyResponse = await super.requestGatewayHelper(
"GET",
`${this.outboundPlatformUrl}/v1/creatives/${
displayAdId
}/renderer_properties`
);
this.logger.debug(
`Fetched Creative Properties: ${displayAdId} - ${JSON.stringify(
creativePropertyResponse.data
)}`
);
return creativePropertyResponse.data;
}
getEncodedClickUrl(redirectUrls: string[]): string {
let urls = redirectUrls.slice(0);
return urls.reduceRight(
(acc, current) => current + encodeURIComponent(acc),
""
);
}
// Method to build an instance context
// To be overriden to get a custom behavior
protected async instanceContextBuilder(creativeId: string): Promise<T> {
const displayAdP = this.fetchDisplayAd(creativeId);
const displayAdPropsP = this.fetchDisplayAdProperties(creativeId);
const results = await Promise.all([displayAdP, displayAdPropsP]);
const displayAd = results[0];
const displayAdProps = results[1];
const context = {
displayAd: displayAd,
displayAdProperties: displayAdProps
} as T;
return Promise.resolve(context);
}
protected abstract onAdContents(
request: AdRendererRequest,
instanceContext: T
): Promise<AdRendererPluginResponse>;
private initAdContentsRoute(): void {
this.app.post(
"/v1/ad_contents",
this.asyncMiddleware(
async (req: express.Request, res: express.Response) => {
if (!req.body || _.isEmpty(req.body)) {
const msg = {
error: "Missing request body"
};
this.logger.error("POST /v1/ad_contents : %s", JSON.stringify(msg));
return res.status(500).json(msg);
} else {
this.logger.debug(
`POST /v1/ad_contents ${JSON.stringify(req.body)}`
);
const adRendererRequest = req.body as AdRendererRequest;
if (!this.onAdContents) {
this.logger.error(
"POST /v1/ad_contents: No AdContents listener registered!"
);
const msg = {
error: "No AdContents listener registered!"
};
return res.status(500).json(msg);
}
if (
!this.pluginCache.get(adRendererRequest.creative_id) ||
adRendererRequest.context === "PREVIEW" ||
adRendererRequest.context === "STAGE"
) {
this.pluginCache.put(
adRendererRequest.creative_id,
this.instanceContextBuilder(adRendererRequest.creative_id),
this.INSTANCE_CONTEXT_CACHE_EXPIRATION
);
}
const instanceContext: T = await this.pluginCache.get(
adRendererRequest.creative_id
);
const adRendererResponse = await this.onAdContents(
adRendererRequest,
instanceContext as T
);
return res
.header(
this.displayContextHeader,
JSON.stringify(adRendererResponse.displayContext)
)
.status(200)
.send(adRendererResponse.html);
}
}
)
);
}
constructor() {
super();
this.initAdContentsRoute();
this.setErrorHandler();
}
}