From 669b2ea3ffce113b96fb3d927465d65dbd0cab60 Mon Sep 17 00:00:00 2001 From: mrednic Date: Wed, 18 Dec 2024 10:28:48 +0100 Subject: [PATCH 1/3] fix: jsonpath-plus audit update --- package.json | 2 +- packages/@o3r/components/package.json | 2 +- packages/@o3r/rules-engine/package.json | 2 +- yarn.lock | 44 ++++++++++++------------- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/package.json b/package.json index e5def39df9..5ec0f4f66f 100644 --- a/package.json +++ b/package.json @@ -227,7 +227,7 @@ "jest-preset-angular": "~14.1.0", "js-yaml": "^4.1.0", "jsonc-eslint-parser": "~2.4.0", - "jsonpath-plus": "~10.0.0", + "jsonpath-plus": "~10.2.0", "lighthouse": "9.6.8", "lint-staged": "^15.0.0", "minimist": "^1.2.6", diff --git a/packages/@o3r/components/package.json b/packages/@o3r/components/package.json index 53580d1d9e..00793bd0d5 100644 --- a/packages/@o3r/components/package.json +++ b/packages/@o3r/components/package.json @@ -161,7 +161,7 @@ "jest-junit": "~16.0.0", "jest-preset-angular": "~14.1.0", "jsonc-eslint-parser": "~2.4.0", - "jsonpath-plus": "~10.0.0", + "jsonpath-plus": "~10.2.0", "memfs": "~4.9.0", "nx": "~18.3.0", "pid-from-port": "^1.1.3", diff --git a/packages/@o3r/rules-engine/package.json b/packages/@o3r/rules-engine/package.json index 75a7d7d203..0c7935b2d0 100644 --- a/packages/@o3r/rules-engine/package.json +++ b/packages/@o3r/rules-engine/package.json @@ -142,7 +142,7 @@ "jest-junit": "~16.0.0", "jest-preset-angular": "~14.1.0", "jsonc-eslint-parser": "~2.4.0", - "jsonpath-plus": "~10.0.0", + "jsonpath-plus": "~10.2.0", "memfs": "~4.9.0", "nx": "~18.3.0", "pid-from-port": "^1.1.3", diff --git a/yarn.lock b/yarn.lock index a3b87034df..0fd6eae091 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4856,21 +4856,21 @@ __metadata: languageName: node linkType: hard -"@jsep-plugin/assignment@npm:^1.2.1": - version: 1.2.1 - resolution: "@jsep-plugin/assignment@npm:1.2.1" +"@jsep-plugin/assignment@npm:^1.3.0": + version: 1.3.0 + resolution: "@jsep-plugin/assignment@npm:1.3.0" peerDependencies: jsep: ^0.4.0||^1.0.0 - checksum: 10/d8db45f052fd95b33207ded7f49af9ae48ff5ce10cb898e28a6fca722863f4a3330892c3a2c355a1a8c94fd230eef3db9be0c45324cb526e5edff7085c1f7a37 + checksum: 10/0c93b703d84af95b4be9fb6c23fbdbe7c7b6985b41c98fd10386cd54686ed1eb751cb39f5d54abcb621e4da2a0900a3b2a852e5bf7f2d322b756db3b22e42a45 languageName: node linkType: hard -"@jsep-plugin/regex@npm:^1.0.3": - version: 1.0.3 - resolution: "@jsep-plugin/regex@npm:1.0.3" +"@jsep-plugin/regex@npm:^1.0.4": + version: 1.0.4 + resolution: "@jsep-plugin/regex@npm:1.0.4" peerDependencies: jsep: ^0.4.0||^1.0.0 - checksum: 10/c08c7bd79a164995923ea799949b9f6b18dcf2bd314522ed0dcfc669fd249a06fea200606086c7d54b12d39ce3cfa61d910229e5184c667ead135f6da6997532 + checksum: 10/0ea6ba81f03955972b762fd9fbc8e3fd7e1c1c12e52ce3d4366e23c0a63c8bff8528687b8b3d8f641cf9f626f8bf5a7841efcd31a2489fe967e1900e5738ee3a languageName: node linkType: hard @@ -7259,7 +7259,7 @@ __metadata: jest-junit: "npm:~16.0.0" jest-preset-angular: "npm:~14.1.0" jsonc-eslint-parser: "npm:~2.4.0" - jsonpath-plus: "npm:~10.0.0" + jsonpath-plus: "npm:~10.2.0" memfs: "npm:~4.9.0" nx: "npm:~18.3.0" pid-from-port: "npm:^1.1.3" @@ -8208,7 +8208,7 @@ __metadata: jest-preset-angular: "npm:~14.1.0" js-yaml: "npm:^4.1.0" jsonc-eslint-parser: "npm:~2.4.0" - jsonpath-plus: "npm:~10.0.0" + jsonpath-plus: "npm:~10.2.0" lighthouse: "npm:9.6.8" lint-staged: "npm:^15.0.0" minimist: "npm:^1.2.6" @@ -8735,7 +8735,7 @@ __metadata: jest-junit: "npm:~16.0.0" jest-preset-angular: "npm:~14.1.0" jsonc-eslint-parser: "npm:~2.4.0" - jsonpath-plus: "npm:~10.0.0" + jsonpath-plus: "npm:~10.2.0" memfs: "npm:~4.9.0" nx: "npm:~18.3.0" pid-from-port: "npm:^1.1.3" @@ -22494,10 +22494,10 @@ __metadata: languageName: node linkType: hard -"jsep@npm:^1.3.9": - version: 1.3.9 - resolution: "jsep@npm:1.3.9" - checksum: 10/c60d7064c3b5047f58345e65e7618bbaecf2f46338e56689244db057b0550bf8fb7c1457a7384dfd38aca9acde3ff851d062c3f182cc1fbc66c13cb2ca0b579d +"jsep@npm:^1.4.0": + version: 1.4.0 + resolution: "jsep@npm:1.4.0" + checksum: 10/935824fe6ac28fcff3cd13878f508f99f6c13e7f0f53ec9fca0d3db465e6dd15f8af030bcdc75a38b07c78359c656647435923a26aceb91607027021f00c17f2 languageName: node linkType: hard @@ -22646,17 +22646,17 @@ __metadata: languageName: node linkType: hard -"jsonpath-plus@npm:~10.0.0": - version: 10.0.1 - resolution: "jsonpath-plus@npm:10.0.1" +"jsonpath-plus@npm:~10.2.0": + version: 10.2.0 + resolution: "jsonpath-plus@npm:10.2.0" dependencies: - "@jsep-plugin/assignment": "npm:^1.2.1" - "@jsep-plugin/regex": "npm:^1.0.3" - jsep: "npm:^1.3.9" + "@jsep-plugin/assignment": "npm:^1.3.0" + "@jsep-plugin/regex": "npm:^1.0.4" + jsep: "npm:^1.4.0" bin: jsonpath: bin/jsonpath-cli.js jsonpath-plus: bin/jsonpath-cli.js - checksum: 10/53a1edb2e82ad53d0495f5b9fb0757dc433d3904134357efad1a87f4fc312662ecc70f74dc8ad919740eb079a399307f81daeb6570e0a5e1caf1b5e7d33f3084 + checksum: 10/3a6bd775d4348f5e014249a11abb635af2f1265d83ba716b3d633ca3f118e79c318223dd685170c50652494a492f3354163bbe4cd5554bb4d7992fecf53c4874 languageName: node linkType: hard From 34b19dee5675500d322fc70cf01270d19c7876bd Mon Sep 17 00:00:00 2001 From: Kilian Panot Date: Wed, 18 Dec 2024 16:42:52 +0900 Subject: [PATCH 2/3] fix(core): add support for "null" type --- .../core/src/utils/deep-fill/deep-fill.spec.ts | 14 ++++++++++++++ .../@o3r/core/src/utils/deep-fill/deep-fill.ts | 6 ++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/@o3r/core/src/utils/deep-fill/deep-fill.spec.ts b/packages/@o3r/core/src/utils/deep-fill/deep-fill.spec.ts index 2b834f0688..435c90e81d 100644 --- a/packages/@o3r/core/src/utils/deep-fill/deep-fill.spec.ts +++ b/packages/@o3r/core/src/utils/deep-fill/deep-fill.spec.ts @@ -12,6 +12,20 @@ describe('Deep fill function', () => { expect(deepFill(base, source)).toEqual(base); }); + it('should support "null" value in source', () => { + const base = { selection: { field: 'test-field' }, a: 'string' }; + const source = { selection: null } as any; + + expect(deepFill(base, source)).toEqual({ selection: null, a: 'string' }); + }); + + it('should support "null" value in base', () => { + const base = { selection: null, a: 'string' } as any; + const source = { selection: { field: 'test-field' } }; + + expect(deepFill(base, source)).toEqual({ selection: { field: 'test-field' }, a: 'string' }); + }); + it('should keep properties from base not present in the source', () => { const base = Object.freeze({a: 1, b: '2', c: true}); const source = Object.freeze({c: false, a: 3}); diff --git a/packages/@o3r/core/src/utils/deep-fill/deep-fill.ts b/packages/@o3r/core/src/utils/deep-fill/deep-fill.ts index 70ebeb3f43..c8ac76a055 100644 --- a/packages/@o3r/core/src/utils/deep-fill/deep-fill.ts +++ b/packages/@o3r/core/src/utils/deep-fill/deep-fill.ts @@ -66,9 +66,11 @@ export function deepFill(base: T, source?: { [x: } const newObj = {...base}; for (const key in base) { - if (key in source && typeof base[key] === typeof source[key]) { + if (source[key] === null) { + newObj[key] = immutablePrimitive(null, additionalMappers); + } else if (key in source && typeof base[key] === typeof source[key]) { const keyOfSource = source[key]; - newObj[key] = typeof keyOfSource === 'undefined' ? immutablePrimitive(base[key], additionalMappers) : deepFill(base[key], source[key], additionalMappers); + newObj[key] = typeof keyOfSource === 'undefined' ? immutablePrimitive(base[key], additionalMappers) : deepFill(base[key], keyOfSource, additionalMappers); } else { newObj[key] = immutablePrimitive(base[key], additionalMappers); } From ea8cac4c9e66942d3ea05ffc3228a3d39a086cd6 Mon Sep 17 00:00:00 2001 From: mrednic Date: Wed, 18 Dec 2024 09:12:07 +0100 Subject: [PATCH 3/3] fix: reset actions emitted when no rulesets remains activated --- .../filter-ruleset-event.operator.spec.ts | 59 ++++++------------- .../helpers/filter-ruleset-event.operator.ts | 14 +++-- .../rules-engine.runner.service.spec.ts | 2 +- 3 files changed, 27 insertions(+), 48 deletions(-) diff --git a/packages/@o3r/rules-engine/src/engine/helpers/filter-ruleset-event.operator.spec.ts b/packages/@o3r/rules-engine/src/engine/helpers/filter-ruleset-event.operator.spec.ts index 1704ccf36a..34d117e79c 100644 --- a/packages/@o3r/rules-engine/src/engine/helpers/filter-ruleset-event.operator.spec.ts +++ b/packages/@o3r/rules-engine/src/engine/helpers/filter-ruleset-event.operator.spec.ts @@ -1,8 +1,8 @@ -import { BehaviorSubject, of } from 'rxjs'; +import { BehaviorSubject, firstValueFrom, of } from 'rxjs'; import { Operator } from '../operator/operator.interface'; import { operatorList } from '../operator/operators/index'; import { RulesetExecutor } from '../ruleset-executor'; -import { ActionBlock, Ruleset } from '../structure'; +import { Ruleset } from '../structure'; import { filterRulesetsEventStream } from './filter-ruleset-event.operator'; describe('Filter rulesets event operator', () => { @@ -134,68 +134,45 @@ describe('Filter rulesets event operator', () => { }, {}); const firstValue = rulesets.reduce>((accRuleset, ruleset) => { - accRuleset[ruleset.id] = new RulesetExecutor(ruleset, {retrieveOrCreateFactStream: () => of(undefined), operators} as any); + accRuleset[ruleset.id] = new RulesetExecutor(ruleset, { retrieveOrCreateFactStream: () => of(undefined), operators } as any); return accRuleset; }, {}); const rulesetsMapSubject$ = new BehaviorSubject>(firstValue); - test('should consider only first ruleset', (done) => { + test('should consider only first ruleset', async () => { - rulesetsMapSubject$.pipe( - filterRulesetsEventStream(['ruleset1']) - ).subscribe(data => { - expect(data.length).toBe(2); - done(); - }); + const data = await firstValueFrom(rulesetsMapSubject$.pipe(filterRulesetsEventStream(['ruleset1']))); + expect(data.length).toBe(2); }); - test('should consider only second ruleset', (done) => { + test('should consider only second ruleset', async () => { - rulesetsMapSubject$.pipe( - filterRulesetsEventStream(['ruleset2']) - ).subscribe(data => { - expect(data.length).toBe(1); - done(); - }); + const data = await firstValueFrom(rulesetsMapSubject$.pipe(filterRulesetsEventStream(['ruleset2']))); + expect(data.length).toBe(1); }); - test('should consider all rulesets by not passing any filter', (done) => { + test('should consider all rulesets by not passing any filter', async () => { - rulesetsMapSubject$.pipe( - filterRulesetsEventStream() - ).subscribe(data => { - expect(data.length).toBe(3); - done(); - }); + const data = await firstValueFrom(rulesetsMapSubject$.pipe(filterRulesetsEventStream())); + expect(data.length).toBe(3); }); - test('should consider all rulesets ids passed', (done) => { + test('should consider all rulesets ids passed', async () => { - rulesetsMapSubject$.pipe( - filterRulesetsEventStream(['ruleset1', 'ruleset2']) - ).subscribe(data => { - expect(data.length).toBe(3); - done(); - }); + const data = await firstValueFrom(rulesetsMapSubject$.pipe(filterRulesetsEventStream(['ruleset1', 'ruleset2']))); + expect(data.length).toBe(3); }); - test('should not emit if ruleset id does not match any registered ruleset', async () => { + test('should emit an empty array when no rulesets remain active', async () => { - let emittedActions: ActionBlock[] | undefined; + const data = await firstValueFrom(rulesetsMapSubject$.pipe(filterRulesetsEventStream(['ruleset3']))); + expect(data.length).toBe(0); - rulesetsMapSubject$.pipe( - filterRulesetsEventStream(['ruleset3']) - ).subscribe(data => { - emittedActions = data; - }); - - await jest.advanceTimersByTimeAsync(500); - expect(emittedActions).toBe(undefined); }); }); diff --git a/packages/@o3r/rules-engine/src/engine/helpers/filter-ruleset-event.operator.ts b/packages/@o3r/rules-engine/src/engine/helpers/filter-ruleset-event.operator.ts index 95015b6960..867d491652 100644 --- a/packages/@o3r/rules-engine/src/engine/helpers/filter-ruleset-event.operator.ts +++ b/packages/@o3r/rules-engine/src/engine/helpers/filter-ruleset-event.operator.ts @@ -1,4 +1,4 @@ -import { combineLatest, Observable } from 'rxjs'; +import { combineLatest, Observable, of } from 'rxjs'; import { map, shareReplay, switchMap } from 'rxjs/operators'; import { RulesetExecutor } from '../ruleset-executor'; @@ -14,11 +14,13 @@ export function filterRulesetsEventStream(restrictiveRuleSets?: string[]) { Object.values(rulesets).filter((ruleSet) => restrictiveRuleSets.indexOf(ruleSet.id) > -1) : Object.values(rulesets); - return combineLatest(activeRulesets.map((ruleset) => ruleset.rulesResultsSubject$)).pipe( - map((item) => item.reduce((acc, currentValue) => { - acc.push(...currentValue); - return acc; - }, []))); + return activeRulesets?.length > 0 + ? combineLatest(activeRulesets.map((ruleset) => ruleset.rulesResultsSubject$)).pipe( + map((item) => item.reduce((acc, currentValue) => { + acc.push(...currentValue); + return acc; + }, []))) + : of([]); }), shareReplay(1) ); diff --git a/packages/@o3r/rules-engine/src/services/runner/rules-engine.runner.service.spec.ts b/packages/@o3r/rules-engine/src/services/runner/rules-engine.runner.service.spec.ts index e336aa7108..9c5534507d 100644 --- a/packages/@o3r/rules-engine/src/services/runner/rules-engine.runner.service.spec.ts +++ b/packages/@o3r/rules-engine/src/services/runner/rules-engine.runner.service.spec.ts @@ -273,7 +273,7 @@ describe('Rules engine service', () => { next: value => nextFn(value) }); // should output no actions as all rulesets are on demand - expect(nextFn).not.toHaveBeenCalled(); + expect(nextFn).toHaveBeenCalledWith([]); sub.unsubscribe(); // activate ruleset 1 via his own linked component