diff --git a/rita-core/src/Parser.ts b/rita-core/src/Parser.ts index 69382c6..52d76fd 100644 --- a/rita-core/src/Parser.ts +++ b/rita-core/src/Parser.ts @@ -161,7 +161,11 @@ export default class Parser { * @param jsonRuleset the atom */ public parseAtom(jsonRuleset: Record): Atom { - return new Atom(jsonRuleset['path'], !!jsonRuleset['isDate']); + return new Atom( + jsonRuleset['path'], + !!jsonRuleset['isDate'], + jsonRuleset['default'] + ); } /** diff --git a/rita-core/src/logicElements/Atom.ts b/rita-core/src/logicElements/Atom.ts index 1b5f224..1bb6f0c 100644 --- a/rita-core/src/logicElements/Atom.ts +++ b/rita-core/src/logicElements/Atom.ts @@ -25,22 +25,30 @@ export class Atom extends Formula { */ public path: string; public isDate: boolean; + public defaultValue?: FormulaResults | Array; - constructor(path: string, isDate: boolean = false) { + constructor( + path: string, + isDate: boolean = false, + defaultValue?: FormulaResults | Array + ) { super(); this.path = path; this.isDate = isDate; + this.defaultValue = defaultValue; } /** * Get the value of an object property or array by a path that is passed as string * @param object object * @param path path + * @param defaultVal default value to return if path is not found * @param context the context of the formula */ - static getPropertyByString( + static async getPropertyByString( object: any, path: string, + defaultVal?: FormulaResults | Array, context?: Formula ): Promise { path = path.replace(/\[(\w+)]/g, '.$1'); // convert indexes to properties @@ -50,6 +58,8 @@ export class Atom extends Formula { const k = a[i]; if (k in object) { object = object[k]; + } else if (defaultVal) { + return defaultVal; } else { throw new UndefinedPathError( 'Undefinded path in data: ' + path, @@ -68,7 +78,12 @@ export class Atom extends Formula { getPropertyByString( object: any ): Promise { - return Atom.getPropertyByString(object, this.path, this); + return Atom.getPropertyByString( + object, + this.path, + this.defaultValue, + this + ); } validate(): boolean { @@ -81,13 +96,15 @@ export class Atom extends Formula { path: this.path, }; if (this.isDate) at['isDate'] = true; + if (this.defaultValue) at['default'] = this.defaultValue; return at; } async evaluate( data: Record - ): Promise> { - const val = this.getPropertyByString(data); + ): Promise { + let val: FormulaResults | FormulaResults[] = + await this.getPropertyByString(data); if (typeof val === 'string' && this.isDate) { return parseDate(val, this); diff --git a/rita-core/src/schema/atom.json b/rita-core/src/schema/atom.json index 817a86f..bbcea72 100644 --- a/rita-core/src/schema/atom.json +++ b/rita-core/src/schema/atom.json @@ -17,6 +17,26 @@ "type": "boolean", "default": false, "description": "Must be set to true if the atom should be parsed as a date" + }, + "default": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "string", + "format": "date-time" + }, + { + "type": "array" + } + ] } }, "required": ["type", "path"], diff --git a/rita-core/test/assets/defaultVal.json b/rita-core/test/assets/defaultVal.json new file mode 100644 index 0000000..3b045c8 --- /dev/null +++ b/rita-core/test/assets/defaultVal.json @@ -0,0 +1,21 @@ +{ + "$schema": "../../../rita-core/src/schema/schema.json", + "rules": [ + { + "id": "r1", + "comment": "", + "rule": { + "type": "comparison", + "operation": "equal", + "arguments": [ + { + "type": "atom", + "path": "fancyness", + "default": "unicorn" + }, + "unicorn" + ] + } + } + ] +} diff --git a/rita-core/test/implementationTests/atom.test.ts b/rita-core/test/implementationTests/atom.test.ts index 5b4807d..073cd8b 100644 --- a/rita-core/test/implementationTests/atom.test.ts +++ b/rita-core/test/implementationTests/atom.test.ts @@ -1,6 +1,8 @@ import { Parser } from '../../src'; // @ts-ignore import { exampleData, ruleTemplate } from '../assets/exampleData'; +// @ts-ignore +import ruleset_defaultVal from '../assets/defaultVal.json'; const p = new Parser(); @@ -44,3 +46,11 @@ it('second customer rated', () => { }); expect(rule.evaluate(exampleData)).resolves.toBe(true); }); +it('use default value', () => { + const rule = p.parseRuleSet(ruleset_defaultVal)[0]; + expect(rule.evaluate({})).resolves.toBe(true); +}); +it('do not use default value when value is specified', () => { + const rule = p.parseRuleSet(ruleset_defaultVal)[0]; + expect(rule.evaluate({ fancyness: 'very' })).resolves.toBe(false); +}); diff --git a/rita-core/test/schema.test.ts b/rita-core/test/schema.test.ts index 5a693c7..6ebd96a 100644 --- a/rita-core/test/schema.test.ts +++ b/rita-core/test/schema.test.ts @@ -14,6 +14,8 @@ import rule_qfa from './assets/quantifiers_fa.json'; // @ts-ignore import rule_qex from './assets/quantifiers_ex.json'; // @ts-ignore +import rule_defaultValue from './assets/defaultVal.json'; +// @ts-ignore import macros from './assets/macros'; import { DateTime } from 'luxon'; @@ -132,6 +134,7 @@ describe('Validate Rule examples', () => { rule_qfa, rule_qex, macros, + rule_defaultValue, ]; for (let i = 0; i < examples.length; i++) { const example = examples[i];