@o3r/rules-engine
Version:
This module provides a rule engine that can be executed on your Otter application to customize your application (translations, placeholders and configs) based on a json file generated by your CMS.
1 lines • 226 kB
Source Map (JSON)
{"version":3,"file":"o3r-rules-engine.mjs","sources":["../../src/components/rules-engine/shared/fallback-to.pipe.ts","../../src/components/rules-engine/shared/json-or-string.pipe.ts","../../src/components/rules-engine/shared/ruleset-history.helper.ts","../../src/components/rules-engine/facts-snapshot/facts-snapshot.component.ts","../../src/components/rules-engine/facts-snapshot/facts-snapshot.template.html","../../src/components/rules-engine/rule-key-value/rule-key-value-pres.component.ts","../../src/components/rules-engine/rule-key-value/rule-key-value-pres.template.html","../../src/components/rules-engine/rule-actions/rule-actions-pres.component.ts","../../src/components/rules-engine/rule-actions/rule-actions-pres.template.html","../../src/components/rules-engine/rule-condition/rule-condition-pres.component.ts","../../src/components/rules-engine/rule-condition/rule-condition-pres.template.html","../../src/components/rules-engine/rule-tree/rule-tree-pres.component.ts","../../src/components/rules-engine/rule-tree/rule-tree-pres.template.html","../../src/components/rules-engine/ruleset-history/ruleset-history-pres.component.ts","../../src/components/rules-engine/ruleset-history/ruleset-history-pres.template.html","../../src/components/rules-engine/ruleset-history/ruleset-history-pres.module.ts","../../src/devkit/rules-engine-devkit.interface.ts","../../src/services/rules-engine.token.ts","../../src/stores/rulesets/rulesets.actions.ts","../../src/stores/rulesets/rulesets.effect.ts","../../src/stores/rulesets/rulesets.reducer.ts","../../src/stores/rulesets/rulesets.state.ts","../../src/stores/rulesets/rulesets.module.ts","../../src/stores/rulesets/rulesets.selectors.ts","../../src/stores/rulesets/rulesets.sync.ts","../../src/engine/debug/helpers.ts","../../src/engine/debug/engine.debug.ts","../../src/engine/helpers/filter-ruleset-event.operator.ts","../../src/engine/operator/operator.helpers.ts","../../src/engine/operator/operators/array-based.operators.ts","../../src/engine/operator/operators/basic.operators.ts","../../src/engine/operator/operators/date-based.operators.ts","../../src/engine/operator/operators/number-based.operators.ts","../../src/engine/operator/operators/index.ts","../../src/engine/rule/rule.helpers.ts","../../src/engine/ruleset-executor.ts","../../src/engine/engine.ts","../../src/services/runner/rules-engine.runner.service.ts","../../src/services/runner/rules-engine.runner.module.ts","../../src/devkit/rules-engine-devtools.token.ts","../../src/devkit/rules-engine-devtools.service.ts","../../src/devkit/rules-engine-devtools.console.service.ts","../../src/devkit/rules-engine-devtools.message.service.ts","../../src/devkit/rules-engine-devtools.module.ts","../../src/fact/fact.abstract-service.ts","../../src/inner-facts/current-time/current-time-fact.service.ts","../../src/o3r-rules-engine.ts"],"sourcesContent":["import {\n Pipe,\n PipeTransform,\n} from '@angular/core';\n\n@Pipe({ name: 'o3rFallbackTo' })\nexport class O3rFallbackToPipe implements PipeTransform {\n public transform<T>(value: T, fallback = 'undefined'): T | string {\n return value === undefined ? fallback : value;\n }\n}\n","import {\n Pipe,\n PipeTransform,\n} from '@angular/core';\n\n@Pipe({ name: 'o3rJsonOrString' })\nexport class O3rJsonOrStringPipe implements PipeTransform {\n /**\n * @inheritDoc\n */\n public transform(value: any): string {\n if (typeof value === 'string') {\n return value;\n }\n return JSON.stringify(value, null, 2);\n }\n}\n","import type {\n DebugEvent,\n Ruleset,\n RulesetExecutionErrorEvent,\n RulesetExecutionEvent,\n} from '../../../engine';\nimport type {\n RulesetExecutionDebug,\n RulesetExecutionStatus,\n} from '../ruleset-history/ruleset-history-pres.component';\n\n/**\n * Compute the status of the execution depending on its execution event type, the output and whether the execution\n * is still active\n * @param rulesetExecution\n * @param isActive\n */\nexport const getStatus = (rulesetExecution: RulesetExecutionErrorEvent | RulesetExecutionEvent, isActive: boolean): RulesetExecutionStatus => {\n if (rulesetExecution.type === 'RulesetExecutionError') {\n return 'Error';\n } else if (rulesetExecution.outputActions?.length === 0) {\n return 'NoEffect';\n } else if (isActive) {\n return 'Active';\n }\n return 'Deactivated';\n};\n\n/**\n * Transform the output of the debug reports into the model for the ruleset history debug panel\n * @param events\n * @param rulesetMap\n */\nexport const rulesetReportToHistory = (events: DebugEvent[], rulesetMap: Record<string, Ruleset>): RulesetExecutionDebug[] => {\n const availableRulesets = (events.filter((e) => e.type === 'AvailableRulesets').reverse()[0])?.availableRulesets || [];\n const lastActiveRulesets = (events.filter((e) => e.type === 'ActiveRulesets').reverse()[0])?.rulesets || [];\n\n return availableRulesets\n .filter((ruleset) => !!ruleset)\n .reduce<(RulesetExecutionEvent | RulesetExecutionErrorEvent)[]>((acc, ruleset) => {\n const rulesetExecutions = events\n .filter((e): e is RulesetExecutionEvent | RulesetExecutionErrorEvent => ((e.type === 'RulesetExecutionError' || e.type === 'RulesetExecution') && e.rulesetId === ruleset.id));\n if (rulesetExecutions) {\n acc.push(...rulesetExecutions);\n }\n return acc;\n }, [])\n .sort((execA, execB) => execB.timestamp - execA.timestamp)\n .map((rulesetExecution) => {\n const rulesetInformation = rulesetMap[rulesetExecution.rulesetId];\n const isActive = lastActiveRulesets.find((r) => r.id === rulesetExecution.rulesetId);\n return {\n ...rulesetExecution,\n status: getStatus(rulesetExecution, !!isActive),\n isActive: !!isActive,\n rulesetInformation,\n rulesEvaluations: (rulesetExecution.rulesEvaluations || []).sort((evalA, evalB) =>\n (rulesetInformation?.rules.findIndex((r) => r.id === evalA.rule.id) || -1)\n - (rulesetInformation?.rules.findIndex((r) => r.id === evalB.rule.id) || -1)\n )\n };\n });\n};\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n input,\n signal,\n ViewEncapsulation,\n} from '@angular/core';\nimport {\n FormsModule,\n ReactiveFormsModule,\n} from '@angular/forms';\nimport type {\n Facts,\n} from '../../../engine';\nimport {\n O3rJsonOrStringPipe,\n} from '../shared/index';\n\n@Component({\n selector: 'o3r-facts-snapshot',\n styleUrls: ['./facts-snapshot.style.scss'],\n templateUrl: './facts-snapshot.template.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [\n O3rJsonOrStringPipe,\n FormsModule,\n ReactiveFormsModule\n ],\n encapsulation: ViewEncapsulation.None\n})\nexport class FactsSnapshotComponent {\n /**\n * Full list of available facts with their current value\n */\n public readonly facts = input.required<{ factName: string; value: Facts }[]>();\n\n /**\n * Search terms\n */\n public readonly search = signal('');\n\n /**\n * Filtered list of facts using search terms\n */\n public readonly filteredFacts = computed(() => {\n const search = this.search();\n const facts = this.facts();\n if (search) {\n const matchString = new RegExp(search.replace(/[\\s#$()*+,.?[\\\\\\]^{|}-]/g, '\\\\$&'), 'i');\n return facts.filter(({ factName, value }) =>\n matchString.test(factName)\n || (typeof value === 'object'\n ? matchString.test(JSON.stringify(value))\n : matchString.test(String(value)))\n );\n } else {\n return facts;\n }\n });\n}\n","<section>\n @if (facts().length < 1) {\n <div class=\"m-2\">\n No facts are registered.\n </div>\n } @else {\n <div class=\"input-group\">\n <label class=\"input-group-text\" for=\"search-fact\">\n <span class=\"mx-1 fa-search\" aria-label=\"Search\"></span>\n </label>\n <input class=\"form-control\" [(ngModel)]=\"search\" type=\"text\" id=\"search-fact\" placeholder=\"Search for fact name or value\" />\n </div>\n <div class=\"mt-3\">\n List of <b>{{facts().length}}</b> registered facts\n @if (facts().length > filteredFacts().length) { (<b>{{filteredFacts().length}}</b> matching the search) }\n </div>\n <ul>\n @for (fact of filteredFacts(); track fact.factName) {\n <li>\n <span class=\"fact-name\">{{fact.factName}}: </span>\n <span class=\"fact-value\">{{fact.value | o3rJsonOrString}}</span>\n </li>\n }\n </ul>\n }\n</section>\n","import {\n CommonModule,\n JsonPipe,\n} from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n Input,\n OnChanges,\n SimpleChanges,\n ViewEncapsulation,\n} from '@angular/core';\nimport {\n of,\n Subject,\n} from 'rxjs';\nimport {\n delay,\n startWith,\n switchMap,\n} from 'rxjs/operators';\n\n/**\n * Duration of the notification for clipboard feature (in milliseconds)\n */\nconst NOTIFICATION_DURATION = 1750;\n\n/**\n * Minimal length required to enable clipboard feature\n */\nconst CLIPBOARD_FEATURE_LENGTH_THRESHOLD = 80;\n\n@Component({\n selector: 'o3r-rule-key-value-pres',\n styleUrls: ['./rule-key-value-pres.style.scss'],\n templateUrl: './rule-key-value-pres.template.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [CommonModule, JsonPipe]\n})\nexport class RuleKeyValuePresComponent implements OnChanges {\n /**\n * Key of the object (name of the fact for example)\n */\n @Input()\n public key?: string;\n\n /**\n * Current value of the object\n */\n @Input()\n public value!: string;\n\n /**\n * Previous value of the object\n */\n @Input()\n public oldValue?: string;\n\n /**\n * Type of display:\n * - 'state': `key: value`, `key: oldValue -> value` or `oldValue -> value`\n * - 'assignment': `key = value`\n */\n @Input()\n public type: 'state' | 'assignment' = 'state';\n\n public shouldLimitCharactersForValue = true;\n public isClipBoardFeatureAvailableForValue = false;\n public isValuePrimitiveType = false;\n\n public shouldLimitCharactersForOldValue = true;\n public isClipBoardFeatureAvailableForOldValue = false;\n public isOldValuePrimitiveType = false;\n\n private readonly triggerNotification = new Subject<void>();\n public showNotification$ = this.triggerNotification.asObservable().pipe(\n switchMap(() => of(false).pipe(delay(NOTIFICATION_DURATION), startWith(true)))\n );\n\n private isClipBoardFeatureAvailable(value: string | undefined) {\n return !!(navigator.clipboard && value && value.length > CLIPBOARD_FEATURE_LENGTH_THRESHOLD);\n }\n\n public ngOnChanges({ value, oldValue }: SimpleChanges) {\n if (value) {\n this.isValuePrimitiveType = value.currentValue === null || typeof value.currentValue !== 'object';\n this.isClipBoardFeatureAvailableForValue = this.isClipBoardFeatureAvailable(this.isValuePrimitiveType ? String(value.currentValue) : JSON.stringify(value.currentValue));\n }\n if (oldValue) {\n this.isOldValuePrimitiveType = oldValue.currentValue === null || typeof oldValue.currentValue !== 'object';\n this.isClipBoardFeatureAvailableForOldValue = this.isClipBoardFeatureAvailable(this.isOldValuePrimitiveType ? String(oldValue.currentValue) : JSON.stringify(oldValue.currentValue));\n }\n }\n\n public async copyToClipBoard(content: string) {\n await navigator.clipboard.writeText(content);\n this.triggerNotification.next();\n }\n}\n","@if (key) {\n <span class=\"input-key\">{{key}}{{type === 'state' ? ':' : ''}}</span>\n}\n{{type === 'assignment' ? '=' : ''}}\n@if (oldValue) {\n <pre class=\"input-value\"\n [class.limit-characters]=\"shouldLimitCharactersForOldValue\"\n (click)=\"shouldLimitCharactersForOldValue = !shouldLimitCharactersForOldValue\"\n (keyup.enter)=\"shouldLimitCharactersForOldValue = !shouldLimitCharactersForOldValue\"\n tabindex=\"0\"\n >\n {{isOldValuePrimitiveType ? oldValue : (oldValue | json)}}\n </pre>\n @if (isClipBoardFeatureAvailableForOldValue) {\n <button (click)=\"copyToClipBoard(oldValue)\" title=\"Copy to clipboard\">📋</button>\n }\n →\n}\n<pre class=\"input-value\"\n [class.limit-characters]=\"shouldLimitCharactersForValue\"\n (click)=\"shouldLimitCharactersForValue = !shouldLimitCharactersForValue\"\n (keyup.enter)=\"shouldLimitCharactersForValue = !shouldLimitCharactersForValue\"\n tabindex=\"0\"\n>\n {{isValuePrimitiveType ? value : (value | json)}}\n</pre>\n@if (isClipBoardFeatureAvailableForValue) {\n <button (click)=\"copyToClipBoard(value)\" title=\"Copy to clipboard\">📋</button>\n}\n@if (showNotification$ | async) {\n <div role=\"alert\" class=\"notification\">Copied to clipboard</div>\n}\n","import {\n CommonModule,\n JsonPipe,\n} from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n Input,\n ViewEncapsulation,\n} from '@angular/core';\nimport type {\n ActionBlock,\n AllBlock,\n Facts,\n} from '../../../engine';\nimport {\n RuleKeyValuePresComponent,\n} from '../rule-key-value/rule-key-value-pres.component';\nimport {\n O3rFallbackToPipe,\n} from '../shared/index';\n\n@Component({\n selector: 'o3r-rule-actions-pres',\n styleUrls: ['./rule-actions-pres.style.scss'],\n templateUrl: './rule-actions-pres.template.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [CommonModule, JsonPipe, RuleKeyValuePresComponent, O3rFallbackToPipe]\n})\nexport class RuleActionsPresComponent {\n /**\n * List of all the output actions of a rules or ruleset execution\n */\n @Input()\n public actions: AllBlock[] = [];\n\n /**\n * The list of temporary facts used and/or modified within the rule or the ruleset.\n * They are scoped to the ruleset and their value is the one after the rule or ruleset execution.\n */\n @Input()\n public temporaryFacts: Record<string, Facts> = {};\n\n /**\n * List of temporary facts that will be modified by the ruleset or the rule.\n */\n @Input()\n public runtimeOutputs: string[] = [];\n\n /**\n * Check if a given block is of type ActionBlock\n * @param block\n */\n public isActionBlock(block: AllBlock): block is ActionBlock {\n return !!(block as ActionBlock).actionType;\n }\n}\n","<div class=\"ruleset-panel-category-title\">Output Actions</div>\n@if (actions?.length === 0 && runtimeOutputs.length === 0) {\n <div class=\"ruleset-panel-category-body empty\">\n No action\n </div>\n} @else {\n <ul class=\"ruleset-panel-category-body\">\n @for (action of actions; track $index) {\n <li>\n @if (isActionBlock(action)) {\n @switch (action.actionType) {\n @case ('SET_FACT') {\n <div>\n <div>Set Fact</div>\n <div>\n <o3r-rule-key-value-pres\n [key]=\"action.fact | o3rFallbackTo: 'Missing \\'fact\\''\"\n [value]=\"action.value | o3rFallbackTo\"\n [type]=\"'assignment'\"></o3r-rule-key-value-pres>\n </div>\n </div>\n }\n @case ('UPDATE_CONFIG') {\n <div>\n <div>Update Config {{action.component}} {{action.library}}</div>\n <div>\n <o3r-rule-key-value-pres\n [key]=\"action.property | o3rFallbackTo: 'Missing \\'property\\''\"\n [value]=\"action.value | o3rFallbackTo\"\n [type]=\"'assignment'\"></o3r-rule-key-value-pres>\n </div>\n </div>\n }\n @case ('UPDATE_ASSET') {\n <div>\n <div>Update Asset:</div>\n <div>\n <o3r-rule-key-value-pres\n [oldValue]=\"action.asset | o3rFallbackTo: 'Missing \\'asset\\''\"\n [value]=\"action.value | o3rFallbackTo\"\n [type]=\"'state'\"></o3r-rule-key-value-pres>\n </div>\n </div>\n }\n @case ('UPDATE_LOCALISATION') {\n <div>\n <div>Update localization:</div>\n <div>\n <o3r-rule-key-value-pres\n [oldValue]=\"action.key | o3rFallbackTo: 'Missing \\'key\\''\"\n [value]=\"action.value | o3rFallbackTo\"\n [type]=\"'state'\"></o3r-rule-key-value-pres>\n </div>\n </div>\n }\n @case ('UPDATE_PLACEHOLDER') {\n <div [class.error]=\"!action.placeholderId\">\n <div>Update placeholder in {{action.component}} {{action.library}}</div>\n <div>\n <o3r-rule-key-value-pres\n [oldValue]=\"action.placeholderId | o3rFallbackTo: 'Missing \\'placeholderId\\''\"\n [value]=\"action.value\"\n [type]=\"'state'\"></o3r-rule-key-value-pres>\n </div>\n </div>\n }\n @default {\n <div class=\"error\">\n <div>Unrecognized action</div>\n <div>{{action | json}}</div>\n </div>\n }\n }\n }\n </li>\n }\n @for (runtimeOutput of runtimeOutputs; track $index) {\n <li>\n <div>Set temporary fact</div>\n <div>\n <o3r-rule-key-value-pres\n [key]=\"runtimeOutput | o3rFallbackTo: 'Missing \\'fact\\''\"\n [value]=\"temporaryFacts[runtimeOutput] | o3rFallbackTo\"\n [type]=\"'assignment'\"></o3r-rule-key-value-pres>\n </div>\n </li>\n }\n </ul>\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n Input,\n ViewEncapsulation,\n} from '@angular/core';\nimport type {\n BinaryOperation,\n GenericOperand,\n OperandFact,\n TopLevelCondition,\n UnaryOperation,\n} from '../../../engine';\n\n@Component({\n selector: 'o3r-rule-condition-pres',\n styleUrls: ['./rule-condition-pres.style.scss'],\n templateUrl: './rule-condition-pres.template.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: []\n})\nexport class RuleConditionPresComponent {\n private _condition?: TopLevelCondition;\n\n /**\n * Left hand operator as it will be displayed in the template.\n * In the case of a fact with a json path, will resolve the whole fact path, else will only display the value\n */\n public lhs = 'undefined';\n\n /**\n * Right hand operator as it will be displayed in the template.\n * In the case of a fact with a json path, will resolve the whole fact path, else will only display the value\n */\n public rhs: string | undefined;\n\n /**\n * Rule condition that will be flattened by the component setter\n */\n @Input()\n public set condition(condition: TopLevelCondition | undefined) {\n this._condition = condition;\n this.lhs = (condition as UnaryOperation | BinaryOperation)?.lhs ? this.getOperandName((condition as UnaryOperation | BinaryOperation).lhs) : 'undefined';\n this.rhs = (condition as BinaryOperation)?.rhs ? this.getOperandName((condition as BinaryOperation).rhs) : undefined;\n }\n\n public get condition(): TopLevelCondition | undefined {\n return this._condition;\n }\n\n private getOperandName(operand: GenericOperand): string {\n const value = `${operand.value ?? 'MISSING_VALUE'}`;\n return (operand as OperandFact).path ? (operand as OperandFact).path!.replace(/^\\$/, value) : value;\n }\n}\n","@if (!condition) {\n <span class=\"input-value\">true</span>\n} @else {\n @if (!$any(condition).all && !$any(condition).any && !$any(condition).not) {\n <span class=\"input-key\">{{ lhs }}</span> {{ $any(condition).operator }}\n @if (rhs !== undefined) {\n <span class=\"input-value\">{{ rhs }}</span>\n }\n }\n @if ($any(condition).all || $any(condition).any || $any(condition).not) {\n @if ($any(condition).all) {\n <span>ALL</span>\n }\n @if ($any(condition).any) {\n <span>ANY</span>\n }\n @if ($any(condition).not) {\n <span>NOT</span>\n }\n <span>(\n @for (cond of $any(condition).all || $any(condition).any || [$any(condition).not]; track cond; let last = $last) {\n <o3r-rule-condition-pres [condition]=\"cond\"></o3r-rule-condition-pres>\n @if (!last) {\n <span>, </span>\n }\n }\n )</span>\n }\n}\n","import {\n CommonModule,\n} from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n Input,\n ViewEncapsulation,\n} from '@angular/core';\nimport type {\n AllBlock,\n TopLevelCondition,\n} from '../../../engine';\nimport {\n RuleActionsPresComponent,\n} from '../rule-actions/rule-actions-pres.component';\nimport {\n RuleConditionPresComponent,\n} from '../rule-condition/rule-condition-pres.component';\n\n@Component({\n selector: 'o3r-rule-tree-pres',\n styleUrls: ['./rule-tree-pres.style.scss'],\n templateUrl: './rule-tree-pres.template.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [CommonModule, RuleActionsPresComponent, RuleConditionPresComponent]\n})\nexport class RuleTreePresComponent {\n /**\n * Rule name. Will only be defined at the root of the rule tree.\n */\n @Input()\n public name?: string;\n\n /**\n * Type of the block being resolved.\n * A type \"IF_ELSE\" will display two branches and the success and failure outputs associated\n * Else, only the successElements will be shown\n */\n @Input()\n public blockType = '';\n\n /**\n * The condition under which the success elements will be displayed.\n */\n @Input()\n public condition?: TopLevelCondition;\n\n /**\n * If case output\n */\n @Input()\n public successElements: AllBlock[] = [];\n\n /**\n * Else case output\n */\n @Input()\n public failureElements: AllBlock[] = [];\n\n /**\n * Should the \"Else case scenario\" actions be displayed\n */\n public failureActionsExpanded = false;\n\n /**\n * Should the \"If case scenario\" actions be displayed\n */\n public successActionsExpanded = false;\n}\n","@if (name) {\n <span>{{name | titlecase}}:</span>\n}\n<div class=\"rule-wrapper tree\">\n @if (blockType === 'IF_ELSE') {\n @if (!name) {\n <div class=\"tree-root\" [attr.aria-hidden]=\"true\">\n <div></div>\n <div></div>\n </div>\n }\n <div class=\"rule-conditions\">\n <div class=\"rule-conditions-title\">If\n <o3r-rule-condition-pres [condition]=\"condition\"></o3r-rule-condition-pres>\n </div>\n <div class=\"tree-root\" [attr.aria-hidden]=\"true\">\n <div></div>\n <div></div>\n </div>\n </div>\n <div class=\"rule-actions-wrapper tree-node\">\n <div class=\"rule-actions tree-branch\">\n <div class=\"tree-leaf\" [attr.aria-hidden]=\"true\">\n <div></div>\n <div></div>\n </div>\n <div class=\"rule-action-title success-actions\"\n tabindex=\"0\"\n (click)=\"successActionsExpanded = !successActionsExpanded\"\n (keyup.enter)=\"successActionsExpanded = !successActionsExpanded\">\n <i class=\"icon refx-icon-validate\"></i>\n <span>Then</span>\n <i class=\"icon\"\n [class.icon-caret-down]=\"!successActionsExpanded\"\n [class.icon-caret-up]=\"successActionsExpanded\">\n </i>\n </div>\n @if (successActionsExpanded) {\n <o3r-rule-actions-pres class=\"rule-tree-actions\"\n [actions]=\"successElements\">\n </o3r-rule-actions-pres>\n }\n <ng-container [ngTemplateOutlet]=\"subTree\" [ngTemplateOutletContext]=\"{blocks: successElements}\"></ng-container>\n </div>\n <div class=\"rule-actions tree-branch\">\n <div class=\"tree-leaf\" [attr.aria-hidden]=\"true\">\n <div></div>\n <div></div>\n </div>\n <div class=\"rule-action-title error-actions\"\n tabindex=\"0\"\n (click)=\"failureActionsExpanded = !failureActionsExpanded\"\n (keyup.enter)=\"failureActionsExpanded = !failureActionsExpanded\">\n <i class=\"icon refx-icon-cross\"></i>\n <span>Else</span>\n <i class=\"icon\"\n [class.icon-caret-down]=\"!failureActionsExpanded\"\n [class.icon-caret-up]=\"failureActionsExpanded\">\n </i>\n </div>\n @if (failureActionsExpanded) {\n <o3r-rule-actions-pres class=\"rule-tree-actions\"\n [actions]=\"failureElements\">\n </o3r-rule-actions-pres>\n }\n <ng-container [ngTemplateOutlet]=\"subTree\" [ngTemplateOutletContext]=\"{blocks: failureElements}\"></ng-container>\n </div>\n </div>\n } @else {\n <div class=\"rule-conditions\">\n <div class=\"rule-conditions-title\">If <span class=\"input-value\">true</span></div>\n <div class=\"tree-root\" [attr.aria-hidden]=\"true\">\n <div></div>\n <div></div>\n </div>\n </div>\n <div class=\"rule-actions-wrapper\">\n <div class=\"rule-actions\">\n <div class=\"rule-action-title success-actions\" tabindex=\"0\"\n (keyup.enter)=\"successActionsExpanded = !successActionsExpanded\"\n (click)=\"successActionsExpanded = !successActionsExpanded\">\n <i class=\"icon refx-icon-validate\">\n </i>\n <span>Then</span>\n <i class=\"icon\"\n [class.icon-caret-down]=\"!successActionsExpanded\"\n [class.icon-caret-up]=\"successActionsExpanded\">\n </i>\n </div>\n @if (successActionsExpanded) {\n <o3r-rule-actions-pres class=\"rule-tree-actions\"\n [actions]=\"successElements\">\n </o3r-rule-actions-pres>\n }\n <ng-container [ngTemplateOutlet]=\"subTree\" [ngTemplateOutletContext]=\"{blocks: successElements}\"></ng-container>\n </div>\n </div>\n }\n</div>\n\n<ng-template #subTree let-blocks=\"blocks\">\n <div class=\"rule-sub-trees\">\n @for (block of blocks; track block) {\n @if (block.blockType === 'IF_ELSE') {\n <div class=\"tree-branch\">\n <o3r-rule-tree-pres\n [blockType]=\"'IF_ELSE'\"\n [condition]=\"block.condition\"\n [failureElements]=\"block.failureElements\"\n [successElements]=\"block.successElements\"></o3r-rule-tree-pres>\n </div>\n }\n }\n </div>\n</ng-template>\n","import {\n CommonModule,\n} from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n inject,\n Input,\n ViewEncapsulation,\n} from '@angular/core';\nimport type {\n Ruleset,\n RulesetExecutionErrorEvent,\n RulesetExecutionEvent,\n} from '../../../engine';\nimport {\n RuleActionsPresComponent,\n} from '../rule-actions/rule-actions-pres.component';\nimport {\n RuleKeyValuePresComponent,\n} from '../rule-key-value/rule-key-value-pres.component';\nimport {\n RuleTreePresComponent,\n} from '../rule-tree/rule-tree-pres.component';\nimport {\n O3rFallbackToPipe,\n O3rJsonOrStringPipe,\n} from '../shared/index';\n\nexport type RulesetExecutionStatus = 'Error' | 'Active' | 'Deactivated' | 'NoEffect';\n/**\n * Model of a RulesetExecution with more information for debug purpose\n */\nexport type RulesetExecutionDebug = (RulesetExecutionEvent | RulesetExecutionErrorEvent) & {\n isActive: boolean;\n status: RulesetExecutionStatus;\n rulesetInformation: Ruleset | undefined;\n};\n\n@Component({\n selector: 'o3r-ruleset-history-pres',\n styleUrls: ['./ruleset-history-pres.style.scss'],\n templateUrl: './ruleset-history-pres.template.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n CommonModule,\n O3rFallbackToPipe,\n O3rJsonOrStringPipe,\n RuleActionsPresComponent,\n RuleKeyValuePresComponent,\n RuleTreePresComponent\n ]\n})\nexport class RulesetHistoryPresComponent {\n private readonly cd = inject(ChangeDetectorRef);\n\n /**\n * Reflects the state of each ruleset expanded elements.\n * Each ruleset entry contains a list of subpanel that can be collapsed or expanded.\n * Ruleset whole panel status is store the 'ruleset' entry.\n * @example\n * Expanded ruleset with rule overview collapsed:\n * {'rulesetId': {'ruleset' : true, 'ruleOverview': false}}\n * @note Collapsing a ruleset will not reset the subpanel expansion status\n */\n public expansionStatus: { [key: string]: { [subpanel: string]: boolean } } = {};\n\n @Input()\n public rulesetExecutions: RulesetExecutionDebug[] = [];\n\n @Input()\n public executionDurationFormat = '1.3-3';\n\n /**\n * Toggle a ruleset subpanel\n * @param ruleId\n * @param subpanel element to collapse. 'ruleset' will toggle the whole panel but won't reset the subpanels states.\n */\n public toggleExpansion(ruleId: string, subpanel: string) {\n if (this.expansionStatus[ruleId]) {\n this.expansionStatus[ruleId][subpanel] = !this.expansionStatus[ruleId][subpanel];\n } else {\n this.expansionStatus[ruleId] = { [subpanel]: true };\n }\n this.cd.detectChanges();\n }\n}\n","<section>\n <ng-template #noRulesEngine>\n <div class=\"alert alert-danger m-2\" role=\"alert\">\n The Rules Engine is not configured on this page.\n </div>\n </ng-template>\n <ul *ngIf=\"rulesetExecutions; else noRulesEngine\" class=\"rulesets\">\n <li *ngFor=\"let execution of rulesetExecutions\" class=\"ruleset\">\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- need to refactor the div to accordion from DF #1518 -->\n <div class=\"ruleset-panel-title ruleset-expansion-action\"\n [class.error]=\"execution.type === 'RulesetExecutionError'\"\n (click)=\"toggleExpansion(execution.executionId, 'ruleset')\">\n <div><span [title]=\"'This ruleset has been evaluated ' + execution.executionCounter + ' time(s)'\">{{execution.executionCounter}}</span> - {{execution.rulesetName | titlecase }}\n <div class=\"ruleset-panel-subtitle\" *ngIf=\"execution.rulesetInformation?.linkedComponents?.or\">\n <ng-container *ngFor=\"let lc of execution.rulesetInformation?.linkedComponents?.or; last as isLast\">\n <div>{{lc.name}} {{lc.library}} <span *ngIf=\"!isLast\"> OR </span></div>\n </ng-container>\n </div>\n <div class=\"ruleset-panel-subtitle\" *ngIf=\"execution.rulesetInformation?.validityRange as validityRange\">\n Date range: {{validityRange.from}} - {{validityRange.to}}\n </div>\n </div>\n <div class=\"ruleset-panel-title-aside\">\n <span class=\"error capsule\" *ngIf=\"execution.status === 'Error'\">Error</span>\n <span class=\"success capsule\" *ngIf=\"execution.status === 'Active'\">Applied</span>\n <span class=\"inactive capsule\" *ngIf=\"execution.status === 'Deactivated'\">Deactivated</span>\n <span class=\"warn capsule\" *ngIf=\"execution.status === 'NoEffect'\">No effect</span>\n <span class=\"time capsule\">\n <span>{{execution.timestamp | date: 'HH:mm:ss SSS'}}</span>\n <span>({{execution.duration | number: executionDurationFormat}}ms)</span>\n </span>\n <button\n class=\"icon\"\n [class.icon-caret-down]=\"!expansionStatus[execution.executionId]?.ruleset\"\n [class.icon-caret-up]=\"expansionStatus[execution.executionId]?.ruleset\">\n </button>\n </div>\n </div>\n <div class=\"ruleset-panel-description\" *ngIf=\"expansionStatus[execution.executionId]?.ruleset\">\n <ng-container [ngTemplateOutlet]=\"rules\"\n [ngTemplateOutletContext]=\"{\n rules: execution.rulesetInformation.rules,\n expansionID: execution.executionId\n }\"></ng-container>\n <ng-container [ngTemplateOutlet]=\"inputs\"\n [ngTemplateOutletContext]=\"{\n inputs: execution.inputFacts\n }\"></ng-container>\n <ng-container *ngIf=\"execution.type === 'RulesetExecutionError'; else success\">\n <div class=\"ruleset-panel-category-title\">Rules:</div>\n <ul class=\"ruleset-panel-category-body rule-description\">\n <li *ngFor=\"let ruleEvaluation of execution.rulesEvaluations; let index=index;\">\n <ng-container>\n <div class=\"ruleset-panel-title\" [class.error]=\"ruleEvaluation.error\">\n <span>{{ruleEvaluation.rule.name | titlecase}} </span>\n <span class=\"capsule error\" *ngIf=\"ruleEvaluation.error\">Error</span>\n </div>\n <div>\n <ng-container *ngIf=\"ruleEvaluation.error\">\n <span class=\"ruleset-panel-category-title\">Error:</span>\n <pre class=\"ruleset-panel-category-body error\">{{ruleEvaluation.error | o3rJsonOrString}}</pre>\n </ng-container>\n <ng-container [ngTemplateOutlet]=\"inputs\"\n [ngTemplateOutletContext]=\"{\n inputs: execution.inputFacts,\n runtimeInputs: execution.rulesetInformation?.rules[index]?.inputRuntimeFacts\n }\"></ng-container>\n <o3r-rule-actions-pres *ngIf=\"!ruleEvaluation.error\"\n [temporaryFacts]=\"ruleEvaluation.temporaryFacts\"\n [runtimeOutputs]=\"execution.rulesetInformation?.rules[index]?.outputRuntimeFacts\"\n ></o3r-rule-actions-pres>\n </div>\n </ng-container>\n </li>\n </ul>\n </ng-container>\n <ng-template #success>\n <o3r-rule-actions-pres [actions]=\"execution.outputActions\"></o3r-rule-actions-pres>\n <div class=\"ruleset-panel-category-title\">Executed Rules</div>\n <ul class=\"rule-description ruleset-panel-category-body\">\n <li *ngFor=\"let ruleEvaluation of execution.rulesEvaluations; let index=index;\">\n <div class=\"ruleset-panel-title\">\n <span>{{ruleEvaluation.rule.name | titlecase}}</span>\n <span class=\"capsule inactive\" *ngIf=\"ruleEvaluation.cached\">Cached</span>\n <span class=\"capsule\">({{ruleEvaluation.duration | number: executionDurationFormat}}ms)</span>\n </div>\n <div>\n <ng-container [ngTemplateOutlet]=\"triggers\"\n [ngTemplateOutletContext]=\"{triggers: (ruleEvaluation.triggers[ruleEvaluation.rule.id])}\"></ng-container>\n <o3r-rule-actions-pres\n [actions]=\"ruleEvaluation.outputActions\"\n [temporaryFacts]=\"ruleEvaluation.temporaryFacts\"\n [runtimeOutputs]=\"execution.rulesetInformation?.rules[index]?.outputRuntimeFacts\">\n </o3r-rule-actions-pres>\n </div>\n </li>\n </ul>\n </ng-template>\n </div>\n </li>\n </ul>\n</section>\n\n<ng-template let-triggers=\"triggers\" #triggers>\n <div class=\"ruleset-panel-category-title\">Basefacts Triggers</div>\n <ul class=\"ruleset-panel-category-body triggers\">\n <ng-container *ngFor=\"let trigger of (triggers | keyvalue)\">\n <li *ngIf=\"trigger.value?.factName\">\n <o3r-rule-key-value-pres\n [key]=\"trigger.value.factName\"\n [oldValue]=\"trigger.value.oldValue | o3rFallbackTo\"\n [value]=\"trigger.value.newValue | o3rFallbackTo\"\n [type]=\"'state'\"></o3r-rule-key-value-pres>\n </li>\n </ng-container>\n </ul>\n</ng-template>\n\n<ng-template let-rules=\"rules\" let-expansionID=\"expansionID\" #rules>\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- need to refactor the div to accordion from DF #1518 -->\n <div class=\"ruleset-panel-category-title ruleset-expansion-action\"\n (click)=\"toggleExpansion(expansionID, 'rulesOverview')\">\n <span>Rules Overview</span>\n <button class=\"icon\"\n [class.icon-caret-down]=\"!expansionStatus[expansionID]?.rulesOverview\"\n [class.icon-caret-up]=\"expansionStatus[expansionID]?.rulesOverview\">\n </button>\n </div>\n <ng-container *ngIf=\"expansionStatus[expansionID]?.rulesOverview\">\n <div *ngIf=\"rules?.length === 0\" class=\"ruleset-panel-category-body empty\">No rule</div>\n <ul class=\"ruleset-panel-category-body\" *ngIf=\"rules?.length > 0\">\n <li *ngFor=\"let rule of rules\">\n <o3r-rule-tree-pres [name]=\"rule.name\"\n [condition]=\"rule?.rootElement?.condition\"\n [blockType]=\"rule?.rootElement?.blockType\"\n [successElements]=\"rule?.rootElement?.successElements\"\n [failureElements]=\"rule?.rootElement?.failureElements\">\n </o3r-rule-tree-pres>\n </li>\n </ul>\n </ng-container>\n</ng-template>\n\n<ng-template let-inputs=\"inputs\" let-runtimeInputs=\"runtimeInputs\" #inputs>\n <div class=\"ruleset-panel-category-title\">Inputs snapshot</div>\n <div *ngIf=\"inputs?.length === 0\" class=\"ruleset-panel-category-body empty\">No inputs</div>\n <ul class=\"ruleset-panel-category-body\" *ngIf=\"inputs?.length > 0\">\n <li *ngFor=\"let input of inputs\">\n <o3r-rule-key-value-pres\n [key]=\"input.factName\"\n [value]=\"input.value | o3rFallbackTo\"\n [type]=\"'state'\"></o3r-rule-key-value-pres>\n </li>\n <li *ngFor=\"let input of runtimeInputs\">{{input}} (scope limited to ruleset)</li>\n </ul>\n</ng-template>\n","import {\n JsonPipe,\n} from '@angular/common';\nimport {\n NgModule,\n} from '@angular/core';\nimport {\n RuleConditionPresComponent,\n} from '../rule-condition/rule-condition-pres.component';\nimport {\n RulesetHistoryPresComponent,\n} from './ruleset-history-pres.component';\n\n/**\n * @deprecated The Components and Pipes are now standalone, this module will be removed in v14\n */\n@NgModule({\n imports: [\n JsonPipe,\n RulesetHistoryPresComponent,\n RuleConditionPresComponent\n ],\n exports: [RulesetHistoryPresComponent]\n})\nexport class RulesetHistoryPresModule {}\n","import type {\n ConnectContentMessage,\n DevtoolsCommonOptions,\n MessageDataTypes,\n OtterMessageContent,\n RequestMessagesContentMessage,\n} from '@o3r/core';\nimport type {\n DebugEvent,\n Ruleset,\n} from '../engine';\n\nexport interface RulesEngineDevtoolsServiceOptions extends DevtoolsCommonOptions {\n /** Size of events list emitted by rules engine; When undefined all history will be kept */\n rulesEngineStackLimit?: number;\n}\n\n/** Rules Engine debug event Message Content */\nexport interface RulesEngineDebugEventsContentMessage extends OtterMessageContent<'rulesEngineEvents'> {\n /** Map of registered rulesets */\n rulesetMap: Record<string, Ruleset>;\n /** List of event from the Rules Engine Debugger */\n events: DebugEvent[];\n}\n\ntype RulesEngineMessageContents = RulesEngineDebugEventsContentMessage;\n\n/** List of possible DataTypes for RulesEngine messages */\nexport type RulesEngineMessageDataTypes = MessageDataTypes<RulesEngineMessageContents>;\n\n/** List of all messages for configuration purpose */\nexport type AvailableRulesEngineMessageContents = RulesEngineMessageContents\n | ConnectContentMessage\n | RequestMessagesContentMessage<RulesEngineMessageDataTypes>;\n\nexport const isRulesEngineMessage = (message: any): message is AvailableRulesEngineMessageContents => {\n return message && (\n message.dataType === 'rulesEngineEvents'\n || message.dataType === 'requestMessages'\n || message.dataType === 'connect'\n );\n};\n","import {\n InjectionToken,\n} from '@angular/core';\n\n/** Determine if the action should be executed */\nexport const RULES_ENGINE_OPTIONS = new InjectionToken<boolean>('Rules Engine Options');\n\n/** Rules engine configuration */\nexport interface RulesEngineServiceOptions {\n /** Determine if the actions resulting of the rule engine should be executed */\n dryRun: boolean;\n /** Flag to activate the run of Rules Engine in debug mode */\n debug: boolean;\n /** Limit the number of debug events kept in stack */\n debugEventsStackLimit?: number;\n}\n\n/** Default Rules engine options */\nexport const DEFAULT_RULES_ENGINE_OPTIONS: Readonly<RulesEngineServiceOptions> = {\n dryRun: false,\n debug: false\n} as const;\n","import {\n createAction,\n props,\n} from '@ngrx/store';\nimport {\n asyncProps,\n AsyncRequest,\n FailAsyncStoreItemEntitiesActionPayload,\n FromApiActionPayload,\n SetActionPayload,\n SetAsyncStoreItemEntitiesActionPayload,\n UpdateActionPayload,\n} from '@o3r/core';\nimport {\n RulesetsModel,\n RulesetsStateDetails,\n} from './rulesets.state';\n\n/** StateDetailsActions */\nconst ACTION_SET = '[Rulesets] set';\nconst ACTION_UPDATE = '[Rulesets] update';\nconst ACTION_RESET = '[Rulesets] reset';\nconst ACTION_CANCEL_REQUEST = '[Rulesets] cancel request';\n\n/** Entity Actions */\nconst ACTION_CLEAR_ENTITIES = '[Rulesets] clear entities';\nconst ACTION_UPSERT_ENTITIES = '[Rulesets] upsert entities';\nconst ACTION_SET_ENTITIES = '[Rulesets] set entities';\nconst ACTION_FAIL_ENTITIES = '[Rulesets] fail entities';\n\n/** Async Actions */\nconst ACTION_SET_ENTITIES_FROM_API = '[Rulesets] set entities from api';\nconst ACTION_UPSERT_ENTITIES_FROM_API = '[Rulesets] upsert entities from api';\n\n/** Action to clear the StateDetails of the store and replace it */\nexport const setRulesets = createAction(ACTION_SET, props<SetActionPayload<RulesetsStateDetails>>());\n\n/** Action to change a part or the whole object in the store. */\nexport const updateRulesets = createAction(ACTION_UPDATE, props<UpdateActionPayload<RulesetsStateDetails>>());\n\n/** Action to reset the whole state, by returning it to initial state. */\nexport const resetRulesets = createAction(ACTION_RESET);\n\n/** Action to cancel a Request ID registered in the store. Can happen from effect based on a switchMap for instance */\nexport const cancelRulesetsRequest = createAction(ACTION_CANCEL_REQUEST, props<AsyncRequest>());\n\n/** Action to clear all rulesets and fill the store with the payload */\nexport const setRulesetsEntities = createAction(ACTION_SET_ENTITIES, props<SetAsyncStoreItemEntitiesActionPayload<RulesetsModel>>());\n\n/** Action to update rulesets with known IDs, insert the new ones */\nexport const upsertRulesetsEntities = createAction(ACTION_UPSERT_ENTITIES, props<SetAsyncStoreItemEntitiesActionPayload<RulesetsModel>>());\n\n/** Action to empty the list of entities, keeping the global state */\nexport const clearRulesetsEntities = createAction(ACTION_CLEAR_ENTITIES);\n\n/** Action to update failureStatus for every RulesetsModel */\nexport const failRulesetsEntities = createAction(ACTION_FAIL_ENTITIES, props<FailAsyncStoreItemEntitiesActionPayload<any>>());\n\n/**\n * Action to put the global status of the store in a pending state. Call SET action with the list of RulesetsModels received, when this action resolves.\n * If the call fails, dispatch FAIL_ENTITIES action\n */\nexport const setRulesetsEntitiesFromApi = createAction(ACTION_SET_ENTITIES_FROM_API, asyncProps<FromApiActionPayload<RulesetsModel[]>>());\n\n/**\n * Action to put global status of the store in a pending state. Call UPSERT action with the list of RulesetsModels received, when this action resolves.\n * If the call fails, dispatch FAIL_ENTITIES action\n */\nexport const upsertRulesetsEntitiesFromApi = createAction(ACTION_UPSERT_ENTITIES_FROM_API, asyncProps<FromApiActionPayload<RulesetsModel[]>>());\n","import {\n inject,\n Injectable,\n} from '@angular/core';\nimport {\n Actions,\n createEffect,\n ofType,\n} from '@ngrx/effects';\nimport {\n fromApiEffectSwitchMap,\n} from '@o3r/core';\nimport {\n from,\n of,\n} from 'rxjs';\nimport {\n catchError,\n map,\n mergeMap,\n} from 'rxjs/operators';\nimport {\n cancelRulesetsRequest,\n failRulesetsEntities,\n setRulesetsEntities,\n setRulesetsEntitiesFromApi,\n upsertRulesetsEntities,\n upsertRulesetsEntitiesFromApi,\n} from './rulesets.actions';\n\n/**\n * Service to handle async Rulesets actions\n */\n@Injectable()\nexport class RulesetsEffect {\n protected actions$ = inject(Actions);\n\n /**\n * Set the entities with the reply content, dispatch failRulesetsEntities if it catches a failure\n */\n public setEntitiesFromApi$ = createEffect(() =>\n this.actions$.pipe(\n ofType(setRulesetsEntitiesFromApi),\n fromApiEffectSwitchMap(\n (reply, action) => setRulesetsEntities({ entities: reply, requestId: action.requestId }),\n (error, action) => of(failRulesetsEntities({ error, requestId: action.requestId })),\n cancelRulesetsRequest\n )\n )\n );\n\n /**\n * Upsert the entities with the reply content, dispatch failRulesetsEntities if it catches a failure\n */\n public upsertEntitiesFromApi$ = createEffect(() =>\n this.actions$.pipe(\n ofType(upsertRulesetsEntitiesFromApi),\n mergeMap((payload) =>\n from(payload.call).pipe(\n map((reply) => upsertRulesetsEntities({ entities: reply, requestId: payload.requestId })),\n catchError((err) => of(failRulesetsEntities({ error: err, requestId: payload.requestId })))\n )\n )\n )\n );\n}\n","import {\n createEntityAdapter,\n} from '@ngrx/entity';\nimport {\n ActionCreator,\n createReducer,\n on,\n ReducerTypes,\n} from '@ngrx/store';\nimport {\n asyncStoreItemAdapter,\n} from '@o3r/core';\nimport * as actions from './rulesets.actions';\nimport {\n RulesetsModel,\n RulesetsState,\n} from './rulesets.state';\n\n/**\n * Rulesets Store adapter\n */\nexport const rulesetsAdapter = createEntityAdapter<RulesetsModel>({\n selectId: (model) => model.id\n});\n\n/**\n * Rulesets Store initial value\n */\nexport const rulesetsInitialState = rulesetsAdapter.getInitialState<RulesetsState>({\n requestIds: []\n});\n\n/**\n * List of basic actions for Rulesets Store\n */\nexport const rulesetsReducerFeatures: ReducerTypes<RulesetsState, ActionCreator[]>[] = [\n on(actions.resetRulesets, () => rulesetsInitialState),\n\n on(actions.setRulesets, (state, payload) => ({ ids: state.ids, entities: state.entities, ...payload.stateDetails })),\n\n on(actions.cancelRulesetsRequest, (state, action) => asyncStoreItemAdapter.resolveRequest(state, action.requestId)),\n\n on(actions.updateRulesets, (state, payload) => ({ ...state, ...payload.stateDetails })),\n\n on(actions.setRulesetsEntities, (state, payload) =>\n rulesetsAdapter.addMany(\n payload.entities,\n rulesetsAdapter.removeAll(asyncStoreItemAdapter.resolveRequest(state, payload.requestId)))\n ),\n\n on(actions.upsertRulesetsEntities, (state, payload) =>\n rulesetsAdapter.upsertMany(payload.entities, asyncStoreItemAdapter.resolveRequest(state, payload.requestId))\n ),\n\n on(actions.clearRulesetsEntities, (state) => rulesetsAdapter.removeAll(state)),\n\n on(actions.failRulesetsEntities, (state, payload) =>\n asyncStoreItemAdapter.failRequest(state, payload.requestId)\n ),\n\n on(actions.setRulesetsEntitiesFromApi, actions.upsertRulesetsEntitiesFromApi, (state, payload) =>\n asyncStoreItemAdapter.addRequest(state, payload.requestId))\n];\n\n/**\n * Rulesets Store reducer\n */\nexport const rulesetsReducer = createReducer(\n rulesetsInitialState,\n ...rulesetsReducerFeatures\n);\n","import {\n EntityState,\n} from '@ngrx/entity';\nimport {\n AsyncStoreItem,\n} from '@o3r/core';\nimport type {\n Ruleset,\n} from '../../engine';\n\n/**\n * Rulesets model\n */\nexport interface RulesetsModel extends Ruleset {\n}\n\n/**\n * Rulesets state details\n */\nexport interface RulesetsStateDetails extends AsyncStoreItem {\n}\n\n/**\n * Rulesets store state\n */\nexport interface RulesetsState extends EntityState<RulesetsModel>, RulesetsStateDetails {\n}\n\n/**\n * Name of the Rulesets Store\n */\nexport const RULESETS_STORE_NAME = 'rulesets';\n\n/**\n * Rulesets Store Interface\n */\nexport interface RulesetsStore {\n /** Rulesets state */\n [RULESETS_STORE_NAME]: RulesetsState;\n}\n","import {\n InjectionToken,\n ModuleWithProviders,\n NgModule,\n} from '@angular/core';\nimport {\n EffectsModule,\n} from '@ngrx/effects';\nimport {\n Action,\n ActionReducer,\n StoreModule,\n} from '@ngrx/store';\nimport {\n RulesetsEffect,\n} from './rulesets.effect';\nimport {\n rulesetsReducer,\n} from './rulesets.reducer';\nimport {\n RULESETS_STORE_NAME,\n RulesetsState,\n} from './rulesets.state';\n\n/** Token of the Rulesets reducer */\nexport const RULESETS_REDUCER_TOKEN = new InjectionToken<ActionReducer<RulesetsState, Action>>('Feature Rulesets Reducer');\n\n/** Provide default reducer for Rulesets store */\nexport function getDefaultRulesetsReducer() {\n return rulesetsReducer;\n}\n\n@NgModule({\n imports: [\n StoreModule.forFeature(RULESETS_STORE_NAME, RULESETS_REDUCER_TOKEN), EffectsModule.forFeature([RulesetsEffect])\n ],\n providers: [\n { provide: RULESETS_REDUCER_TOKEN, useFactory: getDefaultRulesetsReducer }\n ]\n})\nexport class RulesetsStoreModule {\n public static forRoot<T extends RulesetsState>(reducerFactor