@haystacks/async
Version:
A framework to build any number or any kind of native application or automation solution.
905 lines (874 loc) • 66.6 kB
JavaScript
/**
* @file dataBroker.js
* @module dataBroker
* @description Contains all of the lower level data processing functions,
* and also acts as an interface for calling the fileOperations.
* @requires module:ruleBroker
* @requires module:configurator
* @requires module:loggers
* @requires module:data
* @requires {@link https://www.npmjs.com/package/@haystacks/constants|@haystacks/constants}
* @requires {@link https://www.npmjs.com/package/path|path}
* @author Seth Hollingsead
* @date 2021/10/15
* @copyright Copyright © 2022-… by Seth Hollingsead. All rights reserved
*/
// Internal imports
import ruleBroker from './ruleBroker.js';
import configurator from '../executrix/configurator.js';
import loggers from '../executrix/loggers.js';
import D from '../structures/data.js';
// External imports
import hayConst from '@haystacks/constants';
import path from 'path';
const {bas, biz, cfg, gen, msg, num, sys, wrd} = hayConst;
const baseFileName = path.basename(import.meta.url, path.extname(import.meta.url));
// framework.brokers.dataBroker.
const namespacePrefix = wrd.cframework + bas.cDot + wrd.cbrokers + bas.cDot + baseFileName + bas.cDot;
/**
* @function addPluginConfigurationData
* @description Merges plugin defined configuration data with the system defined configuration data, by calling the configurator.
* This function is a wrapper for the same function in the configurator.
* @param {string} pluginName The name of the current plugin these configuration settings belong to.
* @param {object} pluginConfigData A JSON object that contains all of the configuration settings for the current plugin.
* @return {boolean} True or False to indicate if the merge was successful or not.
* @author Seth Hollingsead
* @date 2022/10/24
*/
async function addPluginConfigurationData(pluginName, pluginConfigData) {
let functionName = addPluginConfigurationData.name;
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
// pluginName is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cpluginNameIs + pluginName);
// pluginConfigData is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cpluginConfigDataIs + JSON.stringify(pluginConfigData));
let returnData = false;
returnData = await configurator.addPluginConfigurationData(pluginName, pluginConfigData);
await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData);
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
return returnData;
}
/**
* @function scanDataPath
* @description Scans the specified path and returns a collection
* of all the files contained recursively within that path and all sub-folders.
* @param {string} dataPath The path that should be recursively scanned for files in all the folders.
* @return {array<string>} An array of strings that each have the full path and file name
* at all levels of the specified path including sub-folders.
* @author Seth Hollingsead
* @date 2021/10/15
*/
async function scanDataPath(dataPath) {
let functionName = scanDataPath.name;
// console.log(`BEGIN ${namespacePrefix}${functionName} function`);
// console.log(`dataPath is: ${dataPath}`);
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
await loggers.consoleLog(namespacePrefix + functionName, msg.cdataPathIs + dataPath);
let rules = [biz.cswapBackSlashToForwardSlash, biz.creadDirectoryContents];
let filesFound = [];
// console.log(`execute business rules: ${JSON.stringify(rules)}`);
filesFound = await ruleBroker.processRules([dataPath, ''], rules);
await loggers.consoleLog(namespacePrefix + functionName, msg.cfilesFoundIs + JSON.stringify(filesFound));
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
// console.log(`filesFound is: ${JSON.stringify(filesFound)}`);
// console.log(`END ${namespacePrefix}${functionName} function`);
return filesFound;
}
/**
* @function findUniversalDebugConfigSetting
* @description Determines if there is any True setting for the debug settings
* in either the application system config or the framework system config files.
* @param {array<string>} appConfigFilesToLoad The list of files for the app config files.
* @param {array<string>} frameworkConfigFilesToLoad The list of files for the framework config files.
* @return {boolean} A True or False value to indicate if debug setting value is set in
* either the app config system file or the framework config system file.
* @author Seth Hollingsead
* @date 2022/01/18
*/
async function findUniversalDebugConfigSetting(appConfigFilesToLoad, frameworkConfigFilesToLoad) {
// let functionName = findUniversalDebugConfigSetting.name;
// console.log(`BEGIN ${namespacePrefix}${functionName} function`);
// console.log(`appConfigFilesToLoad is: ${JSON.stringify(appConfigFilesToLoad)}`);
// console.log(`frameworkConfigFilesToLoad is: ${JSON.stringify(frameworkConfigFilesToLoad)}`);
let universalDebugConfigSetting = false;
let appConfigDebugSetting = false;
let frameworkConfigDebugSetting = false;
appConfigDebugSetting = await findIndividualDebugConfigSetting(appConfigFilesToLoad);
frameworkConfigDebugSetting = await findIndividualDebugConfigSetting(frameworkConfigFilesToLoad);
if (appConfigDebugSetting === true || frameworkConfigDebugSetting === true) {
universalDebugConfigSetting = true;
}
// console.log(`universalDebugConfigSetting is: ${universalDebugConfigSetting}`);
// console.log(`END ${namespacePrefix}${functionName} function`);
return universalDebugConfigSetting;
}
/**
* @function findIndividualDebugConfigSetting
* @description Finds if a debugSetting is set for a particular set of config files.
* @param {array<string>} filesToLoad A list of files that should be searched for
* a system config file and then for a debugSetting in that file.
* @return {boolean} A True or False value to indicate what the value of
* the debug setting is for the set of system config files.
* @author Seth Hollingsead
* @date 2022/01/18
*/
async function findIndividualDebugConfigSetting(filesToLoad) {
// let functionName = findIndividualDebugConfigSetting.name;
// console.log(`BEGIN ${namespacePrefix}${functionName} function`);
// console.log(`filesToLoad is: ${JSON.stringify(filesToLoad)}`);
let individualDebugConfigSetting = false;
let foundSystemData = false;
let systemConfigFileName = sys.csystemConfigFileName; // 'framework.system.json';
let applicationConfigFileName = sys.capplicationConfigFileName; // 'application.system.json';
let multiMergedData = {};
let systemDotDebugSettings = wrd.csystem + bas.cDot + cfg.cdebugSettings;
for (const element of filesToLoad) {
let fileToLoad = element;
// console.log('fileToLoad is: ' + fileToLoad);
if (fileToLoad.includes(systemConfigFileName) || fileToLoad.includes(applicationConfigFileName)) {
let dataFile = await preprocessJsonFile(fileToLoad);
multiMergedData[wrd.csystem] = {};
multiMergedData[wrd.csystem] = dataFile;
foundSystemData = true;
} // End-if (fileToLoad.includes(systemConfigFileName) || fileToLoad.includes(applicationConfigFileName))
if (foundSystemData === true) {
break;
}
} // End-for (const element of filesToLoad)
if (multiMergedData[wrd.csystem]) {
if (multiMergedData[wrd.csystem][systemDotDebugSettings]) {
individualDebugConfigSetting = true;
}
} // End-if (multiMergedData[wrd.csystem])
// console.log(`individualDebugConfigSetting is: ${individualDebugConfigSetting}`);
// console.log(`END ${namespacePrefix}${functionName} function`);
return individualDebugConfigSetting;
}
/**
* @function loadAllCsvData
* @description Loads al of the contents of all files and folders and sub-folders at the specified path and builds a list of files to load,
* then loads them accordingly in the D.contextName_fileName.
* @param {array<string>} filesToLoad The data structure containing all of the files to load data from.
* @param {string} contextName The context name that should be used when adding data to the D data structure.
* @return {object} The data in a JSON object after it was loaded from the file.
* @author Seth Hollingsead
* @date 2022/01/27
*/
async function loadAllCsvData(filesToLoad, contextName) {
let functionName = loadAllCsvData.name;
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
// filesToLoad is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cfilesToLoadIs + JSON.stringify(filesToLoad));
// contextName is:
await loggers.consoleLog(namespacePrefix + functionName, msg.ccontextNameIs + contextName);
// let rules = [biz.cgetFileNameFromPath, biz.cremoveFileExtensionFromFileName];
let parsedDataFile;
for (const element of filesToLoad) {
let fileToLoad = element;
// File to load is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cFileToLoadIs + fileToLoad);
// NOTE: We still need a filename to use as a context for the page data that we just loaded.
// A context name will be composed of the input context name with the file name we are processing
// which tells us where we will put the data in the D[contextName] sub-structure.
let fileExtension = await ruleBroker.processRules([fileToLoad, ''], [biz.cgetFileExtension, biz.cremoveDotFromFileExtension]);
// fileExtension is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cfileExtensionIs + fileExtension);
if (fileExtension === gen.ccsv || fileExtension === gen.cCsv || fileExtension === gen.cCSV) {
// execute business rules:
// loggers.consoleLog(namespacePrefix + functionName, msg.cexecuteBusinessRulesColon + JSON.stringify(rules));
// This next line is commented out because it was resulting in colors_colors, which didn't make any sense.
// contextName = contextName + bas.cUnderscore + ruleBroker.processRules([fileToLoad, ''], rules);
// contextName is:
await loggers.consoleLog(namespacePrefix + functionName, msg.ccontextNameIs + contextName);
let dataFile = await ruleBroker.processRules([fileToLoad, ''], [biz.cgetCsvData]);
// loaded file data is:
await loggers.consoleLog(namespacePrefix + functionName , msg.cloadedFileDataIs + JSON.stringify(dataFile));
parsedDataFile = await processCsvData(dataFile, contextName);
// parsedDataFile is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cparsedDataFileIs + JSON.stringify(parsedDataFile));
} // End-if (fileExtension === gen.ccsv || fileExtension === gen.cCsv || fileExtension === gen.cCSV)
} // End-for (const element of filesToLoad)
// parsedDataFile is:
await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(parsedDataFile));
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
return parsedDataFile;
}
/**
* @function loadAllXmlData
* @description Loads all the context of all files and folders and sub-folders at the specified path and builds a list of files to load,
* then loads them accordingly in the D.contextName_fileName.
* @param {array<string>} filesToLoad The data structure containing all of the files to load data from.
* @param {string} contextName The context name that should be used when adding data to the D data structure.
* @return {object} A JSON object that contains all of the data that was loaded and parsed from all the input files list.
* @author Seth Hollingsead
* @date 2022/01/27
*/
async function loadAllXmlData(filesToLoad, contextName) {
let functionName = loadAllXmlData.name;
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
// filesToLoad is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cfilesToLoadIs + JSON.stringify(filesToLoad));
// contextName is:
await loggers.consoleLog(namespacePrefix + functionName, msg.ccontextNameIs + contextName);
let j = 0;
let multiMergedData = {};
let parsedDataFile = {};
let fileNameRules = [biz.cgetFileNameFromPath, biz.cremoveFileExtensionFromFileName];
for (let i = 0; i < filesToLoad.length; i++) {
// BEGIN i-th loop:
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_ithLoop + i);
let fileToLoad = filesToLoad[i];
fileToLoad = await ruleBroker.processRules([fileToLoad, ''], [biz.cswapDoubleForwardSlashToSingleForwardSlash]);
fileToLoad = path.resolve(fileToLoad);
// File to load is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cFileToLoadIs + fileToLoad);
// NOTE: We still need a filename to use as a context for the page data that we just loaded.
// A context name will be composed of the input context name with the file name we are processing
// which tells us where we will put the data in the D[contextName] sub-structure.
let fileExtension = await ruleBroker.processRules([fileToLoad, ''], [biz.cgetFileExtension, biz.cremoveDotFromFileExtension]);
// fileExtension is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cfileExtensionIs + fileExtension);
if (fileExtension === gen.cxml || fileExtension === gen.cXml || fileExtension === gen.cXML) {
// execute business rules:
await loggers.consoleLog(namespacePrefix + functionName, msg.cexecuteBusinessRulesColon + JSON.stringify(fileNameRules));
contextName = contextName + bas.cUnderscore + await ruleBroker.processRules([fileToLoad, ''], fileNameRules);
// contextName is:
await loggers.consoleLog(namespacePrefix + functionName, msg.ccontextNameIs + contextName);
let dataFile = await ruleBroker.processRules([fileToLoad, ''], [biz.cgetXmlData]);
// loaded file data is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cloadedFileDataIs + JSON.stringify(dataFile));
// BEGIN PROCESSING ADDITIONAL DATA
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_PROCESSING_ADDITIONAL_DATA);
// j-th iteration:
await loggers.consoleLog(namespacePrefix + functionName, msg.cjthIteration + j);
if (j === 0) {
j++;
multiMergedData = dataFile;
} else {
j++;
// console.log('multiMergedData is: ' + JSON.stringify(multiMergedData));
// console.log('dataFile is: ' + JSON.stringify(dataFile));
// let mergeTargetNamespace = determineMergeTarget(multiMergedData, dataFile);
// mergeTargetNamespace = mergeTargetNamespace.join(bas.cDot);
multiMergedData = await ruleBroker.processRules([multiMergedData, dataFile], [biz.cobjectDeepMerge]);
// multiMergedData = mergeData(multiMergedData, wrd.cPage, '', dataFile);
// multiMergedData = mergeData(multiMergedData, 'CommandWorkflows', '', dataFile);
// multiMergedData = mergeData(multiMergedData, '', '', dataFile);
// multiMergedData = Object.assign(multiMergedData, dataFile);
}
// DONE PROCESSING ADDITIONAL DATA
await loggers.consoleLog(namespacePrefix + functionName, msg.cDONE_PROCESSING_ADDITIONAL_DATA);
// MERGED data is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cMERGED_dataIs + JSON.stringify(multiMergedData));
dataFile = {};
} // End-if (fileExtension === gen.cxml || fileExtension === gen.cXml || fileExtension === gen.cXML)
// END i-th loop:
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_ithLoop + i);
} // End-for (let i = 0; i < filesToLoad.length; i++)
parsedDataFile = await processXmlData(multiMergedData, contextName);
// parsedDataFile contents are:
await loggers.consoleLog(namespacePrefix + functionName, msg.cparsedDataFileContentsAre + JSON.stringify(parsedDataFile));
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
return parsedDataFile;
}
/**
* @function loadAllJsonData
* @description Loads all the contents of all files and folders and sub-folders at the specified path and builds a list of files to load,
* then loads them accordingly in the D.contextName.
* @param {array<string>} filesToLoad The data structure containing all of the files to load data from.
* @param {string} contextName The context name that should be used when adding data to the D-data structure.
* @return {object} A JSON object that contains all fo the data that was loaded and parsed from all the input files list.
* @author Seth Hollingsead
* @date 2021/10/15
* @NOTE When the plugin uses haystacks to load the plugin data from the plugins configuration files,
// the haystacks instance that does this is not the same instance as the parent instance that is loading the plugin.
// So this new instance of haystacks (created by the plugin) doesn't have any of it's own configuration data loaded.
*/
async function loadAllJsonData(filesToLoad, contextName) {
let functionName = loadAllJsonData.name;
// console.log(`BEGIN ${namespacePrefix}${functionName} function`);
// console.log(`filesToLoad is: ${JSON.stringify(filesToLoad)}`);
// console.log(`contextName is: ${contextName}`);
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
// filesToLoad is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cfilesToLoadIs + JSON.stringify(filesToLoad));
// contextName is:
await loggers.consoleLog(namespacePrefix + functionName, msg.ccontextNameIs + contextName);
let foundSystemData = false;
let systemConfigFileName = sys.csystemConfigFileName; // 'framework.system.json';
let applicationConfigFileName = sys.capplicationConfigFileName; // 'application.system.json';
let pluginConfigFileName = sys.cpluginConfigFileName; // 'plugin.system.json';
let loadPluginDebugSettings = false;
let multiMergedData = {};
let parsedDataFile = {};
// Before we load all configuration data we need to FIRST load all the system configuration settings.
// There will be a system configuration setting that will tell us if we need to load the debug settings or not.
for (const element1 of filesToLoad) {
let fileToLoad = element1;
// console.log('fileToLoad is: ' + fileToLoad);
if (fileToLoad.includes(systemConfigFileName) || fileToLoad.includes(applicationConfigFileName) || fileToLoad.includes(pluginConfigFileName)) {
let dataFile = await preprocessJsonFile(fileToLoad);
// NOTE: In this case we have just loaded either the framework configuration data or the application configuration data or the plugin configuration data,
// and nothing else. So we can just assign the data to the multiMergedData.
// We will need to merge all the other files,
// but there will be a setting here we should examine to determine if the rest of the data should even be load or not.
// We will have a new setting that determines if all the extra debug settings should be loaded or not.
// This way the application performance can be seriously optimized to greater levels of lean performance.
// Adding all that extra debugging configuration settings can affect load times, and application performance to a much lesser degree.
multiMergedData[wrd.csystem] = {};
multiMergedData[wrd.csystem] = dataFile;
if (fileToLoad.includes(pluginConfigFileName)) {
// console.log('****--plugin config setting file is being processed.');
if (multiMergedData[wrd.csystem][wrd.csystem + bas.cDot + cfg.cdebugSettings] === true) {
// console.log('****--The plugin config debug settings value is set to true!');
loadPluginDebugSettings = true;
}
}
foundSystemData = true;
} // End-if (fileToLoad.includes(systemConfigFileName) || fileToLoad.includes(applicationConfigFileName))
if (foundSystemData === true) {
break;
}
} // End-for (const element of filesToLoad)
// Now we need to determine if we should load the rest of the data.
// NOTE: If the filesToLoad contained the pluginConfigFileName, then we will not be able to determine the debugSettings value from the configuration setting.
// See note above.
if (await configurator.getConfigurationSetting(wrd.csystem, cfg.cdebugSettings) === true || loadPluginDebugSettings === true) {
for (const element2 of filesToLoad) {
let fileToLoad = element2;
if (!fileToLoad.includes(systemConfigFileName) && !fileToLoad.includes(applicationConfigFileName) && !fileToLoad.includes(pluginConfigFileName)
&& fileToLoad.toUpperCase().includes(gen.cDotJSON) && !fileToLoad.toLowerCase().includes(wrd.cmetadata + gen.cDotjson)) {
// Get the filename without the file extension, use that as a key for the data.
let filename = await ruleBroker.processRules([fileToLoad, ''], [biz.cgetFileNameFromPath, biz.cremoveFileExtensionFromFileName]);
// console.log('filename is: ' + filename);
let dataFile = await preprocessJsonFile(fileToLoad);
// console.log('dataFile to merge is: ' + JSON.stringify(dataFile));
await loggers.consoleLog(namespacePrefix + functionName, msg.cdataFileToMergeIs + JSON.stringify(dataFile));
if (!multiMergedData[cfg.cdebugSettings]) {
multiMergedData[cfg.cdebugSettings] = {};
if (contextName.includes(wrd.cclient + wrd.cData)) {
// console.log('clientData first time merge!');
multiMergedData[cfg.cdebugSettings] = {[filename]: dataFile};
} else {
// console.log('regular old traditional first time data merge!');
multiMergedData[cfg.cdebugSettings] = dataFile;
}
} else {
if (contextName.includes(wrd.cclient + wrd.cData)) {
// console.log('clientData n-th data merge');
Object.assign(multiMergedData[cfg.cdebugSettings], {[filename]: dataFile});
} else {
// console.log('regular old n-th data merge');
Object.assign(multiMergedData[cfg.cdebugSettings], dataFile);
}
}
}
} // End-for (const element2 of filesToLoad)
} // End-if (configurator.getConfigurationSetting(wrd.csystem, cfg.cdebugSettings) === true)
parsedDataFile = multiMergedData;
// console.log(`parsedDataFile is: ${JSON.stringify(parsedDataFile)}`);
// console.log(`END ${namespacePrefix}${functionName} function`);
await loggers.consoleLog(namespacePrefix + functionName, msg.cparsedDataFileIs + JSON.stringify(parsedDataFile));
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
return parsedDataFile;
}
/**
* @function loadAllJsonDataBruteForce
* @description Loads all the contents of all files and folders and sub-folders at the specified path and stores them in D.contextName.
* @param {array<string>} filesToLoad The array of path and file names to load data from.
* @param {string} contextName The context name that should be used when adding data to the D-data structure.
* @return {object} A JSON object that contains all of the data that was loaded and parsed from all the input files list.
* @author Seth Hollingsead
* @date 2024/10/23
* @NOTE This function is for loading mission critical files that MUST be loaded no matter what for the framework to function correctly.
*/
async function loadAllJsonDataBruteForce(filesToLoad, contextName) {
let functionName = loadAllJsonDataBruteForce.name;
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
// filesToLoad is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cfilesToLoadIs + JSON.stringify(filesToLoad));
// contextName is:
await loggers.consoleLog(namespacePrefix + functionName, msg.ccontextNameIs + contextName);
let returnData = {};
let i = 0;
for (const element3 of filesToLoad) {
let fileToLoad = element3;
if (fileToLoad.toUpperCase().includes(gen.cDotJSON)) {
// Get the filename without the file extension, use that as a key for the data.
let filename = await ruleBroker.processRules([fileToLoad, ''], [biz.cgetFileNameFromPath, biz.cremoveFileExtensionFromFileName]);
// console.log('filename is: ' + filename);
let dataFile = await preprocessJsonFile(fileToLoad);
// console.log('dataFile to merge is: ' + JSON.stringify(dataFile));
await loggers.consoleLog(namespacePrefix + functionName, msg.cdataFileToMergeIs + JSON.stringify(dataFile));
if (i === 0) {
returnData[wrd.cschemas] = {};
}
returnData[wrd.cschemas][filename] = dataFile;
}
await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
i = i + 1;
}
await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
return returnData;
}
/**
* @function processCsvData
* @description Processes all of the CSV data into a usable format and executes any additional processing rules.
* @param {object} data A JSON object that contains all of the data loaded from a CSV file.
* @param {string} contextName The name that should be used when adding the objects to the D data structure for data-sharing.
* @return {object} A parsed and cleaned up JSON object where all of the CSV data is collated and organized and cleaned up ready for use.
* @author Seth Hollingsead
* @date 2022/01/27
*/
async function processCsvData(data, contextName) {
let functionName = processCsvData.name;
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
// input data is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + JSON.stringify(data));
// contextName is:
await loggers.consoleLog(namespacePrefix + functionName, msg.ccontextNameIs + contextName);
let parsedData = await extractDataFromPapaParseObject(data, contextName);
let dataCategory = await getDataCategoryFromContextName(contextName);
// dataCategory is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cdataCategoryIs + dataCategory);
if (contextName.toLowerCase() === wrd.cworkflow) {
// Processing a workflow
Object.assign(D[wrd.cWorkflow], parsedData[contextName]);
} else if (contextName.includes(wrd.ccolors)) {
D[wrd.ccolors] = {};
Object.assign(D[wrd.ccolors], parsedData);
} else {
// Processing all other kinds of files.
if (typeof D[dataCategory] !== 'undefined' && D[dataCategory]) {
Object.assign(D[dataCategory], parsedData);
await mergeData(D, dataCategory, '', parsedData);
} else {
D[dataCategory] = {};
Object.assign(D[dataCategory], parsedData);
await mergeData(D, dataCategory, '', parsedData);
}
}
// fully parsed data is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cfullyParsedDataIs + JSON.stringify(parsedData));
// D final merge is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cD_finalMergeIs + JSON.stringify(D));
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
return parsedData;
}
/**
* @function processXmlData
* @description Does some final processing on JSON data loaded from an XML file,
* converting the data into a usable format and executes any additional data processing rules.
* @param {object} data A JSON object that contains all of the data loaded from a XML file.
* @param {string} contextName The name that should be used when adding the objects to the D data structure for data-sharing.
* @return {object} A parsed and cleaned up JSON object where all of the XML data is collated and organized and cleaned up ready for use.
* @author Seth Hollingsead
* @date 2022/02/22
*/
async function processXmlData(inputData, contextName) {
let functionName = processXmlData.name;
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
// inputData is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + JSON.stringify(inputData));
// contextName is:
await loggers.consoleLog(namespacePrefix + functionName, msg.ccontextNameIs + contextName);
let dataCategory = await getDataCategoryFromContextName(contextName);
// dataCategory is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cdataCategoryIs + dataCategory);
let parsedDataFile = {};
if (dataCategory === sys.cCommandsAliases) {
parsedDataFile[sys.cCommandsAliases] = {};
// eslint-disable-next-line no-unused-vars
for (const _element1 of Object.keys(inputData[sys.cCommandsAliases])) {
inputData[sys.cCommandsAliases] = await processXmlLeafNode(inputData[sys.cCommandsAliases], wrd.cCommand);
} // End-for (const _element1 of Object.keys(inputData[sys.cCommandsAliases]))
parsedDataFile = inputData[sys.cCommandsAliases];
// parsedDataFile[sys.cCommandsAliases][wrd.cCommands] = {};
// for (let i = 0; i < inputData[sys.cCommandsAliases][wrd.cCommand].length; i++) {
// let command = inputData[sys.cCommandsAliases][wrd.cCommand][i][bas.cDollar];
// parsedDataFile[sys.cCommandsAliases][wrd.cCommands][command.Name] = command;
// } // End-for (let i = 0; i < inputData[sys.cCommandAliases][wrd.cCommand].length; i++)
} else if (dataCategory === sys.cCommandWorkflows) { // End-if (dataCategory === sys.cCommandsAliases)
parsedDataFile[sys.cCommandWorkflows] = {};
// eslint-disable-next-line no-unused-vars
for (const _element2 of Object.keys(inputData[sys.cCommandWorkflows])) {
inputData[sys.cCommandWorkflows] = await processXmlLeafNode(inputData[sys.cCommandWorkflows], wrd.cWorkflows);
} // End-for (const _element2 of Object.keys(inputData[sys.cCommandWorkflows]))
parsedDataFile = inputData[sys.cCommandWorkflows];
} else if (dataCategory === sys.cPluginWorkflows) {
parsedDataFile[sys.cPluginWorkflows] = {};
// eslint-disable-next-line no-unused-vars
for (const _element3 of Object.keys(inputData[sys.cPluginWorkflows])) {
inputData[sys.cPluginWorkflows] = await processXmlLeafNode(inputData[sys.cPluginWorkflows], wrd.cWorkflows);
} // End-for (const _element3 of Object.keys(inputData[sys.cPluginWorkflows]))
parsedDataFile = inputData[sys.cPluginWorkflows];
} // End-else-if (dataCategory === sys.cPluginWorkflows)
// parsedDataFile is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cparsedDataFileIs + JSON.stringify(parsedDataFile));
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
return parsedDataFile;
}
/**
* @function processXmlLeafNode
* @description Recursively looks for the leaf node and restructures it from an array with a
* "$" child object to a single object entity with the name of the entity.
* @param {object} inputData The data that should be recursively mutated to the correct data structure and returned.
* @param {string} leafNodeName The leaf node name that we are looking for.
* @return {object} The mutated object with the correct data structure.
* @author Seth Hollingsead
* @date 2022/05/24
* @NOTE: The solution to this at the leaf-node level is:
* let workflow = data[sys.cCommandWorkflows][wrd.cWorkflow][j][bas.cDollar];
* parsedDataFile[sys.cCommandWorkflows][wrd.cWorkflows][workflow.Name] = workflow;
*/
async function processXmlLeafNode(inputData, leafNodeName) {
let functionName = processXmlLeafNode.name;
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
// input data is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + JSON.stringify(inputData));
// leafNodeName is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cleafNodeNameIs + leafNodeName);
let returnData = {};
if (typeof inputData !== wrd.cobject) {
// inputData ain't an objet.
returnData = inputData;
} else {
for (let property in inputData) {
if (!Object.prototype.hasOwnProperty.call(inputData, property)) {
continue; // Take into consideration only object's own properties.
}
// property is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cpropertyIs + JSON.stringify(property));
// inputData[property] is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataPropertyIs + JSON.stringify(inputData[property]));
if (property === wrd.cWorkflow || property === wrd.cCommand) {
let workflowParent = inputData[property];
// workflowParent is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cworkflowParentIs + JSON.stringify(workflowParent));
for (let i = 0; i < workflowParent.length; i++) {
// BEGIN i-th loop:
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_ithLoop + i);
let workflowEntity = workflowParent[i][bas.cDollar];
// workflowEntity is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cworkflowEntityIs + JSON.stringify(workflowEntity));
if (property === wrd.cWorkflow) {
// workflowEntity[Value] is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cworkflowEntityValueIs + JSON.stringify(workflowEntity.Value));
returnData[workflowEntity.Name] = workflowEntity.Value;
} else if (property === wrd.cCommand) {
returnData[workflowEntity.Name] = {};
returnData[workflowEntity.Name][wrd.cName] = workflowEntity.Name;
returnData[workflowEntity.Name][wrd.cAliases] = workflowEntity.Aliases;
returnData[workflowEntity.Name][wrd.cDescription] = workflowEntity.Description;
}
// END i-th Loop:
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_ithLoop + i);
} // End-for (let i = 0; i < workflowParent.length; i++)
// Done with the for-loop, returnData is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cDoneWithForLoopReturnDataIs + JSON.stringify(returnData));
} else {
// property is not a Workflow or a Command,
// so call processXmlLeafNode() recursively!
await loggers.consoleLog(namespacePrefix + functionName, msg.cprocessXmlLeafNodeMessage01 + msg.cprocessXmlLeafNodeMessage02);
if (property === num.c0) {
returnData = [await processXmlLeafNode(inputData[property], leafNodeName)];
} else {
returnData[property] = await processXmlLeafNode(inputData[property], leafNodeName);
}
// AFTER recursive call returnData[property] is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cAfterRecursiveCallReturnDataPropertyIs + JSON.stringify(returnData[property]));
}
} // End-for (let property in inputData)
}
await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
return returnData;
}
/**
* @function preprocessJsonFile
* @description Load all of the data from a single JSON data file.
* @param {string} fileToLoad The fully qualified path to the file that should be loaded.
* @return {object} The JSON data object that was loaded from the specified JSON data file.
* @author Seth Hollingsead
* @date 2021/10/15
*/
async function preprocessJsonFile(fileToLoad) {
let functionName = preprocessJsonFile.name;
// console.log(`BEGIN ${namespacePrefix}${functionName} function`);
// console.log(`fileToLoad is: ${JSON.stringify(fileToLoad)}`);
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
await loggers.consoleLog(namespacePrefix + functionName, msg.cfileToLoadIs + JSON.stringify(fileToLoad));
let filePathRules = [biz.cswapDoubleForwardSlashToSingleForwardSlash, biz.cgetJsonData];
// console.log(`execute business rules: ${JSON.stringify(filePathRules)}`);
await loggers.consoleLog(namespacePrefix + functionName, msg.cexecuteBusinessRules + JSON.stringify(filePathRules));
let dataFile = await ruleBroker.processRules([fileToLoad, ''], filePathRules);
await loggers.consoleLog(namespacePrefix + functionName, msg.cdataFileIs + JSON.stringify(dataFile));
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
// console.log(`dataFile is: ${JSON.stringify(dataFile)}`);
// console.log(`END ${namespacePrefix}${functionName} function`);
return dataFile;
}
/**
* @function writeJsonDataToFile
* @description This is a wrapper function for businessRules.rules.fileOperations.writeJsonData.
* @param {string} fileToSaveTo The full path to the file that should have the data written to it.
* @param {object} dataToWriteOut The JSON data that should be written out to the specified JSON file.
* @return {boolean} True or False to indicate if the file was saved successfully or not.
* @author Seth Hollingsead
* @date 2022/03/11
*/
async function writeJsonDataToFile(fileToSaveTo, dataToWriteOut) {
let functionName = writeJsonDataToFile.name;
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
await loggers.consoleLog(namespacePrefix + functionName, msg.cfileToSaveToIs + fileToSaveTo);
await loggers.consoleLog(namespacePrefix + functionName, msg.cdataToWriteOutIs + JSON.stringify(dataToWriteOut));
let fileWriteRules = [biz.cwriteJsonData];
let returnData = await ruleBroker.processRules([path.resolve(fileToSaveTo), dataToWriteOut], fileWriteRules);
await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData);
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
return returnData;
}
/**
* @function setupDataStorage
* @description Does the initial setup of data storage on the D data structure.
* @return {void} Nothing to return.
* @author Seth Hollingsead
* @date 2022/01/20
*/
async function setupDataStorage() {
let functionName = storeData.name;
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
D[sys.cDataStorage] = {};
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
}
/**
* @function storeData
* @description Stores some data in a data storage hive on the D data structure, under a caller specified sub-data storage hie name.
* @param {string} dataStorageContextName The sub-data storage hive under-which the data should be stored.
* @param {string|boolean|integer|float|array|object} dataToStore The data that should be stored, in any format.
* @return {boolean} A True or False to indicate if the data storage was successful or not.
* @author Seth Hollingsead
* @date 2022/01/20
*/
async function storeData(dataStorageContextName, dataToStore) {
let functionName = storeData.name;
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
// dataStorageContextName is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cdataStorageContextNameIs + dataStorageContextName);
// data To Store is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cdataToStoreIs + JSON.stringify(dataToStore));
let returnData = false;
D[sys.cDataStorage][dataStorageContextName] = {};
D[sys.cDataStorage][dataStorageContextName] = dataToStore;
returnData = true;
await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData);
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
return returnData;
}
/**
* @function initSchemaStorage
* @description Initializes the schema data storage on the D-data structure.
* @return {boolean} A True or FAlse to indicate if the data schema storage was successfully initialized or not.
* @author Seth Hollingsead
* @date 2024/10/23
*/
async function initSchemaStorage() {
let functionName = initSchemaStorage.name;
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
let returnData = true;
D[wrd.cSchemas] = {};
await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData);
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
return returnData;
}
/**
* @function storeSchemaData
* @description Stores a schema data object on the D-data structure.
* @param {object} schemaDataObject A JSON data object that contains a behavior schema.
* @return {boolean} A True or False to indicate if the data storage was successful or not.
* @author Seth Hollingsead
* @date 2024/10/23
*/
async function storeSchemaData(schemaDataObject) {
let functionName = storeSchemaData.name;
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
// schemaDataObject is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cschemaDataObjectIs + JSON.stringify(schemaDataObject));
let returnData = false;
if (schemaDataObject && schemaDataObject[wrd.cschemas]) {
for (const schemaNamespace in schemaDataObject[wrd.cschemas]) {
if (Object.hasOwn(schemaDataObject[wrd.cschemas], schemaNamespace)) {
// Store the schema in the D-data structure under the proper schema namespace.
D[wrd.cSchemas][schemaNamespace] = schemaDataObject[wrd.cschemas][schemaNamespace];
// Stored schema under namespace:
await loggers.consoleLog(namespacePrefix + functionName, msg.cStoredSchemaUnderNamespace + schemaNamespace);
}
}
returnData = true; // Success storing schemas
} else {
// ERROR: Invalid schemaDataObject: Missing schemas key
console.log(msg.cErrorInvalidSchemaDataObjectMissingSchemasKey);
await loggers.consoleLog(namespacePrefix + functionName, msg.cErrorInvalidSchemaDataObjectMissingSchemasKey);
}
await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData);
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
return returnData;
}
/**
* @function getSchema
* @description Gets a specific named schema data object, that should be stored in the system, or all schema data if no name is specified.
* @param {string} schemaName The name of the schema object that should exist in the list of currently loaded schemas.
* @return {object} A JSON object that contains the schema content for the named schema, if it exists, or all schema data if no name is specified.
* @author Seth Hollingsead
* @date 2024/11/22
* @NOTE Cannot use the loggers here, because of a circular dependency.
*/
async function getSchema(schemaName) {
// let functionName = getSchema.name;
// await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
// await loggers.consoleLog(namespacePrefix + functionName, msg.cschemaNameIs + schemaName);
let returnData = false;
let schemasLoaded = await configurator.getConfigurationSetting(wrd.csystem, cfg.cschemasLoaded);
if (schemasLoaded) {
if (schemaName) {
try {
returnData = D[wrd.cSchemas][schemaName];
} catch (err) {
// ERROR: Invalid schema name. Schema does not exist:
console.log(msg.cErrorGetSchemaMessage01 + schemaName);
// await loggers.consoleLog(namespacePrefix + functionName, msg.cErrorGetSchemaMessage01 + schemaName);
returnData = false;
}
} else {
// return all schemas.
// await loggers.consoleLog(namespacePrefix + functionName, msg.creturnAllSchemas);
returnData = D[wrd.cSchemas];
}
} else {
// Fail silently, as this is going to get hit during the loading process.
// The loggers is calling this to get the loggers schema, but since it hasn't been loaded,
// it's not going to work, just fail silently.
returnData = false;
}
// await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
// await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
return returnData;
}
/**
* @function getData
* @description Gets some data from a caller specified sub-data storage hive name.
* @param {string} dataStorageContextName The sub-data storage hive which should be retrieved.
* @return {object} the data that is found, if any at the specified location on the data storage hive.
* @author Seth Hollingsead
* @date 2022/01/20
*/
async function getData(dataStorageContextName) {
let functionName = getData.name;
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
// dataStorageContextName is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cdataStorageContextNameIs + dataStorageContextName);
let returnData = false;
if (D[sys.cDataStorage][dataStorageContextName] !== null && !!D[sys.cDataStorage][dataStorageContextName]) {
returnData = D[sys.cDataStorage][dataStorageContextName];
}
await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData);
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
return returnData;
}
/**
* @function clearData
* @description Clears out all of the data stored in the DataStorage data hive of the D data structure,
* or a particular stored data element if a contextName is provided.
* @param {string} dataStorageContextName (OPTIONAL) The sub-data storage hive which should be cleared.
* @return {void}
* @author Seth Hollingsead
* @date 2022/01/20
*/
async function clearData(dataStorageContextName) {
let functionName = clearData.name;
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
// dataStorageContextName is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cdataStorageContextNameIs + dataStorageContextName);
if (D[sys.cDataStorage][dataStorageContextName] !== null && !!D[sys.cDataStorage][dataStorageContextName] && dataStorageContextName !== '') {
D[sys.cDataStorage][dataStorageContextName] = {};
} else {
D[sys.cDataStorage] = {};
}
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
}
/**
* @function getDataCategoryFromContextName
* @description Gets the data category, given the context name.
* @param {string} contextName The context name which will be something like Application_xxxx or Script_nnnn or Command_yyyy
* @return {string} The string before the first cUnderscore (Application, Script, Command, etc).
* @author Seth Hollingsead
* @date 2022/01/27
*/
async function getDataCategoryFromContextName(contextName) {
let functionName = getDataCategoryFromContextName.name;
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
// contextName is:
await loggers.consoleLog(namespacePrefix + functionName, msg.ccontextNameIs + contextName);
let dataCategory = '';
dataCategory = await ruleBroker.processRules([contextName, ''], [biz.cgetDataCategoryFromDataContextName]);
// dataCategory is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cdataCategoryIs + dataCategory);
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
return dataCategory;
}
/**
* @function getDataCategoryDetailNameFromContextName
* @description Gets the data category detail name, given the context name.
* @param {string} contextName The name which will be something like Command_ApiPost or Script_ApiMacroX.
* @return {string} The string after the first cUnderscore (ApiPost or ApiMacroX).
* @author Seth Hollingsead
* @date 2022/01/27
*/
// eslint-disable-next-line no-unused-vars
async function getDataCategoryDetailNameFromContextName(contextName) {
let functionName = getDataCategoryDetailNameFromContextName.name;
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
// contextName is:
await loggers.consoleLog(namespacePrefix + functionName, msg.ccontextNameIs + contextName);
let dataCategoryDetailName = '';
dataCategoryDetailName = await ruleBroker.processRules([contextName, ''], [biz.cgetDataCategoryDetailNameFromDataContextName]);
// dataCategoryDetailsName is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cdataCategoryDetailsNameIs + dataCategoryDetailName);
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
return dataCategoryDetailName;
}
/**
* @function extractDataFromPapaParseObject
* @description Extracts the parsed data from the papa parse data object.
* @param {object} data Contains the full contents of the papa parse data object, for which we need to get data out of.
* @param {string} contextName The name of the context either Command, Macro from which data should be retrieved.
* @return {object} The fully parsed data that we intend to use for the application.
* @author Seth Hollingsead
* @date 2022/01/27
*/
async function extractDataFromPapaParseObject(data, contextName) {
let functionName = extractDataFromPapaParseObject.name;
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
// input data is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + JSON.stringify(data));
// contextName is:
await loggers.consoleLog(namespacePrefix + functionName, msg.ccontextNameIs + contextName);
let cleanKeysRules = [biz.ccleanCarriageReturnFromString];
let tempData = {};
let validDataAdded = false;
if (contextName === wrd.ccolors) {
contextName = sys.cColorData;
}
// contextName is:
await loggers.consoleLog(namespacePrefix + functionName, msg.ccontextNameIs + contextName);
tempData[contextName] = {};
let highLevelDataCount = Object.keys(data[wrd.cdata]).length;
for (let i = 0; i <= highLevelDataCount; i++) {
validDataAdded = false;
let lowLevelTempData = {};
if (contextName === sys.cColorData) {
let colorName = '';
for (let key1 in data[wrd.cdata][i]) {
validDataAdded = true;
let newKey = await ruleBroker.processRules(