UNPKG

3.41 kBJavaScriptView Raw
1const logger = require('./logger').plugins;
2const eventBus = require('./eventBus');
3const schemas = require('./schemas');
4const parentRequire = require('parent-require');
5const engineVersion = '1.2.0';
6const semver = require('semver');
7const prefix = 'express-gateway-plugin-'; // TODO make configurable
8module.exports.load = function ({ config }) {
9 config = config || require('./config');
10 const pluginsSettings = config.systemConfig.plugins || {};
11
12 const loadedPlugins = [];
13 logger.debug(`Loading plugins. Plugin engine version: ${engineVersion}`);
14 for (const pluginName in pluginsSettings) {
15 const settings = pluginsSettings[pluginName] || {};
16 let requireName = pluginName;
17 if (settings.package) {
18 requireName = settings.package;
19 } else if (pluginName.indexOf(prefix) !== 0) {
20 requireName = prefix + pluginName;
21 }
22
23 try {
24 logger.debug(`Loading plugin ${requireName}`);
25 let plugin;
26 try {
27 plugin = require(requireName); // EG loaded as main module
28 } catch (err) {
29 plugin = parentRequire(requireName); // EG loaded as library (after eg gateway create)
30 }
31 if (semver.lt(engineVersion, plugin.version)) { // TODO: versioning of plugins
32 logger.warn(`${plugin.version} is higher than engine version: ${engineVersion}; trying to load`);
33 }
34
35 // register schema and validate settings
36 const validate = schemas.register('plugin', pluginName, plugin.schema);
37 const { isValid, error } = validate(settings);
38 if (!isValid) {
39 // Stop loading plugin.
40 throw new Error(`Failed to validate settings: ${error}`);
41 }
42
43 const services = require('./services');
44 const context = new PluginContext({ settings, config, services });
45 plugin.init(context);
46 loadedPlugins.push(context);
47 logger.info(`Loaded plugin ${pluginName} from package ${requireName}`);
48 } catch (err) {
49 logger.error(`Failed to load plugin ${requireName}: ${err}`);
50 }
51 }
52
53 // Note: All logic to handle different plugin version should be here
54 // Note: Rest of EG code must use only one standard interface
55 return {
56 policies: extract(loadedPlugins, 'policies'),
57 conditions: extract(loadedPlugins, 'conditions'),
58 gatewayRoutes: extract(loadedPlugins, 'gatewayRoutes'),
59 adminRoutes: extract(loadedPlugins, 'adminRoutes'),
60 cliExtensions: extract(loadedPlugins, 'cliExtensions')
61 };
62};
63
64class PluginContext {
65 constructor ({ settings, config, services }) {
66 this.logger = logger;
67 this.services = services;
68 this.settings = settings || {};
69 this.config = config;
70 this.policies = [];
71 this.conditions = [];
72 this.gatewayRoutes = [];
73 this.adminRoutes = [];
74 this.cliExtensions = [];
75 this.eventBus = eventBus;
76 }
77
78 registerPolicy (policy) {
79 this.policies.push(policy);
80 }
81
82 registerCondition (condition) {
83 this.conditions.push(condition);
84 }
85
86 registerGatewayRoute (gatewayRoutesDeclaration) {
87 this.gatewayRoutes.push(gatewayRoutesDeclaration);
88 }
89
90 registerAdminRoute (adminRoutesDeclaration) {
91 this.adminRoutes.push(adminRoutesDeclaration);
92 }
93
94 registerCLIExtension (cliExtension) {
95 this.cliExtensions.push(cliExtension);
96 }
97}
98
99function extract (loadedPlugins, propName) {
100 return loadedPlugins.reduce((result, current) => {
101 return result.concat(current[propName] || []);
102 }, []);
103};