Skip to content

Instantly share code, notes, and snippets.

@JohnMadakin
Last active November 23, 2020 14:50
Show Gist options
  • Save JohnMadakin/3c36b6645be28836011adb7a319b5f9f to your computer and use it in GitHub Desktop.
Save JohnMadakin/3c36b6645be28836011adb7a319b5f9f to your computer and use it in GitHub Desktop.
rulerunner
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