2
0
Fork 0
tcrud/lib/config-registry.js
2017-06-10 16:53:29 +02:00

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();