@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
136 lines (112 loc) • 5.04 kB
text/typescript
import express from 'express';
import _ from 'lodash';
import { Creative, CreativeResponse } from '../../../api/core/creative';
import { PluginProperty, PluginPropertyResponse } from '../../../api/core/plugin/PluginPropertyInterface';
import { EmailRendererPluginResponse } from '../../../api/plugin/emailtemplaterenderer/EmailRendererPluginResponse';
import { EmailRenderRequest } from '../../../api/plugin/emailtemplaterenderer/EmailRendererRequestInterface';
import { BasePlugin, PropertiesWrapper } from '../../common';
export interface EmailRendererBaseInstanceContext {
creative: Creative;
properties: PropertiesWrapper;
}
export abstract class EmailRendererPlugin<
T extends EmailRendererBaseInstanceContext = EmailRendererBaseInstanceContext,
> extends BasePlugin<T> {
instanceContext: Promise<T>;
constructor(enableThrottling = false) {
super(enableThrottling);
// We init the specific route to listen for email contents requests
this.initEmailContents();
this.setErrorHandler();
}
// Helper to fetch the creative resource with caching
async fetchCreative(id: string, forceReload = false): Promise<Creative> {
const response = await super.requestGatewayHelper<CreativeResponse>(
'GET',
`${this.outboundPlatformUrl}/v1/creatives/${id}`,
undefined,
{ 'force-reload': forceReload },
);
this.logger.debug(`Fetched Creative: ${id} - ${JSON.stringify(response.data)}`);
return response.data;
}
// Method to build an instance context
// To be overriden to get a cutom behavior
async fetchCreativeProperties(id: string, forceReload = false): Promise<PluginProperty[]> {
const response = await super.requestGatewayHelper<PluginPropertyResponse>(
'GET',
`${this.outboundPlatformUrl}/v1/creatives/${id}/renderer_properties`,
undefined,
{ 'force-reload': forceReload },
);
this.logger.debug(`Fetched Email Templates Properties: ${id} - ${JSON.stringify(response.data)}`);
return response.data;
}
// Method to process an Activity Analysis
// This is a default provided implementation
protected async instanceContextBuilder(creativeId: string, forceReload = false): Promise<T> {
const creativeP = this.fetchCreative(creativeId, forceReload);
const creativePropsP = this.fetchCreativeProperties(creativeId, forceReload);
const results = await Promise.all([creativeP, creativePropsP]);
const creative = results[0];
const creativeProps = results[1];
const context = {
creative: creative,
properties: new PropertiesWrapper(creativeProps),
} as T;
return context;
}
protected async getInstanceContext(creativeId: string, forceReload = false): Promise<T> {
if (!this.pluginCache.get(creativeId) || forceReload) {
void this.pluginCache.put(
creativeId,
this.instanceContextBuilder(creativeId, forceReload).catch((err) => {
this.logger.error(`Error while caching instance context: ${(err as Error).message}`);
this.pluginCache.del(creativeId);
throw err;
}),
this.getInstanceContextCacheExpiration(),
);
}
return this.pluginCache.get(creativeId) as Promise<T>;
}
// To be overriden by the Plugin to get a custom behavior
protected abstract onEmailContents(
request: EmailRenderRequest,
instanceContext: T,
): Promise<EmailRendererPluginResponse>;
private initEmailContents(): void {
this.app.post(
'/v1/email_contents',
this.asyncMiddleware(async (req: express.Request, res: express.Response) => {
if (!this.httpIsReady()) {
const msg = {
error: 'Plugin not initialized',
};
this.logger.error('POST /v1/email_contents : %s', JSON.stringify(msg));
return res.status(500).json(msg);
} else if (!req.body || _.isEmpty(req.body)) {
const msg = {
error: 'Missing request body',
};
this.logger.error('POST /v1/email_contents : %s', JSON.stringify(msg));
return res.status(500).json(msg);
} else {
this.logger.debug(`POST /v1/email_contents ${JSON.stringify(req.body)}`);
const emailRenderRequest = req.body as EmailRenderRequest;
if (!this.onEmailContents) {
const errMsg = 'No Email Renderer listener registered!';
this.logger.error(errMsg);
return res.status(500).send({ error: errMsg });
}
// We flush the Plugin Gateway cache during previews
const forceReload = emailRenderRequest.context === 'PREVIEW' || emailRenderRequest.context === 'STAGE';
const instanceContext = await this.getInstanceContext(emailRenderRequest.creative_id, forceReload);
const response = await this.onEmailContents(emailRenderRequest, instanceContext);
this.logger.debug(`Returning: ${JSON.stringify(response)}`);
return res.status(200).send(JSON.stringify(response));
}
}),
);
}
}