UNPKG

devebot

Version:

Nodejs Microservice Framework

413 lines (409 loc) 13.6 kB
"use strict"; const lodash = require("lodash"); const util = require("util"); const loader = require("../utils/loader"); const chores = require("../utils/chores"); const blockRef = chores.getBlockRef(__filename); const _isUpgradeSupported = chores.isUpgradeSupported.bind(chores); const FRAMEWORK_PLUGIN_LABEL = "plugin"; function BridgeLoader(params = {}) { const loggingFactory = params.loggingFactory.branch(blockRef); const L = loggingFactory.getLogger(); const T = loggingFactory.getTracer(); const CTX = lodash.assign({ L, T }, lodash.pick(params, ["issueInspector", "nameResolver", "objectDecorator"])); L && L.has("silly") && L.log("silly", T && T.toMessage({ tags: [blockRef, "constructor-begin"], text: " + constructor start ..." })); L && L.has("dunce") && L.log("dunce", T && T.add(params).toMessage({ text: " + bridgeLoader start with bridgeList: ${bridgeList}" })); this.loadDialects = function (dialectMap, dialectOptions, optType) { dialectMap = dialectMap || {}; lodash.defaultsDeep(dialectMap, buildBridgeDialects(CTX, params.bridgeList, dialectOptions, optType)); return dialectMap; }; this.loadMetadata = function (metadataMap, dialectOptions) { metadataMap = metadataMap || {}; const bridgeDescriptors = loadBridgeConstructors(CTX, params.bridgeList); lodash.defaultsDeep(metadataMap, lodash.mapValues(bridgeDescriptors, function (entrypoint) { const construktor = lodash.get(entrypoint, "construktor", {}); const metadata = construktor.metadata || construktor.metainfo || construktor.metainf || {}; metadata.name = entrypoint.name; return metadata; })); return metadataMap; }; L && L.has("silly") && L.log("silly", T && T.toMessage({ tags: [blockRef, "constructor-end"], text: " - constructor has finished" })); } BridgeLoader.argumentSchema = { "$id": "bridgeLoader", "type": "object", "properties": { "bridgeList": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string" }, "path": { "type": "string" } }, "required": ["name", "path"] } }, "issueInspector": { "type": "object" }, "nameResolver": { "type": "object" }, "loggingFactory": { "type": "object" }, "objectDecorator": { "type": "object" } } }; module.exports = BridgeLoader; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ private members function loadBridgeContructor(ctx, bridgeRef) { const { L, T, issueInspector, nameResolver } = ctx || this || {}; bridgeRef = bridgeRef || {}; const bridgePath = bridgeRef.path; let bridgeName = nameResolver.getOriginalNameOf(bridgeRef.name, bridgeRef.type); let bridgeCode = nameResolver.getDefaultAliasOf(bridgeRef.name, bridgeRef.type); if (!_isUpgradeSupported("refining-name-resolver")) { bridgeName = nameResolver.getOriginalName(bridgeRef); bridgeCode = nameResolver.getDefaultAlias(bridgeRef); } L && L.has("dunce") && L.log("dunce", T && T.add(bridgeRef).toMessage({ text: " - bridge constructor (${name}) loading is started" })); const result = {}; if (typeof bridgeCode !== "string") return result; const opStatus = lodash.assign({ type: "DIALECT", code: bridgeCode }, bridgeRef); try { const bridgeConstructor = loader(bridgePath, { stopWhenError: true }); L && L.has("dunce") && L.log("dunce", T && T.add(bridgeRef).toMessage({ text: " - bridge constructor (${name}) loading has done." })); if (lodash.isFunction(bridgeConstructor)) { result[bridgeCode] = { name: bridgeName, construktor: bridgeConstructor }; } else { L && L.has("dunce") && L.log("dunce", T && T.add(bridgeRef).toMessage({ text: " - bridge '${name}' is not a constructor" })); throw new Error(`The module file ${bridgePath} does not contain a function`); } } catch (err) { L && L.has("dunce") && L.log("dunce", T && T.add(bridgeRef).toMessage({ text: " - bridge constructor (${name}) loading has failed" })); opStatus.hasError = true; opStatus.stack = err.stack; } issueInspector.collect(opStatus); return result; } function loadBridgeConstructors(ctx, bridgeList) { const { L, T } = ctx || this || {}; bridgeList = lodash.isArray(bridgeList) ? bridgeList : []; bridgeList = lodash.filter(bridgeList, function (bridgeRef) { return lodash.isString(bridgeRef.name) && lodash.isString(bridgeRef.path); }); L && L.has("dunce") && L.log("dunce", T && T.add({ bridgeList }).toMessage({ text: " - load a list of bridge constructors: ${bridgeList}" })); const bridgeConstructors = {}; bridgeList.forEach(function (bridgeRef) { lodash.assign(bridgeConstructors, loadBridgeContructor(ctx, bridgeRef)); }); L && L.has("dunce") && L.log("dunce", T && T.add({ bridgeConstructorNames: lodash.keys(bridgeConstructors) }).toMessage({ text: " - bridge constructors have been loaded: ${bridgeConstructorNames}" })); return bridgeConstructors; } function buildBridgeDialect(ctx, dialectOpts) { const { L, T, issueInspector, nameResolver, objectDecorator } = ctx || this || {}; const { pluginName, bridgeCode, bridgeRecord, optType } = dialectOpts; const result = {}; if (!lodash.isString(bridgeCode)) { L && L.has("dunce") && L.log("dunce", T && T.toMessage({ text: " - bridgeCode is invalid (not a string)" })); return result; } else { L && L.has("dunce") && L.log("dunce", T && T.add({ dialectOpts }).toMessage({ text: " - buildBridgeDialect() with parameters: ${dialectOpts}" })); } const dialectName = lodash.get(dialectOpts, "dialectName", bridgeCode + "Wrapper"); L && L.has("dunce") && L.log("dunce", T && T.add({ dialectName }).toMessage({ text: " - building bridgeDialect (${dialectName}) is started" })); let crateScope = pluginName; let crateName = [bridgeCode, dialectName].join("#"); let sectorRef = [crateScope, crateName].join(chores.getSeparator()); let uniqueName = [pluginName, bridgeRecord.name, dialectName].join(chores.getSeparator()); if (!_isUpgradeSupported("bridge-full-ref")) { crateScope = bridgeRecord.name; crateName = dialectName; sectorRef = [crateScope, crateName].join(chores.getSeparator()); uniqueName = [bridgeRecord.name, dialectName].join(chores.getSeparator()); } const bridgeConstructor = bridgeRecord.construktor; if (!lodash.isFunction(bridgeConstructor)) { L && L.has("dunce") && L.log("dunce", T && T.toMessage({ text: " - bridgeConstructor is invalid (not a function)" })); return result; } let configPath; if (!_isUpgradeSupported("bridge-full-ref")) { switch (optType) { case 0: configPath = ["sandboxConfig", "bridges", dialectName, bridgeCode]; break; case 1: configPath = ["sandboxConfig", "bridges", bridgeCode, dialectName]; break; default: configPath = ["sandboxConfig", "bridges", bridgeCode]; } } else { let pluginAlias = pluginName; if (_isUpgradeSupported("standardizing-config")) { pluginAlias = nameResolver.getDefaultAliasOf(pluginName, FRAMEWORK_PLUGIN_LABEL); } configPath = ["sandboxConfig", "bridges", bridgeCode, pluginAlias, dialectName]; } L && L.has("silly") && L.log("silly", T && T.add({ configPath }).toMessage({ tags: [sectorRef, "config-path"], text: " - configPath: ${configPath}" })); function dialectConstructor(kwargs = {}) { const newFeatures = lodash.get(kwargs, ["profileConfig", "newFeatures", dialectName], null) || lodash.get(kwargs, ["profileConfig", "newFeatures", bridgeCode], {}); if (newFeatures.logoliteEnabled !== false) { const loggingFactory = kwargs.loggingFactory.branch(sectorRef); this.logger = loggingFactory.getLogger(); this.tracer = loggingFactory.getTracer(); } else { this.logger = kwargs.loggingFactory.getLogger({ sector: sectorRef }); } L && L.has("silly") && L.log("silly", T && T.add({ dialectName, newFeatures }).toMessage({ tags: [sectorRef, "apply-features"], text: " - newFeatures[${dialectName}]: ${newFeatures}" })); const opStatus = { stage: "instantiating", type: "DIALECT", name: dialectName, code: bridgeCode }; try { if (newFeatures.logoliteEnabled !== false) { L && L.has("silly") && L.log("silly", T && T.toMessage({ tags: [sectorRef, "constructor-begin"], text: " + constructor start ..." })); } bridgeConstructor.call(this, lodash.get(kwargs, configPath, {})); if (newFeatures.logoliteEnabled !== false) { L && L.has("silly") && L.log("silly", T && T.toMessage({ tags: [sectorRef, "constructor-end"], text: " - constructor has finished" })); } } catch (err) { L && L.has("silly") && L.log("silly", T && T.add({ bridgeCode }).toMessage({ tags: [sectorRef, "constructor-failed"], text: " - bridgeConstructor (${bridgeCode}) call has failed" })); opStatus.hasError = true; opStatus.stack = err.stack; } issueInspector.collect(opStatus); } util.inherits(dialectConstructor, bridgeConstructor); dialectConstructor.argumentSchema = { "$id": uniqueName, "type": "object", "properties": { "sandboxName": { "type": "string" }, "sandboxConfig": { "type": "object" }, "profileName": { "type": "string" }, "profileConfig": { "type": "object" }, "loggingFactory": { "type": "object" } } }; const construktor = objectDecorator.wrapBridgeDialect(dialectConstructor, { pluginName: pluginName, bridgeCode: bridgeCode, dialectName: dialectName }); result[uniqueName] = { crateScope: crateScope, name: crateName, construktor: construktor }; L && L.has("dunce") && L.log("dunce", T && T.add({ dialectName }).toMessage({ text: " - building bridgeDialect (${dialectName}) has done." })); return result; } function buildBridgeDialects(ctx, bridgeList, dialectOptions, optType) { const { L, T, nameResolver } = ctx || this || {}; optType = lodash.isNumber(optType) ? optType : 0; L && L.has("silly") && L.log("silly", T && T.add({ bridgeList }).toMessage({ text: " - bridgeDialects will be built: ${bridgeList}" })); const bridgeConstructors = loadBridgeConstructors(ctx, bridgeList); if (lodash.isEmpty(dialectOptions)) { L && L.has("silly") && L.log("silly", T && T.toMessage({ text: " - dialectOptions is not provided, nothing is created" })); } else { L && L.has("silly") && L.log("silly", T && T.add({ dialectOptions }).toMessage({ text: " - dialectInstances will be built with options: ${dialectOptions}" })); } const bridgeDialects = {}; if (!_isUpgradeSupported("bridge-full-ref")) { switch (optType) { case 0: lodash.forOwn(dialectOptions, function (dialectConfig, dialectName) { const bridgeCode = lodash.findKey(dialectConfig, function (o, k) { return lodash.isObject(o) && bridgeConstructors[k]; }); if (bridgeCode) { lodash.assign(bridgeDialects, buildBridgeDialect(ctx, { bridgeCode, bridgeRecord: bridgeConstructors[bridgeCode], dialectName, optType })); } }); break; case 1: lodash.forOwn(dialectOptions, function (dialectMap, bridgeCode) { lodash.forOwn(dialectMap, function (dialectConfig, dialectName) { lodash.assign(bridgeDialects, buildBridgeDialect(ctx, { bridgeCode, bridgeRecord: bridgeConstructors[bridgeCode], dialectName, optType })); }); }); break; default: lodash.forOwn(dialectOptions, function (bridgeConfig, bridgeCode) { lodash.assign(bridgeDialects, buildBridgeDialect(ctx, { bridgeCode, bridgeRecord: bridgeConstructors[bridgeCode], dialectName: bridgeCode + "Wrapper", optType })); }); } } else { lodash.forOwn(dialectOptions, function (bridgeMap, bridgeCode) { if (!bridgeCode || !bridgeConstructors[bridgeCode]) return; lodash.forOwn(bridgeMap, function (pluginMap, pluginName) { pluginName = nameResolver.getOriginalNameOf(pluginName, FRAMEWORK_PLUGIN_LABEL); lodash.forOwn(pluginMap, function (dialectConfig, dialectName) { lodash.assign(bridgeDialects, buildBridgeDialect(ctx, { pluginName, bridgeCode, bridgeRecord: bridgeConstructors[bridgeCode], dialectName, optType })); }); }); }); } L && L.has("silly") && L.log("silly", T && T.add({ bridgeDialectNames: lodash.keys(bridgeDialects) }).toMessage({ text: " - bridgeDialects have been built: ${bridgeDialectNames}" })); return bridgeDialects; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ private members