@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
424 lines • 20 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AudienceFeedConnectorBasePlugin = exports.BatchedAudienceFeedConnectorBasePlugin = void 0;
const UserAgentIdentifierRealmSelectionInterface_1 = require("./../../api/core/webdomain/UserAgentIdentifierRealmSelectionInterface");
const lodash_1 = __importDefault(require("lodash"));
const BatchUpdateHandler_1 = require("../../api/core/batchupdate/BatchUpdateHandler");
const AudienceFeedConnectorPluginResponseInterface_1 = require("../../api/plugin/audiencefeedconnector/AudienceFeedConnectorPluginResponseInterface");
const AudienceFeedConnectorRequestInterface_1 = require("../../api/plugin/audiencefeedconnector/AudienceFeedConnectorRequestInterface");
const common_1 = require("../common");
class GenericAudienceFeedConnectorBasePlugin extends common_1.BasePlugin {
constructor(enableThrottling = false) {
super(enableThrottling);
this.initExternalSegmentCreation();
this.initExternalSegmentConnection();
this.initUserSegmentUpdate();
this.initTroubleshoot();
this.initAuthenticationStatusQuery();
this.initAuthentication();
this.initLogoutQuery();
this.initDynamicPropertyValuesQuery();
}
async fetchAudienceSegment(feedId) {
const response = await super.requestGatewayHelper('GET', `${this.outboundPlatformUrl}/v1/audience_segment_external_feeds/${feedId}/audience_segment`);
this.logger.debug(`Fetched External Segment: FeedId: ${feedId}`, response);
return response.data;
}
async fetchUserAgentIdentifierRealms(datamartId) {
const response = await super.requestGatewayHelper('GET', `${this.outboundPlatformUrl}/v1/datamarts/${datamartId}/user_agent_identifier_realm_selections`);
this.logger.debug(`Fetched user agent identifier realms for the datamart with id: ${datamartId}`);
return response.data;
}
async checkUserAgentIdentifierRealm(datamartId, realmFilter) {
const realms = await this.fetchUserAgentIdentifierRealms(datamartId);
const hasRealm = realms.some((realm) => {
if ((0, UserAgentIdentifierRealmSelectionInterface_1.isWebDomainRealmFilter)(realmFilter)) {
return realm.realm_type === realmFilter.realmType && realm.web_domain.sld_name === realmFilter.sld_name;
}
else
return realm.realm_type === realmFilter.realmType;
});
if (!hasRealm) {
throw new AudienceFeedConnectorPluginResponseInterface_1.MissingRealmError(datamartId, realmFilter);
}
}
async fetchAudienceFeed(feedId) {
const response = await super.requestGatewayHelper('GET', `${this.outboundPlatformUrl}/v1/audience_segment_external_feeds/${feedId}`);
this.logger.debug(`Fetched External Feed: ${feedId}`, { response });
return response.data;
}
// Method to build an instance context
// To be overriden to get a cutom behavior
async fetchAudienceFeedProperties(feedId) {
const response = await super.requestGatewayHelper('GET', `${this.outboundPlatformUrl}/v1/audience_segment_external_feeds/${feedId}/properties`);
this.logger.debug(`Fetched External Feed Properties: ${feedId}`, { response });
return response.data;
}
async createAudienceFeedProperties(feedId, property) {
const response = await super.requestGatewayHelper('POST', `${this.outboundPlatformUrl}/v1/audience_segment_external_feeds/${feedId}/properties`, property);
this.logger.debug(`Created External Feed Properties: ${feedId}`, { response });
return response.data;
}
async updateAudienceFeedProperties(feedId, property) {
const response = await super.requestGatewayHelper('PUT', `${this.outboundPlatformUrl}/v1/audience_segment_external_feeds/${feedId}/properties/technical_name=${property.technical_name}`, property);
this.logger.debug(`Updated External Feed Properties: ${feedId}`, { response });
return response.data;
}
// This is a default provided implementation
async instanceContextBuilder(feedId) {
const audienceFeedP = this.fetchAudienceFeed(feedId);
const audienceFeedPropsP = this.fetchAudienceFeedProperties(feedId);
const results = await Promise.all([audienceFeedP, audienceFeedPropsP]);
const audienceFeed = results[0];
const audienceFeedProps = results[1];
const context = {
feed: audienceFeed,
feedProperties: new common_1.PropertiesWrapper(audienceFeedProps),
};
return context;
}
onTroubleshoot(request, instanceContext) {
return Promise.resolve({ status: 'not_implemented' });
}
onAuthenticationStatusQuery(request) {
return Promise.resolve({ status: 'not_implemented' });
}
onAuthentication(request) {
return Promise.resolve({ status: 'not_implemented' });
}
onLogout(request) {
return Promise.resolve({ status: 'not_implemented' });
}
onDynamicPropertyValuesQuery(request) {
return Promise.resolve({ status: 'not_implemented' });
}
async getInstanceContext(feedId, forceRefresh) {
if (forceRefresh || !this.pluginCache.get(feedId)) {
void this.pluginCache.put(feedId, this.instanceContextBuilder(feedId).catch((error) => {
this.logger.error(`Error while caching instance context`, error);
this.pluginCache.del(feedId);
throw error;
}), this.getInstanceContextCacheExpiration());
}
return this.pluginCache.get(feedId);
}
emptyBodyFilter(req, res, next) {
if (!req.body || lodash_1.default.isEmpty(req.body)) {
const msg = {
error: 'Missing request body',
};
this.logger.error(`POST /v1/${req.url} : %s`, JSON.stringify(msg));
res.status(500).json(msg);
}
else {
next();
}
}
initExternalSegmentCreation() {
this.app.post('/v1/external_segment_creation', this.emptyBodyFilter, async (req, res) => {
try {
this.logger.debug('POST /v1/external_segment_creation', { request: req.body });
if (!this.httpIsReady()) {
throw new Error('Plugin not initialized');
}
const request = req.body;
if (!this.onExternalSegmentCreation) {
throw new Error('No External Segment Creation listener registered!');
}
const instanceContext = await this.getInstanceContext(request.feed_id, true);
const response = await this.onExternalSegmentCreation(request, instanceContext);
const pluginResponse = {
status: response.status,
visibility: response.visibility || 'PUBLIC',
};
if (response.message) {
pluginResponse.message = response.message;
}
const statusCode = response.status === 'ok' ? 200 : 500;
this.logger.debug(`FeedId: ${request.feed_id} - External segment creation returning: ${statusCode}`, {
response,
});
return res.status(statusCode).send(JSON.stringify(pluginResponse));
}
catch (error) {
this.logger.error('Something bad happened on creation', error);
const pluginResponse = {
status: 'error',
message: `${error.message}`,
visibility: error.visibility === 'PUBLIC' ? 'PUBLIC' : 'PRIVATE',
};
return res.status(500).send(pluginResponse);
}
});
}
initExternalSegmentConnection() {
this.app.post('/v1/external_segment_connection', this.emptyBodyFilter, async (req, res) => {
try {
this.logger.debug('POST /v1/external_segment_connection', { request: req.body });
if (!this.httpIsReady()) {
throw new Error('Plugin not initialized');
}
const request = req.body;
if (!this.onExternalSegmentConnection) {
throw new Error('No External Segment Connection listener registered!');
}
const instanceContext = await this.getInstanceContext(request.feed_id);
const response = await this.onExternalSegmentConnection(request, instanceContext);
const pluginResponse = {
status: response.status,
};
if (response.message) {
pluginResponse.message = response.message;
}
let statusCode;
switch (response.status) {
case 'external_segment_not_ready_yet':
statusCode = 502;
break;
case 'ok':
statusCode = 200;
break;
case 'error':
statusCode = 500;
break;
default:
statusCode = 500;
}
this.logger.debug(`FeedId: ${request.feed_id} - External segment connection returning: ${statusCode}`, {
response,
});
return res.status(statusCode).send(JSON.stringify(pluginResponse));
}
catch (error) {
this.logger.error('Something bad happened on connection', error);
return res.status(500).send({ status: 'error', message: `${error.message}` });
}
});
}
initUserSegmentUpdate() {
this.app.post('/v1/user_segment_update', this.emptyBodyFilter, async (req, res) => {
try {
this.logger.debug('POST /v1/user_segment_update', { request: req.body });
const request = req.body;
if (!this.onUserSegmentUpdate) {
throw new Error('No User Segment Update listener registered!');
}
const instanceContext = await this.getInstanceContext(request.feed_id);
const response = await this.onUserSegmentUpdate(request, instanceContext);
if (response.next_msg_delay_in_ms) {
res.set('x-mics-next-msg-delay', response.next_msg_delay_in_ms.toString());
}
let statusCode;
switch (response.status) {
case 'ok':
statusCode = 200;
break;
case 'error':
statusCode = 500;
break;
case 'retry':
statusCode = 429;
break;
case 'no_eligible_identifier':
statusCode = 400;
break;
default:
statusCode = 500;
}
this.logger.debug(`FeedId: ${request.feed_id} - External segment update returning: ${statusCode}`, {
response,
});
return res.status(statusCode).send(JSON.stringify(response));
}
catch (error) {
this.logger.error('Something bad happened on update', error);
return res.status(500).send({ status: 'error', message: `${error.message}` });
}
});
}
initTroubleshoot() {
this.app.post('/v1/troubleshoot', this.emptyBodyFilter, async (req, res) => {
try {
this.logger.debug('POST /v1/troubleshoot', { request: req.body });
const request = req.body;
if (!AudienceFeedConnectorRequestInterface_1.ExternalSegmentTroubleshootActions.includes(request.action)) {
const response = {
status: 'not_implemented',
message: `Action ${request.action} not supported`,
};
return res.status(400).send(JSON.stringify(response));
}
const instanceContext = await this.getInstanceContext(request.feed_id);
const response = await this.onTroubleshoot(request, instanceContext);
let statusCode;
switch (response.status) {
case 'ok':
statusCode = 200;
break;
case 'error':
statusCode = 500;
break;
case 'not_implemented':
statusCode = 400;
break;
default:
statusCode = 500;
}
this.logger.debug(`FeedId: ${request.feed_id} - Troubleshoot returning: ${statusCode}`, { response });
return res.status(statusCode).send(JSON.stringify(response));
}
catch (error) {
this.logger.error('Something bad happened on troubleshoot', error);
return res.status(500).send({ status: 'error', message: `${error.message}` });
}
});
}
initAuthenticationStatusQuery() {
this.app.post('/v1/authentication_status_queries', this.emptyBodyFilter, async (req, res) => {
try {
const request = req.body;
const response = await this.onAuthenticationStatusQuery(request);
let statusCode;
switch (response.status) {
case 'authenticated':
case 'not_authenticated':
statusCode = 200;
break;
case 'error':
statusCode = 500;
break;
case 'not_implemented':
statusCode = 400;
break;
default:
statusCode = 500;
}
this.logger.debug(`Request: ${JSON.stringify(request)} - Authentication status query returning: ${statusCode}`, {
response,
});
return res.status(statusCode).send(JSON.stringify(response));
}
catch (error) {
this.logger.error('Something bad happened on authentication status query', error);
return res.status(500).send({ status: 'error', message: `${error.message}` });
}
});
}
initAuthentication() {
this.app.post('/v1/authentication', this.emptyBodyFilter, async (req, res) => {
try {
const request = req.body;
const response = await this.onAuthentication(request);
let statusCode;
switch (response.status) {
case 'ok':
statusCode = 200;
break;
case 'error':
statusCode = 500;
break;
case 'not_implemented':
statusCode = 400;
break;
default:
statusCode = 500;
}
this.logger.debug(`Request: ${JSON.stringify(req.body)} - Authentication returning: ${statusCode}`, {
response,
});
return res.status(statusCode).send(JSON.stringify(response));
}
catch (error) {
this.logger.error('Something bad happened on authentication', error);
return res.status(500).send({ status: 'error', message: `${error.message}` });
}
});
}
initLogoutQuery() {
this.app.post('/v1/logout', this.emptyBodyFilter, async (req, res) => {
try {
const request = req.body;
const response = await this.onLogout(request);
let statusCode;
switch (response.status) {
case 'ok':
statusCode = 200;
break;
case 'error':
statusCode = 500;
break;
case 'not_implemented':
statusCode = 400;
break;
default:
statusCode = 500;
}
this.logger.debug(`Request: ${JSON.stringify(request)} - Logout query returning: ${statusCode}`, {
response,
});
return res.status(statusCode).send(JSON.stringify(response));
}
catch (error) {
this.logger.error('Something bad happened on logout query', error);
return res.status(500).send({ status: 'error', message: `${error.message}` });
}
});
}
initDynamicPropertyValuesQuery() {
this.app.post('/v1/dynamic_property_values_queries', this.emptyBodyFilter, async (req, res) => {
try {
const request = req.body;
const response = await this.onDynamicPropertyValuesQuery(request);
let statusCode;
switch (response.status) {
case 'ok':
statusCode = 200;
break;
case 'error':
statusCode = 500;
break;
case 'not_implemented':
statusCode = 400;
break;
default:
statusCode = 500;
}
this.logger.debug(`Request: ${JSON.stringify(request)} - Dynamic property values query returning: ${statusCode}`, {
response,
});
return res.status(statusCode).send(JSON.stringify(response));
}
catch (error) {
this.logger.error('Something bad happened on dynamic property values query', error);
return res.status(500).send({ status: 'error', message: `${error.message}` });
}
});
}
}
class BatchedAudienceFeedConnectorBasePlugin extends GenericAudienceFeedConnectorBasePlugin {
constructor(enableThrottling = false) {
super(enableThrottling);
const batchUpdateHandler = new BatchUpdateHandler_1.BatchUpdateHandler(this.app, this.emptyBodyFilter, this.logger);
batchUpdateHandler.registerRoute(async (request) => {
const instanceContext = await this.getInstanceContext(request.context.feed_id);
return this.onBatchUpdate(request, instanceContext);
});
}
}
exports.BatchedAudienceFeedConnectorBasePlugin = BatchedAudienceFeedConnectorBasePlugin;
class AudienceFeedConnectorBasePlugin extends GenericAudienceFeedConnectorBasePlugin {
constructor(enableThrottling = false) {
super(enableThrottling);
this.initBatchUpdate();
}
initBatchUpdate() {
this.app.post('/v1/batch_update', this.emptyBodyFilter, async (req, res) => {
res.status(500).send({ status: 'error', message: "Plugin doesn't support batch update" });
});
}
}
exports.AudienceFeedConnectorBasePlugin = AudienceFeedConnectorBasePlugin;
//# sourceMappingURL=AudienceFeedConnectorBasePlugin.js.map