/************************************************************************* * * Troven CONFIDENTIAL * __________________ * * (c) 2017-2020 Troven Ventures Pty Ltd * All Rights Reserved. * * NOTICE: All information contained herein is, and remains * the property of Troven Pty Ltd and its licensors, * if any. The intellectual and technical concepts contained * herein are proprietary to Troven Pty Ltd * and its suppliers and may be covered by International and Regional Patents, * patents in process, and are protected by trade secret or copyright law. * Dissemination of this information or reproduction of this material * is strictly forbidden unless prior written permission is obtained * from Troven Pty Ltd. */ import { IChassisContext, IOpenAPIv3, IChassisPlugin} from "../interfaces"; import { OpenAPI, Paths, Operation } from "../openapi/"; import jwt_openapi from "../middleware/openapi"; import * as _ from "lodash"; import * as assert from "assert"; import { Router } from "express"; import { PipelinePlugin } from "./pipeline"; import JWTPlugin from "./jwt"; import { jwt } from "../middleware"; import { APIs } from "../helpers"; // import { Vars } from "../helpers"; /** * openapi * --------- * Used when a method is not found * * @type {{name: string, title: string, fn: module.exports.fn}} */ export default class openapi implements IChassisPlugin { name = "openapi"; title = "OpenAPI plugin"; openapi: OpenAPI = null; context: IChassisContext; public api: Router; pipeline: PipelinePlugin; jwt: JWTPlugin; install(context: IChassisContext, options: any) { this.context = context; // merge spec from config.openapi and plugins.openapi let spec = _.extend({}, context.config.openapi, options) as IOpenAPIv3; this.openapi = new OpenAPI(context); this.openapi.load(spec); // ensure all plugins are installed before declarative routing begins ... context.bus.on("api:start", () => { this.pipeline = context.plugins.get("pipeline") as PipelinePlugin; assert(this.pipeline, "pipeline not installed"); this.jwt = context.plugins.get("jwt") as JWTPlugin; if (this.jwt && options.jwt !==false) { context.middleware.set( new jwt(this.jwt) ); context.middleware.set( new jwt_openapi(this.jwt, this.openapi) ); this.context.log( { code: "api:plugin:openapi:jwt:enabled" }); } else { this.context.log( { code: "api:plugin:openapi:jwt:disabled" }); } this.reset(); }); } public reset() { // wire-up express routing this.api = Router( { mergeParams: true }); this.context.api.use(this.api); this.route_paths(this.openapi.paths); } public route_paths(paths: Paths): Router { _.each(paths.ops, (op) => { this.route(op); }); return this.api; } public route(op: Operation): Function { assert(this.api, "openapi race condition"); assert(this.pipeline && this.pipeline.middleware, "can't route before pipeline installed"); let endpoint = this.api[op.actionId]; assert(endpoint, "unknown method: "+op.actionId+" for "+op.resource); let fn = this.pipeline.middleware.fn(op, {}); //transform all OpenAPI {{param}} into Express :param let mount_path = APIs.resource_path_to_express_path(op.resource); let args: any[] = [ mount_path ]; if (op.security) { op.feature.jwt_openapi = { matchers: {} }; let jwt_openapi = this.pipeline.middleware.feature("jwt_openapi"); assert(jwt_openapi, "missing 'jwt_openapi' middleware") let security = jwt_openapi.fn(op,{ security: op.security }); args.push(security); } this.openapi.paths.add(op); // attach fn() to router args.push(fn); endpoint.apply(this.api, args); this.context.log( { code: "api:plugin:openapi", actionId: op.actionId, resource: op.resource, route: mount_path, featureId: op.featureId, operationId: op.operationId, security: op.security?true:false }); return endpoint; } }