@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
272 lines (238 loc) • 7.27 kB
text/typescript
import * as express from "express";
import * as request from "request";
import * as rp from "request-promise-native";
import * as winston from "winston";
import * as bodyParser from "body-parser";
import { Server } from "http";
import * as cache from "memory-cache";
// Helper request function
export abstract class BasePlugin {
INSTANCE_CONTEXT_CACHE_EXPIRATION: number = 30000;
pluginCache: any;
gatewayHost: string;
gatewayPort: number;
outboundPlatformUrl: string;
app: express.Application;
logger: winston.LoggerInstance;
worker_id: string;
authentication_token: string;
_transport: any = rp;
// Log level update implementation
// This method can be overridden by any subclass
protected onLogLevelUpdate(req: express.Request, res: express.Response) {
if (req.body && req.body.level) {
// Lowering case
const logLevel = req.body.level.toLowerCase();
this.logger.info("Setting log level to " + req.body.level);
this.logger.level = logLevel;
res.status(200).end();
} else {
this.logger.error(
"Incorrect body : Cannot change log level, actual: " + this.logger.level
);
res.status(400).end();
}
}
private initLogLevelUpdateRoute() {
//Route used by the plugin manager to check if the plugin is UP and running
this.app.put(
"/v1/log_level",
this.asyncMiddleware(
async (req: express.Request, res: express.Response) => {
this.onLogLevelUpdate(req, res);
}
)
);
}
// Log level update implementation
// This method can be overridden by any subclass
protected onLogLevelRequest(req: express.Request, res: express.Response) {
res.send({ level: this.logger.level.toUpperCase() });
}
private initLogLevelGetRoute() {
this.app.get(
"/v1/log_level",
this.asyncMiddleware(
async (req: express.Request, res: express.Response) => {
this.onLogLevelRequest(req, res);
}
)
);
}
// Health Status implementation
// This method can be overridden by any subclass
protected onStatusRequest(req: express.Request, res: express.Response) {
//Route used by the plugin manager to check if the plugin is UP and running
this.logger.silly("GET /v1/status");
if (this.worker_id && this.authentication_token) {
res.status(200).end();
} else {
this.logger.error(
`Plugin is not inialized yet, we don't have any worker_id & authentification_token`
);
res.status(503).end();
}
}
private initStatusRoute() {
this.app.get(
"/v1/status",
this.asyncMiddleware(
async (req: express.Request, res: express.Response) => {
this.onStatusRequest(req, res);
}
)
);
}
fetchDataFile(uri: string): Promise<Buffer> {
return this.requestGatewayHelper(
"GET",
`${this.outboundPlatformUrl}/v1/data_file/data`,
undefined,
{ uri: uri },
false,
true
);
}
fetchConfigurationFile(fileName: string): Promise<Buffer> {
return this.requestGatewayHelper(
"GET",
`${this.outboundPlatformUrl}/v1/configuration/technical_name=${fileName}`,
undefined,
undefined,
false,
true
);
}
async requestGatewayHelper(
method: string,
uri: string,
body?: any,
qs?: any,
isJson?: boolean,
isBinary?: boolean
) {
let options: request.OptionsWithUri = {
method: method,
uri: uri,
auth: {
user: this.worker_id,
pass: this.authentication_token,
sendImmediately: true
}
};
// Set the body if provided
options.body = body !== undefined ? body : undefined;
// Set the querystring if provided
options.qs = qs !== undefined ? qs : undefined;
// Set the json flag if provided
options.json = isJson !== undefined ? isJson : true;
// Set the encoding to null if it is binary
options.encoding = (isBinary !== undefined && isBinary) ? null : undefined;
this.logger.silly(`Doing gateway call with ${JSON.stringify(options)}`);
try {
return await this._transport(options);
} catch (e) {
if (e.name === "StatusCodeError") {
const bodyString =
isJson !== undefined && !isJson ? body : JSON.stringify(body);
throw new Error(
`Error while calling ${method} '${uri}' with the request body '${bodyString ||
""}': got a ${e.response.statusCode} ${
e.response.statusMessage
} with the response body ${JSON.stringify(e.response.body)}`
);
} else {
this.logger.error(
`Got an issue while doind a Gateway call: ${e.message} - ${e.stack}`
);
throw e;
}
}
}
// Plugin Init implementation
// This method can be overridden by any subclass
protected onInitRequest(req: express.Request, res: express.Response) {
this.logger.debug("POST /v1/init ", req.body);
if (req.body.authentication_token && req.body.worker_id) {
this.authentication_token = req.body.authentication_token;
this.worker_id = req.body.worker_id;
this.logger.info(
"Update authentication_token with %s",
this.authentication_token
);
res.status(200).end();
} else {
this.logger.error(
`Received /v1/init call without authentification_token or worker_id`
);
res.status(400).end();
}
}
private initInitRoute() {
this.app.post(
"/v1/init",
this.asyncMiddleware(
async (req: express.Request, res: express.Response) => {
this.onInitRequest(req, res);
}
)
);
}
protected asyncMiddleware = (
fn: (
req: express.Request,
res: express.Response,
next: express.NextFunction
) => any
) => (
req: express.Request,
res: express.Response,
next: express.NextFunction
) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
protected setErrorHandler() {
this.app.use(
(
err: any,
req: express.Request,
res: express.Response,
next: express.NextFunction
) => {
this.logger.error(
`Something bad happened : ${err.message} - ${err.stack}`
);
return res.status(500).send(err.message + "\n" + err.stack);
}
);
}
// Method to start the plugin
start() {}
constructor() {
const gatewayHost = process.env.GATEWAY_HOST;
if (gatewayHost) {
this.gatewayHost = gatewayHost;
} else {
this.gatewayHost = "plugin-gateway.platform";
}
const gatewayPort = process.env.GATEWAY_PORT;
if (gatewayPort) {
this.gatewayPort = parseInt(gatewayPort);
} else {
this.gatewayPort = 8080;
}
this.outboundPlatformUrl = `http://${this.gatewayHost}:${this.gatewayPort}`;
this.app = express();
this.app.use(bodyParser.json({ type: "*/*" }));
this.logger = new winston.Logger({
transports: [new winston.transports.Console()],
level: "info"
});
this.pluginCache = cache;
this.pluginCache.clear();
this.initInitRoute();
this.initStatusRoute();
this.initLogLevelUpdateRoute();
this.initLogLevelGetRoute();
}
}