@frontegg/entitlements-javascript-commons 1.1.5 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/rules/rule.evaluator.js +12 -4
- package/dist/rules/rule.evaluator.js.map +1 -1
- package/dist/rules/types.d.ts +9 -3
- package/dist/rules/types.js +6 -1
- package/dist/rules/types.js.map +1 -1
- package/docs/CHANGELOG.md +14 -0
- package/package.json +1 -1
- package/src/index.ts +1 -1
- package/src/rules/rule.evaluator.ts +23 -8
- package/src/rules/tests/rule.evaluator.spec.ts +254 -1
- package/src/rules/types.ts +12 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { FeatureFlagEvaluationResult, FeatureFlag, evaluateFeatureFlag } from './feature-flags';
|
|
2
2
|
export { PlanEvaluationResult, Plan, evaluatePlan } from './plans';
|
|
3
|
-
export { TreatmentEnum, Rule } from './rules';
|
|
3
|
+
export { TreatmentEnum, Rule, ConditionGroup, isConditionGroup } from './rules';
|
|
4
4
|
export { Condition } from './conditions';
|
|
5
5
|
export { OperationEnum, ConditionValue } from './operations/types';
|
|
6
6
|
export { evaluateIsEntitledToFeature, evaluateIsEntitledToPermissions, prepareAttributes, checkPermission, createPermissionCheckRegex, Permissions, JwtAttributes, CustomAttributes, FronteggAttributes, NotEntitledJustification, UserEntitlementsContext, EntitlementResult, Attributes, } from './user-entitlements';
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.NotEntitledJustification = exports.createPermissionCheckRegex = exports.checkPermission = exports.prepareAttributes = exports.evaluateIsEntitledToPermissions = exports.evaluateIsEntitledToFeature = exports.OperationEnum = exports.TreatmentEnum = exports.evaluatePlan = exports.evaluateFeatureFlag = void 0;
|
|
3
|
+
exports.NotEntitledJustification = exports.createPermissionCheckRegex = exports.checkPermission = exports.prepareAttributes = exports.evaluateIsEntitledToPermissions = exports.evaluateIsEntitledToFeature = exports.OperationEnum = exports.isConditionGroup = exports.TreatmentEnum = exports.evaluatePlan = exports.evaluateFeatureFlag = void 0;
|
|
4
4
|
var feature_flags_1 = require("./feature-flags");
|
|
5
5
|
Object.defineProperty(exports, "evaluateFeatureFlag", { enumerable: true, get: function () { return feature_flags_1.evaluateFeatureFlag; } });
|
|
6
6
|
var plans_1 = require("./plans");
|
|
7
7
|
Object.defineProperty(exports, "evaluatePlan", { enumerable: true, get: function () { return plans_1.evaluatePlan; } });
|
|
8
8
|
var rules_1 = require("./rules");
|
|
9
9
|
Object.defineProperty(exports, "TreatmentEnum", { enumerable: true, get: function () { return rules_1.TreatmentEnum; } });
|
|
10
|
+
Object.defineProperty(exports, "isConditionGroup", { enumerable: true, get: function () { return rules_1.isConditionGroup; } });
|
|
10
11
|
var types_1 = require("./operations/types");
|
|
11
12
|
Object.defineProperty(exports, "OperationEnum", { enumerable: true, get: function () { return types_1.OperationEnum; } });
|
|
12
13
|
var user_entitlements_1 = require("./user-entitlements");
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,iDAAgG;AAA7C,oHAAA,mBAAmB,OAAA;AACtE,iCAAmE;AAA9B,qGAAA,YAAY,OAAA;AACjD,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,iDAAgG;AAA7C,oHAAA,mBAAmB,OAAA;AACtE,iCAAmE;AAA9B,qGAAA,YAAY,OAAA;AACjD,iCAAgF;AAAvE,sGAAA,aAAa,OAAA;AAAwB,yGAAA,gBAAgB,OAAA;AAE9D,4CAAmE;AAA1D,sGAAA,aAAa,OAAA;AACtB,yDAc6B;AAb3B,gIAAA,2BAA2B,OAAA;AAC3B,oIAAA,+BAA+B,OAAA;AAC/B,sHAAA,iBAAiB,OAAA;AACjB,oHAAA,eAAe,OAAA;AACf,+HAAA,0BAA0B,OAAA;AAK1B,6HAAA,wBAAwB,OAAA"}
|
|
@@ -3,12 +3,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.createRuleEvaluator = void 0;
|
|
4
4
|
const types_1 = require("./types");
|
|
5
5
|
const conditions_1 = require("../conditions");
|
|
6
|
+
function evaluateGroup(group, attributes) {
|
|
7
|
+
const evaluateItem = (item) => {
|
|
8
|
+
if ((0, types_1.isConditionGroup)(item)) {
|
|
9
|
+
return evaluateGroup(item, attributes);
|
|
10
|
+
}
|
|
11
|
+
const evaluator = (0, conditions_1.createConditionEvaluator)({ condition: item });
|
|
12
|
+
return evaluator(attributes);
|
|
13
|
+
};
|
|
14
|
+
const arrayMethod = group.conditionLogic === types_1.ConditionLogicEnum.Or ? 'some' : 'every';
|
|
15
|
+
return group.conditions[arrayMethod](evaluateItem);
|
|
16
|
+
}
|
|
6
17
|
function createRuleEvaluator(payload) {
|
|
7
18
|
return (attributes) => {
|
|
8
|
-
const isRuleTreatable = payload.rule
|
|
9
|
-
const evaluator = (0, conditions_1.createConditionEvaluator)({ condition });
|
|
10
|
-
return evaluator(attributes);
|
|
11
|
-
});
|
|
19
|
+
const isRuleTreatable = evaluateGroup(payload.rule, attributes);
|
|
12
20
|
return isRuleTreatable ? types_1.RuleEvaluationResultEnum.Treatable : types_1.RuleEvaluationResultEnum.Insufficient;
|
|
13
21
|
};
|
|
14
22
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rule.evaluator.js","sourceRoot":"","sources":["../../src/rules/rule.evaluator.ts"],"names":[],"mappings":";;;AAAA,
|
|
1
|
+
{"version":3,"file":"rule.evaluator.js","sourceRoot":"","sources":["../../src/rules/rule.evaluator.ts"],"names":[],"mappings":";;;AAAA,mCAOiB;AACjB,8CAAoE;AAEpE,SAAS,aAAa,CAAC,KAAqB,EAAE,UAAmC;IAC/E,MAAM,YAAY,GAAG,CAAC,IAAgC,EAAW,EAAE;QACjE,IAAI,IAAA,wBAAgB,EAAC,IAAI,CAAC,EAAE;YAC1B,OAAO,aAAa,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;SACxC;QACD,MAAM,SAAS,GAAG,IAAA,qCAAwB,EAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAChE,OAAO,SAAS,CAAC,UAAU,CAAC,CAAC;IAC/B,CAAC,CAAC;IAEF,MAAM,WAAW,GAAG,KAAK,CAAC,cAAc,KAAK,0BAAkB,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;IACtF,OAAO,KAAK,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,YAAY,CAAC,CAAC;AACrD,CAAC;AAED,SAAgB,mBAAmB,CAAC,OAAmC;IACrE,OAAO,CAAC,UAAmC,EAA4B,EAAE;QACvE,MAAM,eAAe,GAAG,aAAa,CAAC,OAAO,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;QAChE,OAAO,eAAe,CAAC,CAAC,CAAC,gCAAwB,CAAC,SAAS,CAAC,CAAC,CAAC,gCAAwB,CAAC,YAAY,CAAC;IACtG,CAAC,CAAC;AACJ,CAAC;AALD,kDAKC"}
|
package/dist/rules/types.d.ts
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { Condition } from '../conditions';
|
|
2
|
+
export interface ConditionGroup {
|
|
3
|
+
conditionLogic: ConditionLogicEnum;
|
|
4
|
+
conditions: (Condition | ConditionGroup)[];
|
|
5
|
+
}
|
|
6
|
+
export declare function isConditionGroup(item: Condition | ConditionGroup): item is ConditionGroup;
|
|
2
7
|
export interface Rule {
|
|
3
|
-
conditionLogic: ConditionLogicEnum
|
|
4
|
-
conditions: Condition[];
|
|
8
|
+
conditionLogic: ConditionLogicEnum;
|
|
9
|
+
conditions: (Condition | ConditionGroup)[];
|
|
5
10
|
treatment: TreatmentEnum;
|
|
6
11
|
}
|
|
7
12
|
export declare enum ConditionLogicEnum {
|
|
8
|
-
And = "and"
|
|
13
|
+
And = "and",
|
|
14
|
+
Or = "or"
|
|
9
15
|
}
|
|
10
16
|
export declare enum TreatmentEnum {
|
|
11
17
|
True = "true",
|
package/dist/rules/types.js
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RuleEvaluationResultEnum = exports.TreatmentEnum = exports.ConditionLogicEnum = void 0;
|
|
3
|
+
exports.RuleEvaluationResultEnum = exports.TreatmentEnum = exports.ConditionLogicEnum = exports.isConditionGroup = void 0;
|
|
4
|
+
function isConditionGroup(item) {
|
|
5
|
+
return 'conditionLogic' in item && !('op' in item);
|
|
6
|
+
}
|
|
7
|
+
exports.isConditionGroup = isConditionGroup;
|
|
4
8
|
var ConditionLogicEnum;
|
|
5
9
|
(function (ConditionLogicEnum) {
|
|
6
10
|
ConditionLogicEnum["And"] = "and";
|
|
11
|
+
ConditionLogicEnum["Or"] = "or";
|
|
7
12
|
})(ConditionLogicEnum = exports.ConditionLogicEnum || (exports.ConditionLogicEnum = {}));
|
|
8
13
|
var TreatmentEnum;
|
|
9
14
|
(function (TreatmentEnum) {
|
package/dist/rules/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/rules/types.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/rules/types.ts"],"names":[],"mappings":";;;AAOA,SAAgB,gBAAgB,CAAC,IAAgC;IAC/D,OAAO,gBAAgB,IAAI,IAAI,IAAI,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC;AACrD,CAAC;AAFD,4CAEC;AAQD,IAAY,kBAGX;AAHD,WAAY,kBAAkB;IAC5B,iCAAW,CAAA;IACX,+BAAS,CAAA;AACX,CAAC,EAHW,kBAAkB,GAAlB,0BAAkB,KAAlB,0BAAkB,QAG7B;AAED,IAAY,aAGX;AAHD,WAAY,aAAa;IACvB,8BAAa,CAAA;IACb,gCAAe,CAAA;AACjB,CAAC,EAHW,aAAa,GAAb,qBAAa,KAAb,qBAAa,QAGxB;AAED,IAAY,wBAGX;AAHD,WAAY,wBAAwB;IAClC,mDAAuB,CAAA;IACvB,yDAA6B,CAAA;AAC/B,CAAC,EAHW,wBAAwB,GAAxB,gCAAwB,KAAxB,gCAAwB,QAGnC"}
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [1.2.0](https://github.com/frontegg/entitlements-javascript-commons/compare/v-1.1.5...v-1.2.0) (2026-02-22)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* **or:** support or ([4ec8cc2](https://github.com/frontegg/entitlements-javascript-commons/commit/4ec8cc2e7669528c8418560c173e8dc9c237ff5b))
|
|
7
|
+
|
|
8
|
+
# [1.2.0-alpha.1](https://github.com/frontegg/entitlements-javascript-commons/compare/v-1.1.5...v-1.2.0-alpha.1) (2026-02-19)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **or:** support or ([4ec8cc2](https://github.com/frontegg/entitlements-javascript-commons/commit/4ec8cc2e7669528c8418560c173e8dc9c237ff5b))
|
|
14
|
+
|
|
1
15
|
## [1.1.5](https://github.com/frontegg/entitlements-javascript-commons/compare/v-1.1.4...v-1.1.5) (2026-02-15)
|
|
2
16
|
|
|
3
17
|
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { FeatureFlagEvaluationResult, FeatureFlag, evaluateFeatureFlag } from './feature-flags';
|
|
2
2
|
export { PlanEvaluationResult, Plan, evaluatePlan } from './plans';
|
|
3
|
-
export { TreatmentEnum, Rule } from './rules';
|
|
3
|
+
export { TreatmentEnum, Rule, ConditionGroup, isConditionGroup } from './rules';
|
|
4
4
|
export { Condition } from './conditions';
|
|
5
5
|
export { OperationEnum, ConditionValue } from './operations/types';
|
|
6
6
|
export {
|
|
@@ -1,14 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
ConditionGroup,
|
|
3
|
+
ConditionLogicEnum,
|
|
4
|
+
CreateRuleEvaluatorPayload,
|
|
5
|
+
isConditionGroup,
|
|
6
|
+
RuleEvaluationResultEnum,
|
|
7
|
+
RuleEvaluator,
|
|
8
|
+
} from './types';
|
|
9
|
+
import { Condition, createConditionEvaluator } from '../conditions';
|
|
3
10
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
11
|
+
function evaluateGroup(group: ConditionGroup, attributes: Record<string, unknown>): boolean {
|
|
12
|
+
const evaluateItem = (item: Condition | ConditionGroup): boolean => {
|
|
13
|
+
if (isConditionGroup(item)) {
|
|
14
|
+
return evaluateGroup(item, attributes);
|
|
15
|
+
}
|
|
16
|
+
const evaluator = createConditionEvaluator({ condition: item });
|
|
17
|
+
return evaluator(attributes);
|
|
18
|
+
};
|
|
8
19
|
|
|
9
|
-
|
|
10
|
-
|
|
20
|
+
const arrayMethod = group.conditionLogic === ConditionLogicEnum.Or ? 'some' : 'every';
|
|
21
|
+
return group.conditions[arrayMethod](evaluateItem);
|
|
22
|
+
}
|
|
11
23
|
|
|
24
|
+
export function createRuleEvaluator(payload: CreateRuleEvaluatorPayload): RuleEvaluator {
|
|
25
|
+
return (attributes: Record<string, unknown>): RuleEvaluationResultEnum => {
|
|
26
|
+
const isRuleTreatable = evaluateGroup(payload.rule, attributes);
|
|
12
27
|
return isRuleTreatable ? RuleEvaluationResultEnum.Treatable : RuleEvaluationResultEnum.Insufficient;
|
|
13
28
|
};
|
|
14
29
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ConditionLogicEnum, Rule, RuleEvaluationResultEnum, TreatmentEnum } from '../types';
|
|
1
|
+
import { ConditionGroup, ConditionLogicEnum, Rule, RuleEvaluationResultEnum, TreatmentEnum } from '../types';
|
|
2
2
|
import { createRuleEvaluator } from '../rule.evaluator';
|
|
3
3
|
import { OperationEnum } from '../../operations/types';
|
|
4
4
|
|
|
@@ -132,4 +132,257 @@ describe('RuleEvaluator', () => {
|
|
|
132
132
|
|
|
133
133
|
expect(result).toEqual(RuleEvaluationResultEnum.Treatable);
|
|
134
134
|
});
|
|
135
|
+
|
|
136
|
+
it('should return RuleEvaluationResultEnum.Treatable with "or" logic when at least one condition passes', () => {
|
|
137
|
+
const rule: Rule = {
|
|
138
|
+
treatment: TreatmentEnum.True,
|
|
139
|
+
conditions: [
|
|
140
|
+
{
|
|
141
|
+
attribute: 'attribute',
|
|
142
|
+
op: OperationEnum.StartsWith,
|
|
143
|
+
value: { list: ['no-match'] },
|
|
144
|
+
negate: false,
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
attribute: 'attribute',
|
|
148
|
+
op: OperationEnum.Contains,
|
|
149
|
+
value: { list: ['test'] },
|
|
150
|
+
negate: false,
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
conditionLogic: ConditionLogicEnum.Or,
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const ruleEvaluator = createRuleEvaluator({ rule });
|
|
157
|
+
const result = ruleEvaluator({ attribute: 'test' });
|
|
158
|
+
|
|
159
|
+
expect(result).toEqual(RuleEvaluationResultEnum.Treatable);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('should return RuleEvaluationResultEnum.Insufficient with "or" logic when no conditions pass', () => {
|
|
163
|
+
const rule: Rule = {
|
|
164
|
+
treatment: TreatmentEnum.True,
|
|
165
|
+
conditions: [
|
|
166
|
+
{
|
|
167
|
+
attribute: 'attribute',
|
|
168
|
+
op: OperationEnum.StartsWith,
|
|
169
|
+
value: { list: ['no-match'] },
|
|
170
|
+
negate: false,
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
attribute: 'attribute',
|
|
174
|
+
op: OperationEnum.Contains,
|
|
175
|
+
value: { list: ['no-match'] },
|
|
176
|
+
negate: false,
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
conditionLogic: ConditionLogicEnum.Or,
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const ruleEvaluator = createRuleEvaluator({ rule });
|
|
183
|
+
const result = ruleEvaluator({ attribute: 'test' });
|
|
184
|
+
|
|
185
|
+
expect(result).toEqual(RuleEvaluationResultEnum.Insufficient);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('should return RuleEvaluationResultEnum.Treatable with "or" logic when all conditions pass', () => {
|
|
189
|
+
const rule: Rule = {
|
|
190
|
+
treatment: TreatmentEnum.True,
|
|
191
|
+
conditions: [
|
|
192
|
+
{
|
|
193
|
+
attribute: 'attribute',
|
|
194
|
+
op: OperationEnum.InList,
|
|
195
|
+
value: { list: ['test'] },
|
|
196
|
+
negate: false,
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
attribute: 'attribute',
|
|
200
|
+
op: OperationEnum.Contains,
|
|
201
|
+
value: { list: ['test'] },
|
|
202
|
+
negate: false,
|
|
203
|
+
},
|
|
204
|
+
],
|
|
205
|
+
conditionLogic: ConditionLogicEnum.Or,
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const ruleEvaluator = createRuleEvaluator({ rule });
|
|
209
|
+
const result = ruleEvaluator({ attribute: 'test' });
|
|
210
|
+
|
|
211
|
+
expect(result).toEqual(RuleEvaluationResultEnum.Treatable);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
describe('nested condition groups', () => {
|
|
215
|
+
it('should return Treatable for x | (y & z) when x passes but y & z do not', () => {
|
|
216
|
+
const rule: Rule = {
|
|
217
|
+
treatment: TreatmentEnum.True,
|
|
218
|
+
conditionLogic: ConditionLogicEnum.Or,
|
|
219
|
+
conditions: [
|
|
220
|
+
{
|
|
221
|
+
attribute: 'x',
|
|
222
|
+
op: OperationEnum.Equal,
|
|
223
|
+
value: { number: 1 },
|
|
224
|
+
negate: false,
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
conditionLogic: ConditionLogicEnum.And,
|
|
228
|
+
conditions: [
|
|
229
|
+
{
|
|
230
|
+
attribute: 'y',
|
|
231
|
+
op: OperationEnum.Equal,
|
|
232
|
+
value: { number: 1 },
|
|
233
|
+
negate: false,
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
attribute: 'z',
|
|
237
|
+
op: OperationEnum.Equal,
|
|
238
|
+
value: { number: 1 },
|
|
239
|
+
negate: false,
|
|
240
|
+
},
|
|
241
|
+
],
|
|
242
|
+
} as ConditionGroup,
|
|
243
|
+
],
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const ruleEvaluator = createRuleEvaluator({ rule });
|
|
247
|
+
const result = ruleEvaluator({ x: 1, y: 0, z: 0 });
|
|
248
|
+
|
|
249
|
+
expect(result).toEqual(RuleEvaluationResultEnum.Treatable);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('should return Insufficient for x | (y & z) when none pass', () => {
|
|
253
|
+
const rule: Rule = {
|
|
254
|
+
treatment: TreatmentEnum.True,
|
|
255
|
+
conditionLogic: ConditionLogicEnum.Or,
|
|
256
|
+
conditions: [
|
|
257
|
+
{
|
|
258
|
+
attribute: 'x',
|
|
259
|
+
op: OperationEnum.Equal,
|
|
260
|
+
value: { number: 1 },
|
|
261
|
+
negate: false,
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
conditionLogic: ConditionLogicEnum.And,
|
|
265
|
+
conditions: [
|
|
266
|
+
{
|
|
267
|
+
attribute: 'y',
|
|
268
|
+
op: OperationEnum.Equal,
|
|
269
|
+
value: { number: 1 },
|
|
270
|
+
negate: false,
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
attribute: 'z',
|
|
274
|
+
op: OperationEnum.Equal,
|
|
275
|
+
value: { number: 1 },
|
|
276
|
+
negate: false,
|
|
277
|
+
},
|
|
278
|
+
],
|
|
279
|
+
} as ConditionGroup,
|
|
280
|
+
],
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
const ruleEvaluator = createRuleEvaluator({ rule });
|
|
284
|
+
const result = ruleEvaluator({ x: 0, y: 0, z: 0 });
|
|
285
|
+
|
|
286
|
+
expect(result).toEqual(RuleEvaluationResultEnum.Insufficient);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it('should return Treatable for (a & b) | (c & d) when second group passes', () => {
|
|
290
|
+
const rule: Rule = {
|
|
291
|
+
treatment: TreatmentEnum.True,
|
|
292
|
+
conditionLogic: ConditionLogicEnum.Or,
|
|
293
|
+
conditions: [
|
|
294
|
+
{
|
|
295
|
+
conditionLogic: ConditionLogicEnum.And,
|
|
296
|
+
conditions: [
|
|
297
|
+
{
|
|
298
|
+
attribute: 'a',
|
|
299
|
+
op: OperationEnum.Equal,
|
|
300
|
+
value: { number: 1 },
|
|
301
|
+
negate: false,
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
attribute: 'b',
|
|
305
|
+
op: OperationEnum.Equal,
|
|
306
|
+
value: { number: 1 },
|
|
307
|
+
negate: false,
|
|
308
|
+
},
|
|
309
|
+
],
|
|
310
|
+
} as ConditionGroup,
|
|
311
|
+
{
|
|
312
|
+
conditionLogic: ConditionLogicEnum.And,
|
|
313
|
+
conditions: [
|
|
314
|
+
{
|
|
315
|
+
attribute: 'c',
|
|
316
|
+
op: OperationEnum.Equal,
|
|
317
|
+
value: { number: 1 },
|
|
318
|
+
negate: false,
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
attribute: 'd',
|
|
322
|
+
op: OperationEnum.Equal,
|
|
323
|
+
value: { number: 1 },
|
|
324
|
+
negate: false,
|
|
325
|
+
},
|
|
326
|
+
],
|
|
327
|
+
} as ConditionGroup,
|
|
328
|
+
],
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
const ruleEvaluator = createRuleEvaluator({ rule });
|
|
332
|
+
const result = ruleEvaluator({ a: 0, b: 0, c: 1, d: 1 });
|
|
333
|
+
|
|
334
|
+
expect(result).toEqual(RuleEvaluationResultEnum.Treatable);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
it('should handle deeply nested groups', () => {
|
|
338
|
+
const rule: Rule = {
|
|
339
|
+
treatment: TreatmentEnum.True,
|
|
340
|
+
conditionLogic: ConditionLogicEnum.And,
|
|
341
|
+
conditions: [
|
|
342
|
+
{
|
|
343
|
+
attribute: 'a',
|
|
344
|
+
op: OperationEnum.Equal,
|
|
345
|
+
value: { number: 1 },
|
|
346
|
+
negate: false,
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
conditionLogic: ConditionLogicEnum.Or,
|
|
350
|
+
conditions: [
|
|
351
|
+
{
|
|
352
|
+
attribute: 'b',
|
|
353
|
+
op: OperationEnum.Equal,
|
|
354
|
+
value: { number: 1 },
|
|
355
|
+
negate: false,
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
conditionLogic: ConditionLogicEnum.And,
|
|
359
|
+
conditions: [
|
|
360
|
+
{
|
|
361
|
+
attribute: 'c',
|
|
362
|
+
op: OperationEnum.Equal,
|
|
363
|
+
value: { number: 1 },
|
|
364
|
+
negate: false,
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
attribute: 'd',
|
|
368
|
+
op: OperationEnum.Equal,
|
|
369
|
+
value: { number: 1 },
|
|
370
|
+
negate: false,
|
|
371
|
+
},
|
|
372
|
+
],
|
|
373
|
+
} as ConditionGroup,
|
|
374
|
+
],
|
|
375
|
+
} as ConditionGroup,
|
|
376
|
+
],
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
const ruleEvaluator = createRuleEvaluator({ rule });
|
|
380
|
+
|
|
381
|
+
// a=1 AND (b=0 OR (c=1 AND d=1)) -> 1 AND (0 OR 1) -> 1 AND 1 -> Treatable
|
|
382
|
+
expect(ruleEvaluator({ a: 1, b: 0, c: 1, d: 1 })).toEqual(RuleEvaluationResultEnum.Treatable);
|
|
383
|
+
|
|
384
|
+
// a=1 AND (b=0 OR (c=1 AND d=0)) -> 1 AND (0 OR 0) -> 1 AND 0 -> Insufficient
|
|
385
|
+
expect(ruleEvaluator({ a: 1, b: 0, c: 1, d: 0 })).toEqual(RuleEvaluationResultEnum.Insufficient);
|
|
386
|
+
});
|
|
387
|
+
});
|
|
135
388
|
});
|
package/src/rules/types.ts
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
import { Condition } from '../conditions';
|
|
2
2
|
|
|
3
|
+
export interface ConditionGroup {
|
|
4
|
+
conditionLogic: ConditionLogicEnum;
|
|
5
|
+
conditions: (Condition | ConditionGroup)[];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function isConditionGroup(item: Condition | ConditionGroup): item is ConditionGroup {
|
|
9
|
+
return 'conditionLogic' in item && !('op' in item);
|
|
10
|
+
}
|
|
11
|
+
|
|
3
12
|
export interface Rule {
|
|
4
|
-
conditionLogic: ConditionLogicEnum
|
|
5
|
-
conditions: Condition[];
|
|
13
|
+
conditionLogic: ConditionLogicEnum;
|
|
14
|
+
conditions: (Condition | ConditionGroup)[];
|
|
6
15
|
treatment: TreatmentEnum;
|
|
7
16
|
}
|
|
8
17
|
|
|
9
18
|
export enum ConditionLogicEnum {
|
|
10
19
|
And = 'and',
|
|
20
|
+
Or = 'or',
|
|
11
21
|
}
|
|
12
22
|
|
|
13
23
|
export enum TreatmentEnum {
|