406 lines
11 KiB
JavaScript
406 lines
11 KiB
JavaScript
var path = require('path');
|
|
var fs = require('fs');
|
|
var debug = require('debug')('ff:tcrud:config:registry');
|
|
var configUtil = require('./config-util');
|
|
|
|
var mod = (function () {
|
|
|
|
var phaseInit = true;
|
|
var phaseConfig = false;
|
|
var phaseServer = false;
|
|
var phaseServerUp = false;
|
|
|
|
// Master template for plugins.
|
|
this.masterTemplates = {
|
|
masterTFieldTemplate: {},
|
|
masterTFieldTHelp: {},
|
|
masterTEntityTemplate: {},
|
|
masterTEntityTHelp: {},
|
|
masterTViewTemplate: {},
|
|
masterTViewTHelp: {}
|
|
};
|
|
|
|
// Master config of tcrud
|
|
this.masterConfig = {
|
|
rootTEntity: null,
|
|
rootTMenu: {},
|
|
plugins: [],
|
|
backends: {},
|
|
validators: {},
|
|
clientResources: {
|
|
js: [],
|
|
css: [],
|
|
dss: []
|
|
}
|
|
};
|
|
|
|
this.pluginKeysAllowed = [
|
|
'configPlugin',
|
|
'configTemplate',
|
|
'configServer',
|
|
'configApi',
|
|
'configApiTListExport',
|
|
'configApiTCreateExport',
|
|
'configApiTReadExport',
|
|
'configApiTEditExport',
|
|
'configApiTDeleteExport',
|
|
'configApiTCountExport',
|
|
'configPostBoot',
|
|
'fillTEntity',
|
|
'fillTField',
|
|
'fillTView',
|
|
'createBackend',
|
|
'key','dbModule','query','conn'];
|
|
|
|
|
|
var mergeMaster = function(objectDest,pluginKey) {
|
|
return function(objectSrc) {
|
|
//console.log('mergeMasterSrc:'+JSON.stringify(objectSrc));
|
|
//console.log('mergeMasterDst:'+JSON.stringify(objectDest));
|
|
|
|
var resPre = {keys: 0,values: 0};
|
|
configUtil.countTree(objectDest,resPre);
|
|
|
|
configUtil.copyByTemplate('',objectDest,objectSrc,objectSrc);
|
|
|
|
var resPost = {keys: 0,values: 0};
|
|
configUtil.countTree(objectDest,resPost);
|
|
|
|
// TODO: check pre+post counters so first 2 arrays grow equal !
|
|
|
|
debug('mergeMaster'
|
|
+' start: '+resPre.keys+' -> '+resPre.values
|
|
+' end: '+resPost.keys+' -> '+resPost.values
|
|
+' diff: '+(resPost.keys-resPre.keys)+' -> '+(resPost.values-resPre.values)
|
|
+' plugin: '+pluginKey);
|
|
}
|
|
};
|
|
|
|
var tentityMergeDown = function(prefix,objectDest,objectSrc) {
|
|
var keys = Object.keys(objectSrc);
|
|
for (var i = 0; i < keys.length; i++) {
|
|
var key = keys[i];
|
|
var value = objectSrc[key];
|
|
if (!value) {
|
|
//console.log(prefix+'.'+key+' src object has no value.');
|
|
continue; // no src value
|
|
}
|
|
if (!objectDest[key]) {
|
|
objectDest[key] = configUtil.clone(value);
|
|
} else if (typeof value === 'object') {
|
|
|
|
if (key === 'tchilds') {
|
|
continue;
|
|
}
|
|
//console.log(prefix+'.'+key+' going deeper');
|
|
tentityMergeDown(prefix+'.'+key,objectDest[key],value)
|
|
}
|
|
}
|
|
};
|
|
|
|
var tentityFillTree = function(parent) {
|
|
debug('tentityFillTree.tid: %s',parent.tid);
|
|
for (var key in parent.tmeta.tfields) {
|
|
pluginCall('fillTField',{
|
|
filterValue: function(a,b,c,d) {return configUtil.filterValue(a,b,c,d);},
|
|
tfield: parent.tmeta.tfields[key]
|
|
});
|
|
}
|
|
for (var i = 0; i < parent.tchilds.length; i++) {
|
|
var child = parent.tchilds[i];
|
|
tentityMergeDown('',child,parent);
|
|
tentityFillTree(child);
|
|
}
|
|
pluginCall('fillTEntity',{
|
|
filterValue: function(a,b,c,d) {return configUtil.filterValue(a,b,c,d);},
|
|
tentity: parent
|
|
});
|
|
};
|
|
|
|
var pluginCall = function(fnName,ctx,afterPluginCb) {
|
|
var startTime = new Date().getTime();
|
|
var callCount = 0;
|
|
var keys = Object.keys(masterConfig.plugins);
|
|
for (var i = 0; i < keys.length; i++) {
|
|
var pluginKey = keys[i];
|
|
var plugin = masterConfig.plugins[pluginKey];
|
|
if (plugin.tenable === false) {
|
|
continue;
|
|
}
|
|
ctx.tplugin = plugin;
|
|
if (plugin[fnName]) {
|
|
var result = plugin[fnName](ctx);
|
|
callCount++;
|
|
if (afterPluginCb !== undefined) {
|
|
afterPluginCb(plugin, result);
|
|
}
|
|
}
|
|
}
|
|
debug('pluginCall.function: %s done %s plugins in %s ms.',fnName,callCount,(new Date().getTime()-startTime));
|
|
};
|
|
|
|
var assertPhaseInit = function() {
|
|
if (!phaseInit) {
|
|
throw Error('Not in init phase.')
|
|
}
|
|
};
|
|
|
|
var assertPhaseConfig = function() {
|
|
if (!phaseConfig) {
|
|
throw Error('Not in config phase.')
|
|
}
|
|
};
|
|
|
|
var assertPhaseServer = function() {
|
|
if (!phaseServer) {
|
|
throw Error('Not in server phase.')
|
|
}
|
|
};
|
|
|
|
var assertPhaseServerUp = function() {
|
|
if (!phaseServerUp) {
|
|
throw Error('Not in serverUp phase.')
|
|
}
|
|
};
|
|
|
|
var pluginLoad = function(plugin) {
|
|
assertPhaseInit();
|
|
if (plugin === undefined) {
|
|
throw new Error('No object given');
|
|
}
|
|
if (plugin.configPlugin === undefined) {
|
|
throw new Error('Object is not a plugin: Missing configPlugin()');
|
|
}
|
|
// run config to have key
|
|
var ctx = {
|
|
key: null,
|
|
description: null,
|
|
dependencies: [],
|
|
localDir: null,
|
|
localConfigTemplate: null
|
|
}
|
|
plugin.configPlugin(ctx);
|
|
if (ctx.key === null) {
|
|
throw new Error('Plugin does not provide pluginKey.');
|
|
}
|
|
|
|
// Validate plugin before injection
|
|
for (var objectKey in plugin) {
|
|
if (objectKey.charAt(0) === '_') {
|
|
continue; // tmp
|
|
}
|
|
if (pluginKeysAllowed.indexOf(objectKey) !== -1) {
|
|
continue;
|
|
}
|
|
throw new Error('Illegal plugin objectKey: '+objectKey+' plugin: '+plugin.tmeta.key);
|
|
}
|
|
|
|
plugin['tmeta'] = ctx;
|
|
plugin['troutes'] = [];
|
|
plugin['tenable'] = false;
|
|
|
|
if (ctx.localConfigTemplate) {
|
|
var pluginJsonFile = path.join(ctx.localDir,ctx.localConfigTemplate);
|
|
var pluginJsonTemplate = fs.readFileSync(pluginJsonFile, 'utf8');
|
|
plugin['configTemplateLocal'] = function (ctx) {
|
|
debug('configTemplateLocal file %s',pluginJsonFile);
|
|
ctx.mergeMaster(JSON.parse(pluginJsonTemplate));
|
|
};
|
|
debugPostfix = ' localConfigTemplate:\t'+pluginJsonFile;
|
|
}
|
|
//masterConfig.plugins[plugin.tmeta.key]=plugin;
|
|
masterConfig.plugins.push(plugin);
|
|
debug('pluginLoad %s',plugin.tmeta.key);
|
|
};
|
|
|
|
var pluginLoadTree = function (plugins) {
|
|
var keys = Object.keys(plugins);
|
|
var result = [];
|
|
for (var i = 0; i < keys.length; i++) {
|
|
var key = keys[i];
|
|
var value = plugins[key];
|
|
|
|
if (typeof value === 'function') {
|
|
pluginLoad(new value());
|
|
} else {
|
|
pluginLoadTree(value);
|
|
}
|
|
}
|
|
};
|
|
|
|
var pluginEnable = function(pluginFilter, value) {
|
|
assertPhaseInit();
|
|
if (pluginFilter === undefined) {
|
|
throw new Error('No pluginKey given');
|
|
}
|
|
if (value === undefined) {
|
|
value = true;
|
|
}
|
|
var keys = Object.keys(masterConfig.plugins);
|
|
for (var i = 0; i < keys.length; i++) {
|
|
var pluginIdx = keys[i];
|
|
var plugin = masterConfig.plugins[pluginIdx];
|
|
var pluginKey = plugin.tmeta.key;
|
|
|
|
if (pluginKey.match(pluginFilter)) {
|
|
debug('pluginEnable plugin: '+pluginKey+' value: '+value);
|
|
plugin.tenable = value;
|
|
}
|
|
}
|
|
};
|
|
|
|
var pluginIndexOf = function(checkKey) {
|
|
var keys = Object.keys(masterConfig.plugins);
|
|
for (var i = 0; i < keys.length; i++) {
|
|
var pluginKey = keys[i];
|
|
var plugin = masterConfig.plugins[pluginKey];
|
|
if (plugin.tmeta.key === checkKey) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
var pluginOrder = function() {
|
|
|
|
var keys = Object.keys(masterConfig.plugins);
|
|
for (var pluginId = 0; pluginId < keys.length; pluginId++) {
|
|
var pluginKey = keys[pluginId];
|
|
var plugin = masterConfig.plugins[pluginKey];
|
|
if (plugin.tmeta.dependencies.length === 0) {
|
|
continue;
|
|
}
|
|
for (var depId in plugin.tmeta.dependencies) {
|
|
var dep = plugin.tmeta.dependencies[depId];
|
|
var depIdx = pluginIndexOf(dep);
|
|
|
|
|
|
if (depIdx === -1) {
|
|
throw new Error('Missing plugin: '+dep+' requested by: '+plugin.tmeta.key);
|
|
}
|
|
if (depIdx < pluginId) {
|
|
continue;
|
|
}
|
|
debug('pluginOrder dependency move: '+dep+' before: '+plugin.tmeta.key);
|
|
|
|
var depMove = masterConfig.plugins[depIdx];
|
|
var oldPlugins = masterConfig.plugins;
|
|
var newPlugins = [];
|
|
|
|
newPlugins = oldPlugins.slice(0,pluginId);
|
|
newPlugins.push(depMove);
|
|
newPlugins = newPlugins.concat(oldPlugins.slice(pluginId,depIdx));
|
|
newPlugins = newPlugins.concat(oldPlugins.slice(depIdx+1));
|
|
|
|
masterConfig.plugins = newPlugins;
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return function ConfigRegistry() {
|
|
|
|
this.assertPhaseInit = assertPhaseInit;
|
|
this.assertPhaseConfig = assertPhaseConfig;
|
|
this.assertPhaseServer = assertPhaseServer;
|
|
this.assertPhaseServerUp = assertPhaseServerUp;
|
|
|
|
this.pluginCall = pluginCall;
|
|
this.pluginLoad = pluginLoad;
|
|
this.pluginLoadTree = pluginLoadTree;
|
|
this.pluginEnable = pluginEnable;
|
|
|
|
this.phaseNext = function() {
|
|
if (phaseInit) {
|
|
debug('phaseNext init->config');
|
|
phaseInit = false;
|
|
phaseConfig = true;
|
|
|
|
debug('pluginOrder start');
|
|
while (!pluginOrder()) {
|
|
}
|
|
debug('pluginOrder done');
|
|
|
|
var ctx = {
|
|
mergeMaster: function(template) {
|
|
mergeMaster(masterTemplates,ctx.tplugin.tmeta.key)(template);
|
|
}
|
|
};
|
|
pluginCall('configTemplate',ctx);
|
|
pluginCall('configTemplateLocal',ctx);
|
|
pluginCall('createBackend',{},function (plugin,backend) {
|
|
masterConfig.backends[backend.getKey()]=backend;
|
|
debug('createBackend: %s',backend.getKey());
|
|
});
|
|
|
|
} else if (phaseConfig) {
|
|
debug('phaseNext config->server');
|
|
phaseConfig = false;
|
|
phaseServer = true;
|
|
|
|
tentityFillTree(masterConfig.rootTEntity);
|
|
|
|
} else if (phaseServer) {
|
|
debug('phaseNext server->serverUp');
|
|
phaseServer = false;
|
|
phaseServerUp = true;
|
|
|
|
} else if (phaseServerUp) {
|
|
debug('phaseNext serverUp->done');
|
|
phaseServerUp = false;
|
|
|
|
} else {
|
|
throw new Error('All phases are done.');
|
|
}
|
|
};
|
|
|
|
this.getRootTEntity = function() {
|
|
if (masterConfig.rootTEntity === null) {
|
|
masterConfig.rootTEntity = configUtil.clone(masterTemplates.masterTEntityTemplate);
|
|
masterConfig.rootTEntity.tid = 'root';
|
|
debug('getRootTEntity.created');
|
|
}
|
|
return masterConfig.rootTEntity;
|
|
};
|
|
|
|
this.getMasterTemplates = function() {
|
|
return masterTemplates;
|
|
};
|
|
|
|
this.getMasterConfig = function() {
|
|
return masterConfig;
|
|
}
|
|
|
|
this.registrateClientResource = function(clientResource, resourceType) {
|
|
if (clientResource === undefined) {
|
|
throw new Error('No resource provided');
|
|
}
|
|
if (resourceType === undefined) {
|
|
throw new Error('No resource provided');
|
|
}
|
|
debug('registrateClientResource: '+clientResource+' type: '+resourceType);
|
|
masterConfig.clientResources[resourceType].push(clientResource);
|
|
};
|
|
|
|
this.createClientResourceFetchList = function() {
|
|
var fetchList = [];
|
|
for (var clientResourceIdx in masterConfig.clientResources.js) {
|
|
var url = masterConfig.clientResources.js[clientResourceIdx];
|
|
fetchList.push({url:url,type:'js'});
|
|
}
|
|
for (var clientResourceIdx in masterConfig.clientResources.css) {
|
|
var url = masterConfig.clientResources.css[clientResourceIdx];
|
|
fetchList.push({url:url,type:'css'});
|
|
}
|
|
for (var clientResourceIdx in masterConfig.clientResources.dss) {
|
|
var url = masterConfig.clientResources.dss[clientResourceIdx];
|
|
fetchList.push({url:url,type:'dss'});
|
|
}
|
|
return fetchList;
|
|
};
|
|
};
|
|
})();
|
|
|
|
module.exports = new mod();
|