@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
125 lines (104 loc) • 4.77 kB
text/typescript
import express from 'express';
import _ from 'lodash';
import { PluginProperty, PluginPropertyResponse } from '../../api/core/plugin/PluginPropertyInterface';
import { Catalog, CatalogResponse, RecommendationsWrapper } from '../../api/datamart';
import { RecommenderRequest } from '../../api/plugin/recommender/RecommenderRequestInterface';
import { BasePlugin, PropertiesWrapper } from '../common/BasePlugin';
export interface RecommenderBaseInstanceContext {
properties: PropertiesWrapper;
}
export abstract class RecommenderPlugin extends BasePlugin<RecommenderBaseInstanceContext> {
instanceContext: Promise<RecommenderBaseInstanceContext>;
constructor() {
super();
// We init the specific route to listen for activity analysis requests
this.initRecommendationRequest();
this.setErrorHandler();
}
// Helper to fetch the activity analyzer resource with caching
async fetchRecommenderCatalogs(recommenderId: string): Promise<Catalog[]> {
const recommenderCatalogsResponse = await super.requestGatewayHelper<CatalogResponse>(
'GET',
`${this.outboundPlatformUrl}/v1/recommenders/${recommenderId}/catalogs`,
);
this.logger.debug(
`Fetched recommender catalogs: ${recommenderId} - ${JSON.stringify(recommenderCatalogsResponse.data)}`,
);
return recommenderCatalogsResponse.data;
}
// Method to build an instance context
// To be overriden to get a cutom behavior
// Helper to fetch the activity analyzer resource with caching
async fetchRecommenderProperties(recommenderId: string): Promise<PluginProperty[]> {
const recommenderPropertyResponse = await super.requestGatewayHelper<PluginPropertyResponse>(
'GET',
`${this.outboundPlatformUrl}/v1/recommenders/${recommenderId}/properties`,
);
this.logger.debug(
`Fetched recommender Properties: ${recommenderId} - ${JSON.stringify(recommenderPropertyResponse.data)}`,
);
return recommenderPropertyResponse.data;
}
// Method to process an Activity Analysis
// This is a default provided implementation
protected async instanceContextBuilder(recommenderId: string): Promise<RecommenderBaseInstanceContext> {
const recommenderProps = await this.fetchRecommenderProperties(recommenderId);
const context: RecommenderBaseInstanceContext = {
properties: new PropertiesWrapper(recommenderProps),
};
return context;
}
protected async getInstanceContext(recommenderId: string): Promise<RecommenderBaseInstanceContext | null> {
if (!this.pluginCache.get(recommenderId)) {
void this.pluginCache.put(
recommenderId,
this.instanceContextBuilder(recommenderId).catch((err) => {
this.logger.error(`Error while caching instance context: ${(err as Error).message}`);
this.pluginCache.del(recommenderId);
throw err;
}),
this.getInstanceContextCacheExpiration(),
);
}
return this.pluginCache.get(recommenderId);
}
// To be overriden by the Plugin to get a custom behavior
protected abstract onRecommendationRequest(
request: RecommenderRequest,
instanceContext: RecommenderBaseInstanceContext | null,
): Promise<RecommendationsWrapper>;
private initRecommendationRequest(): void {
this.app.post(
'/v1/recommendations',
this.asyncMiddleware(async (req: express.Request, res: express.Response) => {
if (!this.httpIsReady()) {
const msg = {
error: 'Plugin not initialized',
};
this.logger.error('POST /v1/recommendations : %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/recommendations : %s', JSON.stringify(msg));
return res.status(500).json(msg);
} else {
this.logger.debug(`POST /v1/recommendations ${JSON.stringify(req.body)}`);
const recommenderRequest = req.body as RecommenderRequest;
if (!this.onRecommendationRequest) {
const errMsg = 'No Recommendation request listener registered!';
this.logger.error(errMsg);
return res.status(500).json({ error: errMsg });
}
const instanceContext: RecommenderBaseInstanceContext | null = await this.getInstanceContext(
recommenderRequest.recommender_id,
);
const pluginResponse = await this.onRecommendationRequest(recommenderRequest, instanceContext);
this.logger.debug(`Returning: ${JSON.stringify(pluginResponse)}`);
return res.status(200).send(JSON.stringify(pluginResponse));
}
}),
);
}
}