devebot
Version:
Nodejs Microservice Framework
310 lines (308 loc) • 9.7 kB
JavaScript
"use strict";
const Promise = require("bluebird");
const lodash = require("lodash");
const DEFAULT_PORTLET_NAME = "default";
const PORTLETS_COLLECTION_NAME = "portlets";
function createPortletifier() {
function Service(params = {}) {
const {
sandboxBaseConfig,
sandboxConfig
} = params;
//
let pluginConfig;
//
this.getPluginConfig = function () {
pluginConfig = pluginConfig || portletifyConfig(sandboxConfig);
return pluginConfig;
};
//
this.getPortletDescriptors = function (selectedPortlets) {
return _filterPortletDescriptors(this.getPluginConfig(), selectedPortlets);
};
//
this.getPortletBaseConfig = function () {
return sandboxBaseConfig;
};
}
//
return Service;
}
function getPortletDescriptors(sandboxConfig, selectedPortlets) {
return _filterPortletDescriptors(portletifyConfig(sandboxConfig), selectedPortlets);
}
function _filterPortletDescriptors(pluginConfig, selectedPortlets) {
let portletDescriptors = lodash.get(pluginConfig, PORTLETS_COLLECTION_NAME);
if (lodash.isArray(selectedPortlets)) {
portletDescriptors = lodash.pick(portletDescriptors, selectedPortlets);
}
//
const ok = strictPortletConfig(portletDescriptors);
if (!ok) {
throw _newError("DuplicatedPortsInDescriptors", {
message: "pluginConfig.portlets has duplicated ports",
payload: {}
});
}
//
return portletDescriptors;
}
function portletifyConfig(sandboxConfig, globalFieldNames) {
if (!lodash.isPlainObject(sandboxConfig)) {
throw _newError("InvalidSandboxConfigError", {
message: "sandboxConfig must be an object",
payload: {
sandboxConfig
}
});
}
//
if (!lodash.has(sandboxConfig, PORTLETS_COLLECTION_NAME)) {
lodash.set(sandboxConfig, PORTLETS_COLLECTION_NAME, {});
}
const portlets = lodash.get(sandboxConfig, PORTLETS_COLLECTION_NAME);
//
globalFieldNames = globalFieldNames || [];
if (!globalFieldNames.includes(PORTLETS_COLLECTION_NAME)) {
globalFieldNames.push(PORTLETS_COLLECTION_NAME);
}
const defaultPortletConfig = lodash.omit(sandboxConfig, globalFieldNames);
//
if (lodash.has(portlets, DEFAULT_PORTLET_NAME)) {
lodash.defaultsDeep(portlets[DEFAULT_PORTLET_NAME], defaultPortletConfig);
return lodash.omit(sandboxConfig, lodash.keys(defaultPortletConfig));
} else {
if (lodash.size(defaultPortletConfig) > 0 || lodash.size(portlets) == 0) {
lodash.set(portlets, DEFAULT_PORTLET_NAME, defaultPortletConfig);
return lodash.omit(sandboxConfig, lodash.keys(defaultPortletConfig));
}
}
//
return sandboxConfig;
}
function strictPortletConfig(portlets) {
const counter = {};
for (let portletId in portlets) {
const portletDescriptor = portlets[portletId];
if (lodash.get(portletDescriptor, "enabled") == false) {
continue;
}
if (lodash.get(portletDescriptor, ["__metadata__", "enabled"]) == false) {
continue;
}
if (lodash.has(portletDescriptor, "port")) {
const counterId = "" + portletDescriptor.port;
counter[counterId] = counter[counterId] || 0;
counter[counterId] += 1;
}
}
//
const dup = lodash.filter(lodash.values(counter), function (count) {
return count > 1;
});
//
return lodash.size(dup) == 0;
}
/**
* portletForwarder is an interface with hasPortlet() method
* portletArguments ~ { L, T, blockRef }
* @param {*} params
*/
function PortletMixiner(params = {}) {
const self = this;
this._portlets = {};
this._aliases = {};
//
let {
portletBaseConfig,
portletDescriptors
} = params;
const {
portletReferenceHolders,
portletArguments,
PortletConstructor
} = params;
//
const {
pluginConfig,
portletCommonConfig,
portletForwarder
} = params;
let {
portletAvailableChecker
} = params;
//
portletBaseConfig = portletBaseConfig || portletCommonConfig || {};
//
if (lodash.isNil(portletDescriptors)) {
if (lodash.isNil(pluginConfig)) {
throw _newError("UndefinedPortletDescriptors", {
message: "portletDescriptors or pluginConfig must be declared"
});
} else {
// @deprecated
if (!(PORTLETS_COLLECTION_NAME in pluginConfig)) {
throw _newError("InvalidPluginConfigPortlets", {
message: "pluginConfig.portlets not found",
payload: {
fieldName: PORTLETS_COLLECTION_NAME
}
});
}
portletDescriptors = lodash.get(pluginConfig, PORTLETS_COLLECTION_NAME);
}
}
if (!lodash.isPlainObject(portletDescriptors)) {
throw _newError("InvalidPortletDescriptors", {
message: "portletDescriptors must be an object"
});
}
//
if (portletReferenceHolders && !lodash.isPlainObject(portletReferenceHolders)) {
throw _newError("InvalidPortletReferenceHolders", {
message: "portletReferenceHolders must be a plain object"
});
}
//
portletAvailableChecker = portletAvailableChecker || function (portletName) {
return hasPortletOf(portletForwarder, portletName);
};
//
lodash.forOwn(portletDescriptors, function (portletDescriptor, portletKey) {
if (portletDescriptor.enabled !== false) {
const portletName = lodash.get(portletDescriptor, ["__metadata__", "name"], portletKey);
if (portletName != portletKey) {
self._aliases[portletKey] = portletName;
}
const portletConfig = lodash.merge({}, portletBaseConfig, lodash.omit(portletDescriptor, "__metadata__", {}));
//
const portletDependencies = {};
for (const portletReferenceName in portletReferenceHolders) {
const referenceHolder = portletReferenceHolders[portletReferenceName];
if (referenceHolder && referenceHolder.hasPortlet && referenceHolder.hasPortlet(portletName)) {
portletDependencies[portletReferenceName] = referenceHolder.getPortlet(portletName);
}
}
const isPortletAvailable = lodash.size(portletDependencies) == lodash.size(portletReferenceHolders);
//
self._portlets[portletName] = {
available: portletAvailableChecker(portletName) && isPortletAvailable,
processor: new PortletConstructor(Object.assign({
portletConfig,
portletName
}, portletDependencies, portletArguments || {}))
};
}
});
//
this._strictMode = lodash.get(params, "strictMode", false);
}
PortletMixiner.prototype.getPortletNames = function () {
return lodash.keys(this._portlets);
};
PortletMixiner.prototype.resolvePortletName = function (portletName) {
return this._aliases[portletName] || portletName || DEFAULT_PORTLET_NAME;
};
PortletMixiner.prototype.hasPortlet = function (portletName) {
portletName = this.resolvePortletName(portletName);
return this._portlets[portletName] && this._portlets[portletName].available || false;
};
PortletMixiner.prototype.getPortlet = function (portletName) {
portletName = this.resolvePortletName(portletName);
const processor = this._portlets[portletName] && this._portlets[portletName].processor;
if (!processor) {
if (this._strictMode) {
throw _newError("PortletNotFoundError", {
payload: {
portletName,
availablePortlets: lodash.keys(this._portlets)
}
});
}
return undefined;
}
return processor;
};
PortletMixiner.prototype.eachPortlets = function (iteratee, portletNames) {
if (!lodash.isFunction(iteratee)) {
return Promise.reject(_newError("InvalidArgumentError", {
message: "The first argument must be a function",
payload: {
type: typeof iteratee,
value: iteratee
}
}));
}
//
if (lodash.isNil(portletNames)) {
portletNames = this.getPortletNames();
}
if (lodash.isString(portletNames)) {
if (!(portletNames in this._portlets)) {
return Promise.reject(_newError("InvalidArgumentError", {
message: "The second argument is not an available portlet",
payload: {
type: typeof portletNames,
value: portletNames
}
}));
}
portletNames = [portletNames];
}
if (!lodash.isArray(portletNames)) {
return Promise.reject(_newError("InvalidArgumentError", {
message: "The second argument must be an array",
payload: {
type: typeof portletNames,
value: portletNames
}
}));
}
//
const selectedPortletNames = [];
const notfoundPortletNames = [];
for (const portletName of portletNames) {
if (this.hasPortlet(portletName)) {
selectedPortletNames.push(portletName);
} else {
notfoundPortletNames.push(portletName);
}
}
//
if (notfoundPortletNames.length > 0 && this._strictMode) {
return Promise.reject(_newError("InvalidArgumentError", {
message: "There are some portlet names are not available",
payload: {
selectedPortletNames,
notfoundPortletNames
}
}));
}
//
const self = this;
return Promise.mapSeries(selectedPortletNames, function (portletName) {
return iteratee(self.getPortlet(portletName), portletName);
});
};
function hasPortletOf(portletForwarder, portletName) {
if (portletForwarder && lodash.isFunction(portletForwarder.hasPortlet)) {
return portletForwarder.hasPortlet(portletName);
}
return true;
}
function _newError(name, options) {
options = options || {};
const err = new Error(options.message);
err.name = name;
err.payload = options.payload;
return err;
}
module.exports = {
DEFAULT_PORTLET_NAME,
PORTLETS_COLLECTION_NAME,
createPortletifier,
getPortletDescriptors,
portletifyConfig,
strictPortletConfig,
PortletMixiner
};