@haystacks/async
Version:
A framework to build any number or any kind of native application or automation solution.
583 lines (563 loc) • 31.4 kB
JavaScript
/**
* @file dataArrayParsing.js
* @module dataArrayParsing
* @description Contains all system defined business rules for parsing arrays specific to data.
* @requires module:dataBroker
* @requires module:ruleParsing
* @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 2022/04/26
* @copyright Copyright © 2022-… by Seth Hollingsead. All rights reserved
*/
// Internal imports
import dataBroker from '../../../brokers/dataBroker.js';
import ruleParsing from '../ruleParsing.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, msg, sys, wrd} = hayConst;
const baseFileName = path.basename(import.meta.url, path.extname(import.meta.url));
// framework.businessRules.rules.arrayParsing.dataArrayParsing.
const namespacePrefix = wrd.cframework + bas.cDot + sys.cbusinessRules + bas.cDot + wrd.crules + bas.cDot + wrd.carray + wrd.cParsing + bas.cDot + baseFileName + bas.cDot;
/**
* @function arraysAreEqual
* @description Determines if a set of arrays are equal or not.
* @param {array<string|integer|boolean|float|object>} inputData The first array that should be checked for equality.
* @param {array<string|integer|boolean|float|object>} inputMetaData The second array that should be checked for equality.
* @return {boolean} True or False to indicate if the arrays are equal or not equal.
* @author Seth Hollingsead
* @date 2022/01/20
* @NOTE: https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
*/
async function arraysAreEqual(inputData, inputMetaData) {
let functionName = arraysAreEqual.name;
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + JSON.stringify(inputData));
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + JSON.stringify(inputMetaData));
let returnData = false;
if (inputData && inputMetaData) {
if (inputData === inputMetaData) { returnData = true; }
if (inputData === null || inputMetaData === null) { returnData = false; }
if (inputData.length != inputMetaData.length) { returnData = false; }
} // End-if (inputData && inputMetaData)
await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData);
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
return returnData;
}
/**
* @function storeData
* @description Stores some data using the DataStorage data hie on the D data store.
* @param {string} inputData The context name that the data should be stored with.
* @param {string|integer|boolean|object|array} inputMetaData The data that should be stored.
* @return {void}
* @author Seth Hollingsead
* @date 2022/01/20
*/
async function storeData(inputData, inputMetaData) {
let functionName = arraysAreEqual.name;
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + JSON.stringify(inputData));
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + JSON.stringify(inputMetaData));
let returnData = false;
if (inputData && inputMetaData) {
await dataBroker.storeData(inputData, inputMetaData);
returnData = true;
} // End-if (inputData && inputMetaData)
await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData);
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
return returnData;
}
/**
* @function getStoredData
* @description Gets the named data stored in the D data structure in the DataStorage data hive.
* @param {string} inputData The name of the sub-data hive that should contain the stored data we are looking for.
* @param {string} inputMetaData Not used for this business rule.
* @return {object} The data that was stored in the sub-data hive under the DataStorage data hive of the D data structure.
* @author Seth Hollingsead
* @date 2022/01/20
*/
async function getStoredData(inputData, inputMetaData) {
let functionName = getStoredData.name;
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + JSON.stringify(inputData));
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + JSON.stringify(inputMetaData));
let returnData = false;
if (inputData) {
returnData = await dataBroker.getData(inputData);
}
await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData);
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
return returnData;
}
/**
* @function isObjectEmpty
* @description Determines if a JSON object is empty or not.
* @param {object} inputData The object that should be checked for emptiness.
* @param {string} inputMetaData Not used for this business rule.
* @return {boolean} True or False to indicate if the object is empty or not empty.
* @author Seth Hollingsead
* @date 2022/01/21
*/
async function isObjectEmpty(inputData, inputMetaData) {
let functionName = isObjectEmpty.name;
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + JSON.stringify(inputData));
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + inputMetaData);
let returnData = true;
if (inputData) {
for (let key in inputData) {
if (Object.prototype.hasOwnProperty.call(inputData, key)) {
returnData = false;
// It may have a value, but is that value === null, if it is, reset back to true.
if (inputData[key] === null) {
returnData = true;
}
} // End-if (inputData.hasOwnProperty(key))
} // End-for (let key in inputData)
} // End-if (inputData)
await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData);
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
return returnData;
}
/**
* @function isArrayEmpty
* @description Determines if a JSON array is empty or not.
* @param {array<string|integer|boolean|float|object>} inputData The array that should be checked for emptiness.
* @param {string} inputMetaData Not used for this business rule.
* @return {boolean} True or False to indicate if the array is empty or not empty.
* @author Seth Hollingsead
* @date 2022/01/21
*/
async function isArrayEmpty(inputData, inputMetaData) {
let functionName = isArrayEmpty.name;
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + JSON.stringify(inputData));
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + JSON.stringify(inputMetaData));
let returnData = true;
if (inputData) {
returnData = await !Object.keys(inputData).length;
}
await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData);
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
return returnData;
}
/**
* @function isObject
* @description Determines if an object is a JSON object or not.
* @param {object|array<string|integer|boolean|float|object>} inputData The object that should be tested to see if it is a JSON object or not.
* @param {string} inputMetaData Not used for this business rule.
* @return {boolean} True or False to indicate if the input object is an array or not.
* @author Seth Hollingsead
* @date 2022/01/21
*/
async function isObject(inputData, inputMetaData) {
let functionName = isObject.name;
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + JSON.stringify(inputData));
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + inputMetaData);
let returnData = false;
if (inputData) {
if (typeof inputData === wrd.cobject) {
returnData = true;
}
} // End-if (inputData)
await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData);
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
return returnData;
}
/**
* @function isArray
* @description Determines if an object is an array or not.
* @param {object|array<string|integer|boolean|float|object>} inputData The object that
* should be tested to see if it is an array or not.
* @param {string} inputMetaData Not used for this business rule.
* @return {boolean} True or False to indicate if the input object is an array or not.
* @author Seth Hollingsead
* @date 2022/01/21
*/
async function isArray(inputData, inputMetaData) {
let functionName = isArray.name;
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + JSON.stringify(inputData));
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + inputMetaData);
let returnData = false;
if (inputData) {
returnData = await Array.isArray(inputData);
}
await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData);
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
return returnData;
}
/**
* @function isArrayOrObject
* @description Determines if an input object is either an array or a JSON object.
* @param {object|array<string|integer|boolean|float|object>} inputData The object that
* should be tested to see if it is either an array or a JSON object or not.
* @param {string} inputMetaData Not used for this business rule.
* @return {boolean} True or False to indicate if the input object is either an array or a JSON object.
* @author Seth Hollingsead
* @date 2022/01/21
*/
async function isArrayOrObject(inputData, inputMetaData) {
let functionName = isArrayOrObject.name;
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + JSON.stringify(inputData));
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + inputMetaData);
let returnData = false;
if (inputData) {
if (await isObject(inputData, '') === true || await isArray(inputData, '') === true) {
returnData = true;
}
} // End-if (inputData)
await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData);
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
return returnData;
}
/**
* @function isNonZeroLengthArray
* @description Determines if an object is an array of length greater than or equal to one or not.
* @param {object|array<string|integer|boolean|float|object>} inputData The object/array that
* should be tested to see if it is an array of length greater than or equal to 1 or not.
* @param {string} inputMetaData Not used for this business rule.
* @return {boolean} True or False to indicate if the input object is an array of length greater than equal to zero or not.
* @author Seth Hollingsead
* @date 2022/01/21
*/
async function isNonZeroLengthArray(inputData, inputMetaData) {
let functionName = isNonZeroLengthArray.name;
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + JSON.stringify(inputData));
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + inputMetaData);
let returnData = false;
if (inputData) {
if (await isArray(inputData, '') === true && inputData.length >= 1) {
returnData = true;
}
} // End-if (inputData)
await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData);
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
return returnData;
}
/**
* @function arrayDeepClone
* @description Clones an array by using JSON.stringify & JSON.parse.
* Almost all other methods of cloning an array will actually clone by reference which essentially just clones the pointer to the array.
* @NOTE: https://www.freecodecamp.org/news/how-to-clone-an-array-in-javascript-1d3183468f6a/
* @param {array<string|integer|boolean|float|object>} inputData The array that should be deeply cloned.
* @param {string} inputMetaData Not used for this business rule.
* @return {array<string|integer|boolean|float|object>} The new array object after being cloned deeply.
* @author Seth Hollingsead
* @date 2022/01/21
*/
async function arrayDeepClone(inputData, inputMetaData) {
let functionName = arrayDeepClone.name;
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + JSON.stringify(inputData));
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + JSON.stringify(inputMetaData));
let returnData = false;
if (inputData && await isArray(inputData, '') === true && await isArrayEmpty(inputData, '') === false) {
returnData = await JSON.parse(await JSON.stringify(inputData));
}
await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
return returnData;
}
/**
* @function objectDeepClone
* @description Recursively walks through all levels of a JSON object and deeply clones all of its contents including function objects.
* @param {object} inputData The JSON object that should be deeply cloned.
* @param {string} inputMetaData Not used for this business rule.
* @return {object} A clone of the original input JSON object.
* @author Seth Hollingsead
* @date 2023/02/15
* @NOTE This function was generated with the help of ChatGPT.
*/
async function objectDeepClone(inputData, inputMetaData) {
let functionName = objectDeepClone.name;
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + JSON.stringify(inputData));
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + JSON.stringify(inputMetaData));
let returnData;
let fastExit = false;
if (typeof inputData !== wrd.cobject || inputData === null) {
if (typeof inputData === wrd.cstring || typeof inputData === wrd.cnumber || typeof inputData === wrd.cboolean) {
returnData = inputData;
fastExit = true;
// fast exit enabled
await loggers.consoleLog(namespacePrefix + functionName, msg.cfastExitEnabled);
} else {
// Invalid input object. Expected a valid JSON object. Object type is:
throw new Error(msg.cErrorObjectDeepCloneMessage01 + typeof inputData);
}
}
if (fastExit === false) {
if (Array.isArray(inputData)) {
if (typeof inputData[1] === wrd.cobject) {
// array cloning object
await loggers.consoleLog(namespacePrefix + functionName, msg.carrayCloningObject);
let cloneArray = [];
for (let item in inputData) {
cloneArray[item] = await objectDeepClone(inputData[item], '');
}
returnData = cloneArray;
} else {
// array deep clone string
await loggers.consoleLog(namespacePrefix + functionName, msg.carrayDeepCloneString);
returnData = await arrayDeepClone(inputData, '');
}
} else {
// object deep cloning
await loggers.consoleLog(namespacePrefix + functionName, msg.cobjectDeepCloning);
returnData = {};
for (let key in inputData) {
if (Object.prototype.hasOwnProperty.call(inputData, key)) {
if (typeof inputData[key] === wrd.cobject && inputData[key] !== null) {
returnData[key] = await objectDeepClone(inputData[key], '');
} else if (typeof inputData[key] === wrd.cfunction) {
// Solution from: https://stackoverflow.com/questions/1833588/javascript-clone-a-function
// const newFunction = inputData.bind(this);
// Shallow copy any function properties if any.
// returnData[key] = Object.assign(newFunction, inputData);
returnData[key] = inputData[key];
} else {
returnData[key] = inputData[key];
}
} // End-if (inputData.hasOwnProperty(key))
} // End-for (let key in inpuData)
}
}
await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
return returnData;
}
/**
* @function objectDeepMerge
* @description Recursively deeply merges two objects that may or may not contains nested arrays.
* @param {object} inputData The target data to be merged with.
* @param {object} inputMetaData The data that should be merged.
* @return {object} The merged data after the merge is complete.
* @author Pery Mimon
* @date 2020/04/23
* @reference: https://stackoverflow.com/questions/27936772/how-to-deep-merge-instead-of-shallow-merge
*/
async function objectDeepMerge(inputData, inputMetaData) {
let functionName = objectDeepMerge.name;
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + JSON.stringify(inputData));
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + JSON.stringify(inputMetaData));
let returnData = false;
if (typeof inputData !== wrd.cobject || typeof inputMetaData !== wrd.cobject) {
// inputData or inputMetaData or both ain't objets, merging doesn't make sense.
returnData = false;
} else {
for (let property in inputMetaData) {
if (!Object.prototype.hasOwnProperty.call(inputMetaData, property)) {
continue; // Take into consideration only object's own properties.
}
// property is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cpropertyIs + JSON.stringify(property));
if (property in inputData) {
// property is in inputData
await loggers.consoleLog(namespacePrefix + functionName, msg.cobjectDeepMergeMessage01);
// inputData[property] is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataPropertyIs + JSON.stringify(inputData[property]));
// Handling merging of two properties with equal names.
if (typeof inputData[property] !== wrd.cobject) {
// inputData[property] is not an object!
// Assign it directly.
await loggers.consoleLog(namespacePrefix + functionName, msg.cobjectDeepMergeMessage02 + bas.cSpace + msg.cobjectDeepMergeMessage03);
inputData[property] = inputMetaData[property];
} else {
// inputData[property] is an object!
await loggers.consoleLog(namespacePrefix + functionName, msg.cobjectDeepMergeMessage04);
// inputMetaData[property] is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataPropertyIs + JSON.stringify(inputMetaData[property]));
if (typeof inputMetaData[property] !== wrd.cobject) {
// inputMetaData[property] is not an object,
// Assign it directly.
await loggers.consoleLog(namespacePrefix + functionName, msg.cobjectDeepMergeMessage05 + msg.cobjectDeepMergeMessage03);
inputData[property] = inputMetaData[property];
} else {
// inputMetaData[property] is an object.
await loggers.consoleLog(namespacePrefix + functionName, msg.cobjectDeepMergeMessage06);
if (inputData[property].concat && inputMetaData[property].concat) {
// Are the arrays length 1 or greater?
if (inputData[property].length === 1 && inputMetaData[property].length === 1) {
// Array lengths are the same at this level.
await loggers.consoleLog(namespacePrefix + functionName, msg.cobjectDeepMergeMessage07);
// We should deeply merge the contents of the arrays.
inputData[property] = await objectDeepMerge(inputData[property], inputMetaData[property]);
} else {
// Two arrays get concatenated.
await loggers.consoleLog(namespacePrefix + functionName, msg.cobjectDeepMergeMessage08);
inputData[property] = inputData[property].concat(inputMetaData[property]);
// AFTER concatenating two arrays: inputData[property] is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cAfterConcatenating2ArraysInputDataPropertyIs + JSON.stringify(inputData[property]));
}
} else {
// Two objects get merged recursively.
await loggers.consoleLog(namespacePrefix + functionName, msg.cobjectDeepMergeMessage09);
inputData[property] = await objectDeepMerge(inputData[property], inputMetaData[property]);
// AFTER recursive merge: inputData[property] is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cAfterRecursiveMergeInputDataPropertyIs + JSON.stringify(inputData[property]));
}
}
}
} else {
// property is not in inputData,
// so add it directly.
await loggers.consoleLog(namespacePrefix + functionName, msg.cobjectDeepMergeMessage10 + msg.cobjectDeepMergeMessage11);
inputData[property] = inputMetaData[property];
}
} // End-for (let property in inputMetaData)
returnData = inputData;
}
// NOTE: This implementation from Vincent on the reference: Dated: 2019/11/19
// https://stackoverflow.com/questions/27936772/how-to-deep-merge-instead-of-shallow-merge
// Did not work:
// inputData was: {"CommandWorkflows":{"businessRules":[{"arrayParsing":[{"characterArrayParsing":[{"Workflow":[{"$":{"Name":"doesArrayContainCharacter","Value":"cmdgen bizRul,doesArrayContainCharacter,$,[the|answer|to|life|the|universe|and|everything|is|$42] 4"}},{"$":{"Name":"removeCharacterFromArray","Value":"cmdgen bizRul,removeCharacterFromArray,$,[the|answer|to|life|the|universe|and|everything|is|$42] 4"}},{"$":{"Name":"replaceCharacterAtIndex","Value":"cmdgen bizRul,replaceCharacterAtIndex,aggregateNumericalDifferenceBetweenTwoStrings,[10|$] 4"}},{"$":{"Name":"characterArrayParsing","Value":"cmdSeq wrkflo,doesArrayContainCharacter wrkflo,removeCharacterFromArray wrkflo,replaceCharacterAtIndex"}}]}]}]}]}}
//
// inputMetaData was: {"CommandWorkflows":{"businessRules":[{"arrayParsing":[{"pathArrayParsing":[{"Workflow":[{"$":{"Name":"doesArrayContainFilename","Value":"cmdgen bizRul,doesArrayContainFilename,[20200603-142834-763_0.3.2_haystacks.zip|20200603-144529-749_0.3.3_haystacks.zip|20200604-133509-704_0.4.0_haystacks.zip|20200604-133546-749_0.4.1_haystacks.zip|20200604-160655-262_0.4.2_haystacks.zip|20200604-161819-191_0.4.3_haystacks.zip|20200604-173727-348_0.4.4_haystacks.zip|20200604-193551-258_0.5.0_haystacks.zip|20200604-193629-853_0.5.1_haystacks.zip|20200605-083055-193_0.5.2_haystacks.zip|20200610-170634-141_0.5.3_haystacks.zip|20200615-162658-576_0.5.4_haystacks.zip|20200617-150430-416_0.5.5_haystacks.zip|20200618-134424-575_0.5.6_haystacks.zip],20200604-193551-258_0.5.0_haystacks.zip 4"}},{"$":{"Name":"pathArrayParsing","Value":"cmdSeq wrkflo,doesArrayContainFilename"}}]}]}]}]}}
//
// returnData was: {"CommandWorkflows":{"businessRules":[{"arrayParsing":[{"characterArrayParsing":[{"Workflow":[{"$":{"Name":"doesArrayContainCharacter","Value":"cmdgen bizRul,doesArrayContainCharacter,$,[the|answer|to|life|the|universe|and|everything|is|$42] 4"}},{"$":{"Name":"removeCharacterFromArray","Value":"cmdgen bizRul,removeCharacterFromArray,$,[the|answer|to|life|the|universe|and|everything|is|$42] 4"}},{"$":{"Name":"replaceCharacterAtIndex","Value":"cmdgen bizRul,replaceCharacterAtIndex,aggregateNumericalDifferenceBetweenTwoStrings,[10|$] 4"}},{"$":{"Name":"characterArrayParsing","Value":"cmdSeq wrkflo,doesArrayContainCharacter wrkflo,removeCharacterFromArray wrkflo,replaceCharacterAtIndex"}}]}]}]}]}}
//
// Clearly it didn't do it's job!!
// **************************************************
// if (inputData && inputMetaData) {
// for (let key of Object.entries(inputMetaData)) {
// if (!inputData.hasOwnProperty(key) || typeof inputMetaData[key] !== wrd.cobject) { inputData[key] = inputMetaData[key]; }
// else if (inputData[key] instanceof Array && inputMetaData[key] instanceof Array) {inputData[key] = inputData[key].concat(inputMetaData[key]); }
// else { objectDeepMerge(inputData[key], inputMetaData[key]); }
// } // End-for (key of Object.keys(inputMetaData))
// returnData = inputData;
// } // End-if (inputData && inputMetaData)
// ***************************************************
// NOTE: This implementation from Pery Mimon on the reference: Dated 2020/04/23
// https://stackoverflow.com/questions/27936772/how-to-deep-merge-instead-of-shallow-merge
// Did not work:
// Throws an error: inputMetaData is not iterable
// **************************************************
// if (inputData && inputMetaData) {
// for (source of inputMetaData) {
// for (let k in source) {
// let vs = source[k], vt = inputData[k];
// if (Object(vs) === vs && Object(vt) === vt) {
// inputData[k] = objectDeepMerge(vt, vs);
// continue;
// }
// inputData[k] = source[k];
// } // End-for (let k in source)
// } // End-for (source of inputMetaData)
// returnData = inputData;
// } // End-if (inputData && inputMetaData)
// **************************************************
await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
return returnData;
}
/**
* @function getNamespacedDataObject
* @description Navigates the D data structure JSON data object tree to find the namespace of data settings.
* @param {array<string>} inputData The path in the data JSON object where the
* setting should be returned.
* @param {boolean} inputMetaData True or False value to indicate if
* the path elements should be created or not it they are not found.
* @return {object|boolean} The object found at the specified namespace address in the data object,
* or False if nothing was found.
* @author Seth Hollingsead
* @date 2022/05/10
* @NOTE This function contains duplicate code from the configurator.js,
* however, we cannot call this business rule from the configurator because
* the configurator gets called before the business rules are bootstrapped.
* And there are protections in place, but rules cannot be called if they are not yet bootstrapped.
* So therefore this code will need to remain duplicated.
* And by the time this gets called everything should be effectively bootstrapped.
* Therefore we can use the loggers here.
*/
async function getNamespacedDataObject(inputData, inputMetaData) {
let functionName = getNamespacedDataObject.name;
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + JSON.stringify(inputData));
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + inputMetaData);
let returnData = false;
let processingValidData = false;
let namespaceDataObject = D;
if (inputData && inputData.length > 0) {
for (const element of inputData) {
processingValidData = true;
if (!namespaceDataObject[element] && inputMetaData === true) {
// It doesn't exist yet, so lets make it.
namespaceDataObject[element] = {};
} else if (!namespaceDataObject[element]) {
console.log(msg.cnamespaceDataObjectPathNotFound + JSON.stringify(element));
processingValidData = false;
break;
}
namespaceDataObject = namespaceDataObject[element];
} // End-for (const element of inputData)
if (processingValidData === true) {
returnData = namespaceDataObject;
}
} // End-if (inputData && inputData.length > 0)
await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData);
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
return returnData;
}
/**
* @function setNamespacedDataObject
* @description Persists a change to the data structure.
* @param {array<string>} inputData The path in the data JSON object where the
* setting should be persisted.
* @param {object} inputMetaData The data to be persisted on the D-data structure.
* @return {boolean} True or False to indicate if the data was persisted correctly or not.
* @author Seth Hollingsead
* @date 2022/05/11
*/
async function setNamespacedDataObject(inputData, inputMetaData) {
let functionName = setNamespacedDataObject.name;
await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + JSON.stringify(inputData));
await loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + JSON.stringify(inputMetaData));
let returnData = false;
let namespaceDataObject = D;
if (inputData && inputData.length > 0) {
for (let i = 0; i < inputData.length - 1; i++) {
namespaceDataObject = namespaceDataObject[inputData[i]];
if (i === inputData.length - 2) {
// namespaceDataObject is:
await loggers.consoleLog(namespacePrefix + functionName, msg.cnamespaceDataObjectIs + JSON.stringify(namespaceDataObject));
let fullyQualifiedKey = await namespaceDataObject.join(bas.cDot);
if (await ruleParsing.processRulesInternal([[namespaceDataObject, cfg.cdebugSetting], await ruleParsing.getRule(biz.cascertainMatchingElements)], [biz.cdoesArrayContainValue]) === true) {
namespaceDataObject[fullyQualifiedKey] = inputMetaData;
} else {
namespaceDataObject[inputData[i + 1]] = inputMetaData;
}
returnData = true;
} // End-if (i === inputData.length - 2)
} // End-for (let i = 0; i < inputData.length - 1; i++)
} // End-if (inputData && inputData.length > 0)
await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData);
await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
return returnData;
}
export default {
arraysAreEqual,
storeData,
getStoredData,
isObjectEmpty,
isArrayEmpty,
isObject,
isArray,
isArrayOrObject,
isNonZeroLengthArray,
arrayDeepClone,
objectDeepClone,
objectDeepMerge,
getNamespacedDataObject,
setNamespacedDataObject
};