Repository: CacheControl/json-rules-engine
Pull request #396 (2024-11-16 17:54:44)

Add RightSideComparisonResult as factResult
Hello!

I noticed the issue mentioned in #310, which is important for my use case, so I tried to address it with this pull request.

My implementation approach is straightforward. When setting the factResult for a condition, if the value property is an object, I include the rightSideValue as the factResult.

I'm eager to see this merged, as it is of great interest to me.




Patch:
diff --git a/src/condition.js b/src/condition.js
index 28a86a3..5668959 100644
--- a/src/condition.js
+++ b/src/condition.js
@@ -62,6 +62,9 @@ export default class Condition {
       if (this.factResult !== undefined) {
         props.factResult = this.factResult
       }
+      if (this.valueResult !== undefined) {
+        props.valueResult = this.valueResult
+      }
       if (this.result !== undefined) {
         props.result = this.result
       }
diff --git a/src/rule.js b/src/rule.js
index 39e5a32..3d52fbf 100644
--- a/src/rule.js
+++ b/src/rule.js
@@ -231,6 +231,7 @@ class Rule extends EventEmitter {
           .then((evaluationResult) => {
             const passes = evaluationResult.result
             condition.factResult = evaluationResult.leftHandSideValue
+            condition.valueResult = evaluationResult.rightHandSideValue
             condition.result = passes
             return passes
           })
diff --git a/types/index.d.ts b/types/index.d.ts
index 827e79f..6dea6b8 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -192,6 +192,7 @@ interface BooleanConditionResultProperties {
 
 interface ConditionResultProperties extends BooleanConditionResultProperties {
   factResult?: unknown
+  valueResult?: unknown
 }
 
 interface ConditionProperties {


Test patch:
diff --git a/test/acceptance/acceptance.js b/test/acceptance/acceptance.js
index fb68cc7..ca470e2 100644
--- a/test/acceptance/acceptance.js
+++ b/test/acceptance/acceptance.js
@@ -36,6 +36,7 @@ describe('Acceptance', () => {
       path: '$.values',
       value: 2,
       factResult: [2],
+      valueResult: 2,
       result: true
     },
     {
@@ -43,6 +44,7 @@ describe('Acceptance', () => {
       operator: 'in',
       value: [2],
       factResult: 2,
+      valueResult: [2],
       result: true
     }
     ],
@@ -169,7 +171,8 @@ describe('Acceptance', () => {
             },
             path: '$.values',
             result: true,
-            value: 2
+            value: 2,
+            valueResult: 2
           },
           {
             fact: 'low-priority',
@@ -178,6 +181,9 @@ describe('Acceptance', () => {
             result: true,
             value: [
               2
+            ],
+            valueResult: [
+              2
             ]
           }
         ],
@@ -202,6 +208,7 @@ describe('Acceptance', () => {
             factResult: [
               2
             ],
+            valueResult: 2,
             operator: 'containsDivisibleValuesOf',
             params: {
               factParam: 1
diff --git a/test/engine-event.test.js b/test/engine-event.test.js
index d86f75c..a4035dd 100644
--- a/test/engine-event.test.js
+++ b/test/engine-event.test.js
@@ -642,7 +642,7 @@ describe('Engine: event', () => {
       await engine.run()
       const ruleResult = successSpy.getCall(0).args[2]
       const expected =
-        '{"conditions":{"priority":1,"any":[{"name":"over 21","operator":"greaterThanInclusive","value":21,"fact":"age","factResult":21,"result":true},{"operator":"equal","value":true,"fact":"qualified","factResult":false,"result":false}]},"event":{"type":"setDrinkingFlag","params":{"canOrderDrinks":true}},"priority":100,"result":true}'
+        '{"conditions":{"priority":1,"any":[{"name":"over 21","operator":"greaterThanInclusive","value":21,"fact":"age","factResult":21,"valueResult":21,"result":true},{"operator":"equal","value":true,"fact":"qualified","factResult":false,"valueResult":true,"result":false}]},"event":{"type":"setDrinkingFlag","params":{"canOrderDrinks":true}},"priority":100,"result":true}'
       expect(JSON.stringify(ruleResult)).to.equal(expected)
     })
   })
@@ -651,7 +651,7 @@ describe('Engine: event', () => {
     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}'
+      const expected = '{"conditions":{"priority":1,"any":[{"priority":1,"all":[{"name":"over 21","operator":"greaterThanInclusive","value":21,"fact":"age","factResult":21,"valueResult":21,"result":true}]}]},"event":{"type":"awesome"},"priority":100,"result":true}'
       expect(JSON.stringify(ruleResult)).to.equal(expected)
     })
   })
@@ -662,7 +662,7 @@ describe('Engine: event', () => {
       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}'
+      const expected = '{"conditions":{"priority":1,"any":[{"name":"nameOfTheUndefinedConditionReference","condition":"conditionThatIsNotDefined"},{"name":"over 21","operator":"greaterThanInclusive","value":21,"fact":"age","factResult":21,"valueResult":21,"result":true}]},"event":{"type":"awesome"},"priority":100,"result":true}'
       expect(JSON.stringify(ruleResult)).to.equal(expected)
     })
   })
diff --git a/test/engine-fact-comparison.test.js b/test/engine-fact-comparison.test.js
index 39af590..feb08c8 100644
--- a/test/engine-fact-comparison.test.js
+++ b/test/engine-fact-comparison.test.js
@@ -118,4 +118,23 @@ describe('Engine: fact to fact comparison', () => {
       expect(eventSpy.callCount).to.equal(0)
     })
   })
+
+  context('constant facts: checking valueResult and factResult', () => {
+    const constantCondition = {
+      all: [{
+        fact: 'height',
+        operator: 'lessThanInclusive',
+        value: {
+          fact: 'width'
+        }
+      }]
+    }
+    it('result has the correct valueResult and factResult properties', async () => {
+      setup(constantCondition)
+      const result = await engine.run({ height: 1, width: 2 })
+
+      expect(result.results[0].conditions.all[0].factResult).to.equal(1)
+      expect(result.results[0].conditions.all[0].valueResult).to.equal(2)
+    })
+  })
 })