Last active
August 4, 2020 04:29
-
-
Save adamkl/5d89a100300536582e0091e2e2e861db to your computer and use it in GitHub Desktop.
Helpers for json-rule-engine
This file contains hidden or 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
import { processRules, RuleEngineError } from "./ruleEngineHelpers.ts"; | |
const rules = [ruleA, ruleB, ruleC]; | |
const facts = { | |
fact1: "Some data", | |
fact2: "Some other data", | |
fact3: 42 | |
}; | |
const { errors, events } = await processRules([rule1, rule2, rule3], facts); | |
if (errors.length > 0) { | |
throw new RuleEngineError(errors); | |
} |
This file contains hidden or 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
import { | |
Rule, | |
Engine, | |
RuleResult, | |
AllConditions, | |
Almanac, | |
AnyConditions, | |
Event, | |
NestedCondition, | |
ConditionProperties | |
} from 'json-rules-engine'; | |
export async function processRules<T>(rules: Rule[], facts: T) { | |
const errors = []; | |
const engine = new Engine(rules).on( | |
'failure', | |
async (event, almanac, ruleResult) => { | |
errors.push({ | |
type: event.type, | |
failedConditions: await getFailedConditions(almanac, ruleResult) | |
}); | |
} | |
); | |
const result = await engine.run(facts); | |
return { | |
...result, | |
errors | |
}; | |
} | |
async function getFailedConditions(almanac: Almanac, ruleResult: RuleResult) { | |
const allConditions = (ruleResult.conditions as AllConditions).all || []; | |
const anyConditions = (ruleResult.conditions as AnyConditions).any || []; | |
const conditions = [ | |
...allConditions, | |
...anyConditions | |
] as (ConditionProperties & { result: unknown; factResult: unknown })[]; | |
if (conditions.length === 0) { | |
return []; | |
} | |
return Promise.all( | |
conditions | |
.filter(c => !c.result) | |
.map(async c => { | |
const fact = c.fact; | |
const expected = c.value.fact | |
? await almanac.factValue(c.value.fact) | |
: c.value; | |
const actual = c.factResult; | |
return { | |
message: `condition for ${fact} failed: expected '${expected}', actual '${actual}'`, | |
fact, | |
expected, | |
actual | |
}; | |
}) | |
); | |
} | |
export class RuleEngineError extends Error { | |
constructor(errors) { | |
const message = errors | |
.map( | |
e => `${e.type}[${e.failedConditions.map(c => c.message).join(',')}]` | |
) | |
.join('; '); | |
super(message); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment