Skip to content

Commit

Permalink
Merge pull request #390 from emilefokkemanavara/fix/352/rule-result-json
Browse files Browse the repository at this point in the history
fix: improve typing of RuleResult
  • Loading branch information
chris-pardy authored Nov 12, 2024
2 parents ae8fa05 + 1d00bef commit 9d51178
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 3 deletions.
64 changes: 64 additions & 0 deletions test/engine-event.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ describe('Engine: event', () => {
canOrderDrinks: true
}
}

const awesomeEvent = {
type: 'awesome'
}
/**
* sets up a simple 'any' rule with 2 conditions
*/
Expand Down Expand Up @@ -92,6 +96,46 @@ describe('Engine: event', () => {
engine.addFact('gender', 'male') // gender succeeds
}

function setupWithConditionReference () {
const conditionName = 'awesomeCondition'
const conditions = {
any: [{ condition: conditionName }]
}
engine = engineFactory()
const ruleOptions = { conditions, event: awesomeEvent, priority: 100 }
const rule = factories.rule(ruleOptions)
engine.addRule(rule)
engine.setCondition(conditionName, {
all: [{
name: 'over 21',
fact: 'age',
operator: 'greaterThanInclusive',
value: 21
}]
})
engine.addFact('age', 21)
}

function setupWithUndefinedCondition () {
const conditionName = 'conditionThatIsNotDefined'
const conditions = {
any: [
{ condition: conditionName, name: 'nameOfTheUndefinedConditionReference' },
{
name: 'over 21',
fact: 'age',
operator: 'greaterThanInclusive',
value: 21
}
]
}
engine = engineFactory([], { allowUndefinedConditions: true })
const ruleOptions = { conditions, event: awesomeEvent, priority: 100 }
const rule = factories.rule(ruleOptions)
engine.addRule(rule)
engine.addFact('age', 21)
}

context('engine events: simple', () => {
beforeEach(() => simpleSetup())

Expand Down Expand Up @@ -602,4 +646,24 @@ describe('Engine: event', () => {
expect(JSON.stringify(ruleResult)).to.equal(expected)
})
})

context('rule events: json serializing with condition reference', () => {
beforeEach(() => setupWithConditionReference())
it('serializes properties', async () => {
const { results: [ruleResult] } = await engine.run()
const expected = '{"conditions":{"priority":1,"any":[{"priority":1,"all":[{"name":"over 21","operator":"greaterThanInclusive","value":21,"fact":"age","factResult":21,"result":true}]}]},"event":{"type":"awesome"},"priority":100,"result":true}'
expect(JSON.stringify(ruleResult)).to.equal(expected)
})
})

context('rule events: json serializing with condition reference that is undefined', () => {
beforeEach(() => setupWithUndefinedCondition())
it('serializes properties', async () => {
const { results: [ruleResult] } = await engine.run()
const { conditions: { any: [conditionReference] } } = ruleResult
expect(conditionReference.result).to.equal(false)
const expected = '{"conditions":{"priority":1,"any":[{"name":"nameOfTheUndefinedConditionReference","condition":"conditionThatIsNotDefined"},{"name":"over 21","operator":"greaterThanInclusive","value":21,"fact":"age","factResult":21,"result":true}]},"event":{"type":"awesome"},"priority":100,"result":true}'
expect(JSON.stringify(ruleResult)).to.equal(expected)
})
})
})
41 changes: 40 additions & 1 deletion types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,12 +153,22 @@ export type RuleSerializable = Pick<
"conditions" | "event" | "name" | "priority"
>;

export type RuleResultSerializable = Pick<
Required<RuleResult>,
"name" | "event" | "priority" | "result"> & {
conditions: TopLevelConditionResultSerializable
}

export interface RuleResult {
name: string;
conditions: TopLevelCondition;
conditions: TopLevelConditionResult;
event?: Event;
priority?: number;
result: any;
toJSON(): string;
toJSON<T extends boolean>(
stringify: T
): T extends true ? string : RuleResultSerializable;
}

export class Rule implements RuleProperties {
Expand All @@ -176,6 +186,14 @@ export class Rule implements RuleProperties {
): T extends true ? string : RuleSerializable;
}

interface BooleanConditionResultProperties {
result?: boolean
}

interface ConditionResultProperties extends BooleanConditionResultProperties {
factResult?: unknown
}

interface ConditionProperties {
fact: string;
operator: string;
Expand All @@ -186,25 +204,46 @@ interface ConditionProperties {
name?: string;
}

type ConditionPropertiesResult = ConditionProperties & ConditionResultProperties

type NestedCondition = ConditionProperties | TopLevelCondition;
type NestedConditionResult = ConditionPropertiesResult | TopLevelConditionResult;
type AllConditions = {
all: NestedCondition[];
name?: string;
priority?: number;
};
type AllConditionsResult = AllConditions & {
all: NestedConditionResult[]
} & BooleanConditionResultProperties
type AnyConditions = {
any: NestedCondition[];
name?: string;
priority?: number;
};
type AnyConditionsResult = AnyConditions & {
any: NestedConditionResult[]
} & BooleanConditionResultProperties
type NotConditions = { not: NestedCondition; name?: string; priority?: number };
type NotConditionsResult = NotConditions & {not: NestedConditionResult} & BooleanConditionResultProperties;
type ConditionReference = {
condition: string;
name?: string;
priority?: number;
};
type ConditionReferenceResult = ConditionReference & BooleanConditionResultProperties
export type TopLevelCondition =
| AllConditions
| AnyConditions
| NotConditions
| ConditionReference;
export type TopLevelConditionResult =
| AllConditionsResult
| AnyConditionsResult
| NotConditionsResult
| ConditionReferenceResult
export type TopLevelConditionResultSerializable =
| AllConditionsResult
| AnyConditionsResult
| NotConditionsResult
| ConditionReference
21 changes: 19 additions & 2 deletions types/index.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ import rulesEngine, {
Rule,
RuleProperties,
RuleResult,
RuleSerializable
RuleSerializable,
TopLevelConditionResult,
AnyConditionsResult,
AllConditionsResult,
NotConditionsResult
} from "../";

// setup basic fixture data
Expand Down Expand Up @@ -126,7 +130,20 @@ engine.on<{ foo: Array<string> }>('foo', (event, almanac, ruleResult) => {
})

// Run the Engine
expectType<Promise<EngineResult>>(engine.run({ displayMessage: true }));
const result = engine.run({ displayMessage: true })
expectType<Promise<EngineResult>>(result);

const topLevelConditionResult = result.then(r => r.results[0].conditions);
expectType<Promise<TopLevelConditionResult>>(topLevelConditionResult)

const topLevelAnyConditionsResult = topLevelConditionResult.then(r => (r as AnyConditionsResult).result);
expectType<Promise<boolean | undefined>>(topLevelAnyConditionsResult)

const topLevelAllConditionsResult = topLevelConditionResult.then(r => (r as AllConditionsResult).result);
expectType<Promise<boolean | undefined>>(topLevelAllConditionsResult)

const topLevelNotConditionsResult = topLevelConditionResult.then(r => (r as NotConditionsResult).result);
expectType<Promise<boolean | undefined>>(topLevelNotConditionsResult)

// Alamanac tests
const almanac: Almanac = (await engine.run()).almanac;
Expand Down

0 comments on commit 9d51178

Please sign in to comment.