Last active
November 23, 2020 14:50
-
-
Save JohnMadakin/3c36b6645be28836011adb7a319b5f9f to your computer and use it in GitHub Desktop.
rulerunner
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module.exports = function schemmaRunner(dataObject, ruleSchema) { | |
console.log('+++++++ ', combineRuleSchema(dataObject, ruleSchema)); | |
}; | |
function combineRuleSchema(dataObject, ruleSchema) { | |
const result = []; | |
const ruleSet = ruleSchema.ruleSet; | |
if (ruleSet['and'] && ruleSet['and'].length) { | |
result.push(evaluateRuleSchema(ruleSet, dataObject, 'and')); | |
} | |
if (ruleSet['or'] && ruleSet['or'].length) { | |
result.push(evaluateRuleSchema(ruleSet, dataObject, 'or')); | |
} | |
if (result.length) { | |
return processResult(result, ruleSchema.ruleSetBooleanOp); | |
} | |
return { booleanResult: false, resultValue: null }; | |
} | |
function evaluateRuleSchema(ruleSet, dataObject, setBooleanOp, result = [], depth = 0) { | |
let index = depth; | |
// { booleanResult: true, resultValue: 20, } | |
const ruleSetResult = evaluateRuleSet(ruleSet[setBooleanOp][index], dataObject); | |
ruleSetResult.resultValue = ruleSet[setBooleanOp][index].result || null; | |
ruleSetResult.weight = ruleSet[setBooleanOp][index].weight || null; | |
if (ruleSetResult.booleanResult === true && ruleSet[setBooleanOp][index].breakOn === true) { | |
return ruleSetResult; | |
} | |
result.push(ruleSetResult); | |
if (index === ruleSet[setBooleanOp].length - 1) { | |
return processResult(result, setBooleanOp); | |
} | |
index += 1; | |
return evaluateRuleSchema(ruleSet, dataObject, setBooleanOp, result, index); | |
} | |
function evaluateRuleSet(ruleSet, dataObject, result = [], depth = 0) { | |
let index = depth; | |
// { booleanResult: true, resultValue: 20, } | |
const rulesResult = evaluateRules(ruleSet.rules[index], dataObject); | |
rulesResult.resultValue = ruleSet.rules[index].result || null; | |
rulesResult.weight = ruleSet.rules[index].weight || null; | |
if (rulesResult.booleanResult === true && ruleSet.rules[index].breakOn === true) { | |
return rulesResult; | |
} | |
result.push(rulesResult); | |
// if this is the last item in the array, return gracefully | |
if (index === ruleSet.rules.length - 1) { | |
return processResult(result, ruleSet.setBooleanOp); | |
} | |
index += 1; | |
return evaluateRuleSet(ruleSet, dataObject, result, index); | |
} | |
function evaluateRules(rule, dataObject) { | |
// { booleanResult: true, resultValue: 20, } | |
const conditionResult = evaluateArrayConditions(rule.conditions, dataObject, rule.booleanOp); | |
return conditionResult; | |
} | |
function evaluateArrayConditions(conditions, dataObject, booleanOp, result = [], depth = 0) { | |
let index = depth; | |
// { booleanResult: true, resultValue: 20, } | |
const conditionResult = evaluateCondition(conditions[index], dataObject); | |
conditionResult.resultValue = conditions[index].result || null; | |
conditionResult.weight = conditions[index].weight || null; | |
result.push(conditionResult); | |
if (conditionResult.booleanResult === true && conditions[index].breakOn === true) { | |
return { | |
booleanResult: conditionResult.booleanResult, | |
resultValue: conditionResult.resultValue, | |
}; | |
} | |
// if last item, return result | |
if (index === conditions.length - 1) { | |
return processResult(result, booleanOp); | |
} | |
index += 1; | |
return evaluateArrayConditions(conditions, dataObject, booleanOp, result, index); | |
} | |
function evaluateEachCondition(con) { | |
const condition = con; | |
return { | |
evaluate: (function evaluate(value, conditionValue) { | |
const cond = { | |
eq: function eq(x = value, y = conditionValue) { | |
return x === y; | |
}, | |
neq: function eq(x = value, y = conditionValue) { | |
return x !== y; | |
}, | |
gt: function gt(x = value, y = conditionValue) { | |
return x > y; | |
}, | |
gte: function gte(x = value, y = conditionValue) { | |
return x >= y; | |
}, | |
lte: function gte(x = value, y = conditionValue) { | |
return x <= y; | |
}, | |
lt: function lt(x = value, y = conditionValue) { | |
return x < y; | |
}, | |
regex: function regex(x = value, y = conditionValue) { | |
return new RegExp(y).test(x); | |
}, | |
nregex: function nregex(x = value, y = conditionValue) { | |
return !new RegExp(y).test(x); | |
}, | |
stringcontains: function stringcontains(x = value, y = conditionValue) { | |
if (typeof y !== 'string' || typeof x !== 'string') return false; | |
return x.includes(y); | |
}, | |
arraycontains: function arraycontains(x = value, y = conditionValue) { | |
if (!Array.isArray(y)) return false; | |
return y.includes(x); | |
}, | |
objectcontains: function objectcontains(x = value, y = conditionValue) { | |
if(!x || typeof x === 'object') return false; | |
return !!x[y]; | |
}, | |
}; | |
return cond[condition]; | |
})(), | |
}; | |
} | |
function evaluateCondition(condition, dataObject) { | |
if (dataObject[condition.prop]) { | |
let result = true; | |
const shouldNotInvert = { | |
regex: 1, | |
nregex: 1, | |
arraycontains: 1, | |
objectcontains: 1, | |
}; | |
if (condition.isInverted && !shouldNotInvert[condition.condition]) { | |
result = evaluateEachCondition(condition.condition).evaluate( | |
condition.conditionVal, | |
dataObject[condition.prop], | |
); | |
} else { | |
result = evaluateEachCondition(condition.condition).evaluate( | |
dataObject[condition.prop], | |
condition.conditionVal, | |
); | |
} | |
return { | |
booleanResult: !!result, | |
}; | |
} | |
return { | |
booleanResult: false | |
}; | |
} | |
// [{ booleanResult: true, weight: 100, resultValue: 2}] | |
function processResult(result, bool = 'or') { | |
// check for length of the result first | |
if (result.length === 1) { | |
const finRes = result[0].booleanResult | |
? result[0] | |
: { booleanResult: false, resultValue: null }; | |
return finRes; | |
} | |
const filResult = result.filter((res) => res.booleanResult === true); | |
const sortedByWeight = filResult.sort((a, b) => b.weight - a.weight); | |
if (bool === 'and') { | |
if (filResult.length === result.length) { | |
// means the item at the head of the array has the highest weight | |
if (sortedByWeight[0] && sortedByWeight[0].weight) { | |
return { booleanResult: true, resultValue: sortedByWeight[0].resultValue || null }; | |
} | |
// means weight is undefined || NaN. Go ahead and pick the result of the last item if any | |
return { | |
booleanResult: true, | |
resultValue: sortedByWeight[sortedByWeight.length - 1].resultValue || null, | |
}; | |
} | |
return { booleanResult: false, resultValue: null }; | |
} | |
if (bool === 'or') { | |
if (sortedByWeight[0] && sortedByWeight[0].booleanResult === true) { | |
return { booleanResult: true, resultValue: sortedByWeight[0].resultValue || null }; | |
} | |
return { booleanResult: false, resultValue: null }; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment