@frontegg/entitlements-javascript-commons 1.0.0-alpha.1 → 1.0.0-alpha.3
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/.github/workflows/publish.yaml +1 -1
- package/.releaserc.yaml +2 -2
- package/dist/conditions/condition.evaluator.d.ts +6 -0
- package/dist/conditions/condition.evaluator.js +15 -0
- package/dist/conditions/condition.evaluator.js.map +1 -0
- package/dist/conditions/index.d.ts +2 -0
- package/dist/conditions/index.js +19 -0
- package/dist/conditions/index.js.map +1 -0
- package/dist/conditions/types.d.ts +7 -0
- package/dist/conditions/types.js +3 -0
- package/dist/conditions/types.js.map +1 -0
- package/dist/feature-flags/feature-flag.evaluator.d.ts +2 -0
- package/dist/feature-flags/feature-flag.evaluator.js +24 -0
- package/dist/feature-flags/feature-flag.evaluator.js.map +1 -0
- package/dist/feature-flags/index.d.ts +1 -0
- package/dist/feature-flags/index.js +18 -0
- package/dist/feature-flags/index.js.map +1 -0
- package/dist/feature-flags/types.d.ts +10 -0
- package/dist/feature-flags/types.js +3 -0
- package/dist/feature-flags/types.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -4
- package/dist/index.js.map +1 -1
- package/dist/operations/boolean/index.d.ts +3 -0
- package/dist/operations/boolean/index.js +24 -0
- package/dist/operations/boolean/index.js.map +1 -0
- package/dist/operations/boolean/operations.d.ts +3 -0
- package/dist/operations/boolean/operations.js +8 -0
- package/dist/operations/boolean/operations.js.map +1 -0
- package/dist/operations/boolean/types.d.ts +3 -0
- package/dist/operations/boolean/types.js +3 -0
- package/dist/operations/boolean/types.js.map +1 -0
- package/dist/operations/components/operation.resolver.d.ts +2 -0
- package/dist/operations/components/operation.resolver.js +19 -0
- package/dist/operations/components/operation.resolver.js.map +1 -0
- package/dist/operations/date/index.d.ts +4 -0
- package/dist/operations/date/index.js +28 -0
- package/dist/operations/date/index.js.map +1 -0
- package/dist/operations/date/operations.d.ts +6 -0
- package/dist/operations/date/operations.js +22 -0
- package/dist/operations/date/operations.js.map +1 -0
- package/dist/operations/date/types.d.ts +8 -0
- package/dist/operations/date/types.js +3 -0
- package/dist/operations/date/types.js.map +1 -0
- package/dist/operations/numeric/index.d.ts +4 -0
- package/dist/operations/numeric/index.js +30 -0
- package/dist/operations/numeric/index.js.map +1 -0
- package/dist/operations/numeric/operations.d.ts +8 -0
- package/dist/operations/numeric/operations.js +28 -0
- package/dist/operations/numeric/operations.js.map +1 -0
- package/dist/operations/numeric/types.d.ts +8 -0
- package/dist/operations/numeric/types.js +3 -0
- package/dist/operations/numeric/types.js.map +1 -0
- package/dist/operations/string/index.d.ts +4 -0
- package/dist/operations/string/index.js +29 -0
- package/dist/operations/string/index.js.map +1 -0
- package/dist/operations/string/operations.d.ts +7 -0
- package/dist/operations/string/operations.js +33 -0
- package/dist/operations/string/operations.js.map +1 -0
- package/dist/operations/string/types.d.ts +7 -0
- package/dist/operations/string/types.js +3 -0
- package/dist/operations/string/types.js.map +1 -0
- package/dist/operations/types/index.d.ts +15 -0
- package/dist/operations/types/index.js +18 -0
- package/dist/operations/types/index.js.map +1 -0
- package/dist/operations/types/operation.enum.d.ts +18 -0
- package/dist/operations/types/operation.enum.js +27 -0
- package/dist/operations/types/operation.enum.js.map +1 -0
- package/dist/rules/index.d.ts +2 -0
- package/dist/rules/index.js +19 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/rule.evaluator.d.ts +2 -0
- package/dist/rules/rule.evaluator.js +16 -0
- package/dist/rules/rule.evaluator.js.map +1 -0
- package/dist/rules/types.d.ts +21 -0
- package/dist/rules/types.js +18 -0
- package/dist/rules/types.js.map +1 -0
- package/docs/CHANGELOG.md +17 -0
- package/jest.config.js +26 -0
- package/package.json +2 -2
- package/src/conditions/condition.evaluator.ts +20 -0
- package/src/conditions/index.ts +2 -0
- package/src/conditions/tests/condition.evaluator.spec.ts +52 -0
- package/src/conditions/types.ts +8 -0
- package/src/feature-flags/feature-flag.evaluator.ts +27 -0
- package/src/feature-flags/index.ts +1 -0
- package/src/feature-flags/tests/feature-flag.evaluator.spec.ts +176 -0
- package/src/feature-flags/types.ts +12 -0
- package/src/index.ts +1 -3
- package/src/operations/boolean/index.ts +9 -0
- package/src/operations/boolean/operations.spec.ts +13 -0
- package/src/operations/boolean/operations.ts +6 -0
- package/src/operations/boolean/types.ts +3 -0
- package/src/operations/components/operation.resolver.ts +18 -0
- package/src/operations/components/tests/operation.resolver.spec.ts +8 -0
- package/src/operations/date/index.ts +18 -0
- package/src/operations/date/operations.spec.ts +45 -0
- package/src/operations/date/operations.ts +20 -0
- package/src/operations/date/types.ts +10 -0
- package/src/operations/numeric/index.ts +22 -0
- package/src/operations/numeric/operations.spec.ts +63 -0
- package/src/operations/numeric/operations.ts +26 -0
- package/src/operations/numeric/tests/operations.spec.ts +64 -0
- package/src/operations/numeric/types.ts +10 -0
- package/src/operations/string/index.ts +20 -0
- package/src/operations/string/operations.spec.ts +38 -0
- package/src/operations/string/operations.ts +32 -0
- package/src/operations/string/tests/operations.spec.ts +38 -0
- package/src/operations/string/types.ts +9 -0
- package/src/operations/types/index.ts +22 -0
- package/src/operations/types/operation.enum.ts +25 -0
- package/src/rules/index.ts +2 -0
- package/src/rules/rule.evaluator.ts +14 -0
- package/src/rules/tests/rule.evaluator.spec.ts +135 -0
- package/src/rules/types.ts +27 -0
- package/src/index.spec.ts +0 -5
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { OperationEnum } from './operation.enum';
|
|
2
|
+
import { StringOperationPayload } from '../string';
|
|
3
|
+
import { NumericOperationPayload } from '../numeric';
|
|
4
|
+
import { DateOperationPayload } from '../date';
|
|
5
|
+
import { BooleanOperationPayload } from '../boolean/types';
|
|
6
|
+
export interface OperationResult {
|
|
7
|
+
isValid: boolean;
|
|
8
|
+
}
|
|
9
|
+
export type ConditionValue = StringOperationPayload | NumericOperationPayload | DateOperationPayload | BooleanOperationPayload;
|
|
10
|
+
export type OperationHandler = (attribute: any) => OperationResult;
|
|
11
|
+
export type OperationContextEnricher = (value: ConditionValue) => OperationHandler;
|
|
12
|
+
export type OperationsMapper = {
|
|
13
|
+
[key in OperationEnum]?: OperationContextEnricher;
|
|
14
|
+
};
|
|
15
|
+
export * from './operation.enum';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./operation.enum"), exports);
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/operations/types/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAqBA,mDAAiC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare enum OperationEnum {
|
|
2
|
+
InList = "in_list",
|
|
3
|
+
StartsWith = "starts_with",
|
|
4
|
+
EndsWith = "ends_with",
|
|
5
|
+
Contains = "contains",
|
|
6
|
+
Matches = "matches",
|
|
7
|
+
Equal = "equal",
|
|
8
|
+
GreaterThan = "greater_than",
|
|
9
|
+
GreaterThanEqual = "greater_than_equal",
|
|
10
|
+
LesserThan = "lower_than",
|
|
11
|
+
LesserThanEqual = "lower_than_equal",
|
|
12
|
+
BetweenNumeric = "between_numeric",
|
|
13
|
+
Is = "is",
|
|
14
|
+
On = "on",
|
|
15
|
+
BetweenDate = "between_date",
|
|
16
|
+
OnOrAfter = "on_or_after",
|
|
17
|
+
OnOrBefore = "on_or_before"
|
|
18
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OperationEnum = void 0;
|
|
4
|
+
var OperationEnum;
|
|
5
|
+
(function (OperationEnum) {
|
|
6
|
+
// String Operations
|
|
7
|
+
OperationEnum["InList"] = "in_list";
|
|
8
|
+
OperationEnum["StartsWith"] = "starts_with";
|
|
9
|
+
OperationEnum["EndsWith"] = "ends_with";
|
|
10
|
+
OperationEnum["Contains"] = "contains";
|
|
11
|
+
OperationEnum["Matches"] = "matches";
|
|
12
|
+
// Numeric Operations
|
|
13
|
+
OperationEnum["Equal"] = "equal";
|
|
14
|
+
OperationEnum["GreaterThan"] = "greater_than";
|
|
15
|
+
OperationEnum["GreaterThanEqual"] = "greater_than_equal";
|
|
16
|
+
OperationEnum["LesserThan"] = "lower_than";
|
|
17
|
+
OperationEnum["LesserThanEqual"] = "lower_than_equal";
|
|
18
|
+
OperationEnum["BetweenNumeric"] = "between_numeric";
|
|
19
|
+
// Boolean Operations
|
|
20
|
+
OperationEnum["Is"] = "is";
|
|
21
|
+
// Date Operations
|
|
22
|
+
OperationEnum["On"] = "on";
|
|
23
|
+
OperationEnum["BetweenDate"] = "between_date";
|
|
24
|
+
OperationEnum["OnOrAfter"] = "on_or_after";
|
|
25
|
+
OperationEnum["OnOrBefore"] = "on_or_before";
|
|
26
|
+
})(OperationEnum = exports.OperationEnum || (exports.OperationEnum = {}));
|
|
27
|
+
//# sourceMappingURL=operation.enum.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operation.enum.js","sourceRoot":"","sources":["../../../src/operations/types/operation.enum.ts"],"names":[],"mappings":";;;AAAA,IAAY,aAwBX;AAxBD,WAAY,aAAa;IACvB,oBAAoB;IACpB,mCAAkB,CAAA;IAClB,2CAA0B,CAAA;IAC1B,uCAAsB,CAAA;IACtB,sCAAqB,CAAA;IACrB,oCAAmB,CAAA;IAEnB,qBAAqB;IACrB,gCAAe,CAAA;IACf,6CAA4B,CAAA;IAC5B,wDAAuC,CAAA;IACvC,0CAAyB,CAAA;IACzB,qDAAoC,CAAA;IACpC,mDAAkC,CAAA;IAElC,qBAAqB;IACrB,0BAAS,CAAA;IAET,kBAAkB;IAClB,0BAAS,CAAA;IACT,6CAA4B,CAAA;IAC5B,0CAAyB,CAAA;IACzB,4CAA2B,CAAA;AAC7B,CAAC,EAxBW,aAAa,GAAb,qBAAa,KAAb,qBAAa,QAwBxB"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./types"), exports);
|
|
18
|
+
__exportStar(require("./rule.evaluator"), exports);
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0CAAwB;AACxB,mDAAiC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createRuleEvaluator = void 0;
|
|
4
|
+
const types_1 = require("./types");
|
|
5
|
+
const conditions_1 = require("../conditions");
|
|
6
|
+
function createRuleEvaluator(payload) {
|
|
7
|
+
return (attributes) => {
|
|
8
|
+
const isRuleTreatable = payload.rule.conditions.every((condition) => {
|
|
9
|
+
const evaluator = (0, conditions_1.createConditionEvaluator)({ condition });
|
|
10
|
+
return evaluator(attributes);
|
|
11
|
+
});
|
|
12
|
+
return isRuleTreatable ? types_1.RuleEvaluationResultEnum.Treatable : types_1.RuleEvaluationResultEnum.Insufficient;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
exports.createRuleEvaluator = createRuleEvaluator;
|
|
16
|
+
//# sourceMappingURL=rule.evaluator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule.evaluator.js","sourceRoot":"","sources":["../../src/rules/rule.evaluator.ts"],"names":[],"mappings":";;;AAAA,mCAA8F;AAC9F,8CAAyD;AAEzD,SAAgB,mBAAmB,CAAC,OAAmC;IACrE,OAAO,CAAC,UAAmC,EAA4B,EAAE;QACvE,MAAM,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,SAAS,EAAE,EAAE;YAClE,MAAM,SAAS,GAAG,IAAA,qCAAwB,EAAC,EAAE,SAAS,EAAE,CAAC,CAAC;YAE1D,OAAO,SAAS,CAAC,UAAU,CAAC,CAAC;QAC/B,CAAC,CAAC,CAAC;QAEH,OAAO,eAAe,CAAC,CAAC,CAAC,gCAAwB,CAAC,SAAS,CAAC,CAAC,CAAC,gCAAwB,CAAC,YAAY,CAAC;IACtG,CAAC,CAAC;AACJ,CAAC;AAVD,kDAUC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Condition } from '../conditions';
|
|
2
|
+
export interface Rule {
|
|
3
|
+
conditionLogic: ConditionLogicEnum.And;
|
|
4
|
+
conditions: Condition[];
|
|
5
|
+
treatment: TreatmentEnum;
|
|
6
|
+
}
|
|
7
|
+
export declare enum ConditionLogicEnum {
|
|
8
|
+
And = "and"
|
|
9
|
+
}
|
|
10
|
+
export declare enum TreatmentEnum {
|
|
11
|
+
True = "true",
|
|
12
|
+
False = "false"
|
|
13
|
+
}
|
|
14
|
+
export declare enum RuleEvaluationResultEnum {
|
|
15
|
+
Treatable = "treatable",
|
|
16
|
+
Insufficient = "insufficient"
|
|
17
|
+
}
|
|
18
|
+
export interface CreateRuleEvaluatorPayload {
|
|
19
|
+
rule: Rule;
|
|
20
|
+
}
|
|
21
|
+
export type RuleEvaluator = (attributes: Record<string, unknown>) => RuleEvaluationResultEnum;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RuleEvaluationResultEnum = exports.TreatmentEnum = exports.ConditionLogicEnum = void 0;
|
|
4
|
+
var ConditionLogicEnum;
|
|
5
|
+
(function (ConditionLogicEnum) {
|
|
6
|
+
ConditionLogicEnum["And"] = "and";
|
|
7
|
+
})(ConditionLogicEnum = exports.ConditionLogicEnum || (exports.ConditionLogicEnum = {}));
|
|
8
|
+
var TreatmentEnum;
|
|
9
|
+
(function (TreatmentEnum) {
|
|
10
|
+
TreatmentEnum["True"] = "true";
|
|
11
|
+
TreatmentEnum["False"] = "false";
|
|
12
|
+
})(TreatmentEnum = exports.TreatmentEnum || (exports.TreatmentEnum = {}));
|
|
13
|
+
var RuleEvaluationResultEnum;
|
|
14
|
+
(function (RuleEvaluationResultEnum) {
|
|
15
|
+
RuleEvaluationResultEnum["Treatable"] = "treatable";
|
|
16
|
+
RuleEvaluationResultEnum["Insufficient"] = "insufficient";
|
|
17
|
+
})(RuleEvaluationResultEnum = exports.RuleEvaluationResultEnum || (exports.RuleEvaluationResultEnum = {}));
|
|
18
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/rules/types.ts"],"names":[],"mappings":";;;AAQA,IAAY,kBAEX;AAFD,WAAY,kBAAkB;IAC5B,iCAAW,CAAA;AACb,CAAC,EAFW,kBAAkB,GAAlB,0BAAkB,KAAlB,0BAAkB,QAE7B;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,20 @@
|
|
|
1
|
+
# [1.0.0-alpha.3](https://github.com/frontegg/entitlements-javascript-commons/compare/v-1.0.0-alpha.2...v-1.0.0-alpha.3) (2023-10-04)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* **feature-flags:** add feature flag evaluation ([#11](https://github.com/frontegg/entitlements-javascript-commons/issues/11)) ([94679a1](https://github.com/frontegg/entitlements-javascript-commons/commit/94679a123581cd4977fd9f2087adc9e2532a638c))
|
|
7
|
+
|
|
8
|
+
# [1.0.0-alpha.2](https://github.com/frontegg/entitlements-javascript-commons/compare/v-1.0.0-alpha.1...v-1.0.0-alpha.2) (2023-10-03)
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* **rule:** add rule evaluator ([#6](https://github.com/frontegg/entitlements-javascript-commons/issues/6)) ([5fbf4da](https://github.com/frontegg/entitlements-javascript-commons/commit/5fbf4da00a3d9df2908d8899723a64b1bd80a7c2))
|
|
13
|
+
* create pipeline and release infra ([16fd6d1](https://github.com/frontegg/entitlements-javascript-commons/commit/16fd6d165cff4c3ae28e2392db2480d41dd591b1))
|
|
14
|
+
* **conditions:** add condition evaluator ([#5](https://github.com/frontegg/entitlements-javascript-commons/issues/5)) ([5ca2446](https://github.com/frontegg/entitlements-javascript-commons/commit/5ca24465a76b9fa103977e5600a6d870da5520cb))
|
|
15
|
+
* **operations:** add numeric operations ([#3](https://github.com/frontegg/entitlements-javascript-commons/issues/3)) ([#10](https://github.com/frontegg/entitlements-javascript-commons/issues/10)) ([0b3de2f](https://github.com/frontegg/entitlements-javascript-commons/commit/0b3de2f7f1aede036ec63e4fadf898dcf5ad32a4))
|
|
16
|
+
* **operations:** add string operations ([#2](https://github.com/frontegg/entitlements-javascript-commons/issues/2)) ([e2e63a7](https://github.com/frontegg/entitlements-javascript-commons/commit/e2e63a74211a723dc326918e42e2093fcca86779))
|
|
17
|
+
|
|
1
18
|
# 1.0.0-alpha.1 (2023-10-03)
|
|
2
19
|
|
|
3
20
|
|
package/jest.config.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
transform: { '^.+\\.ts?$': 'ts-jest' },
|
|
3
|
+
testEnvironment: 'node',
|
|
4
|
+
testRegex: 'src/.*\\.(test|spec)?\\.(ts|tsx)$',
|
|
5
|
+
moduleFileExtensions: ['ts', 'js', 'json', 'node'],
|
|
6
|
+
rootDir: '.',
|
|
7
|
+
collectCoverageFrom: ['src/**/*.{js,ts}', '!**/node_modules/**', '!**/dist/**', '!**/vendor/**'],
|
|
8
|
+
coverageThreshold: {
|
|
9
|
+
global: {
|
|
10
|
+
statements: 17,
|
|
11
|
+
branches: 24,
|
|
12
|
+
functions: 20,
|
|
13
|
+
lines: 18,
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
reporters: [
|
|
17
|
+
'default',
|
|
18
|
+
[
|
|
19
|
+
'jest-junit',
|
|
20
|
+
{
|
|
21
|
+
outputDirectory: 'test-results',
|
|
22
|
+
outputName: 'jest-junit.xml',
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
],
|
|
26
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontegg/entitlements-javascript-commons",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.3",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"author": "Frontegg",
|
|
22
22
|
"license": "ISC",
|
|
23
23
|
"homepage": "https://github.com/frontegg/entitlements-javascript-commons",
|
|
24
|
-
"dependencies": {},
|
|
25
24
|
"devDependencies": {
|
|
25
|
+
"@fast-check/jest": "^1.7.3",
|
|
26
26
|
"@semantic-release/changelog": "^6.0.1",
|
|
27
27
|
"@semantic-release/git": "^10.0.1",
|
|
28
28
|
"@types/jest": "^29.2.0",
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Condition } from './types';
|
|
2
|
+
import { useOperation } from '../operations/components/operation.resolver';
|
|
3
|
+
|
|
4
|
+
export interface CreateConditionEvaluatorPayload {
|
|
5
|
+
condition: Condition;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type ConditionEvaluator = (attributes: Record<string, unknown>) => boolean;
|
|
9
|
+
|
|
10
|
+
export function createConditionEvaluator(payload: CreateConditionEvaluatorPayload): ConditionEvaluator {
|
|
11
|
+
const operation = useOperation(payload.condition.op, payload.condition.value);
|
|
12
|
+
|
|
13
|
+
return (attributes: Record<string, unknown>) => {
|
|
14
|
+
const attributeKey = payload.condition.attribute;
|
|
15
|
+
const value = attributes[attributeKey];
|
|
16
|
+
const { isValid: result } = value !== undefined && operation ? operation(value) : { isValid: false };
|
|
17
|
+
|
|
18
|
+
return payload.condition.negate ? !result : result;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { createConditionEvaluator } from '../condition.evaluator';
|
|
2
|
+
import { OperationEnum } from '../../operations/types';
|
|
3
|
+
|
|
4
|
+
describe('ConditionEvaluator', () => {
|
|
5
|
+
it('should return false when condition operation is undefined', () => {
|
|
6
|
+
const conditionEvaluator = createConditionEvaluator({
|
|
7
|
+
condition: { op: 'not supported' as any, value: { string: 'test' }, negate: false, attribute: 'test' },
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
expect(conditionEvaluator({})).toEqual(false);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('should return false when attribute value is undefined', () => {
|
|
14
|
+
const attributeKey = 'vendorId';
|
|
15
|
+
const attributes = { [attributeKey]: undefined };
|
|
16
|
+
const conditionEvaluator = createConditionEvaluator({
|
|
17
|
+
condition: { op: OperationEnum.Contains, value: { list: ['test'] }, negate: false, attribute: attributeKey },
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
expect(conditionEvaluator(attributes)).toEqual(false);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should return false when attribute fails operation validation', () => {
|
|
24
|
+
const attributeKey = 'vendorId';
|
|
25
|
+
const attributes = { [attributeKey]: 'Vendor' };
|
|
26
|
+
const conditionEvaluator = createConditionEvaluator({
|
|
27
|
+
condition: { op: OperationEnum.Contains, value: { list: ['test'] }, negate: false, attribute: attributeKey },
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
expect(conditionEvaluator(attributes)).toEqual(false);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should return true when attribute succeeds operation validation', () => {
|
|
34
|
+
const attributeKey = 'vendorId';
|
|
35
|
+
const attributes = { [attributeKey]: 'testVendor' };
|
|
36
|
+
const conditionEvaluator = createConditionEvaluator({
|
|
37
|
+
condition: { op: OperationEnum.Contains, value: { list: ['test'] }, negate: false, attribute: attributeKey },
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
expect(conditionEvaluator(attributes)).toEqual(true);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should return false when attribute succeeds operation validation with negate', () => {
|
|
44
|
+
const attributeKey = 'vendorId';
|
|
45
|
+
const attributes = { [attributeKey]: 'testVendor' };
|
|
46
|
+
const conditionEvaluator = createConditionEvaluator({
|
|
47
|
+
condition: { op: OperationEnum.Contains, value: { list: ['test'] }, negate: true, attribute: attributeKey },
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
expect(conditionEvaluator(attributes)).toEqual(false);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { FeatureFlag, FeatureFlagEvaluationResult } from './types';
|
|
2
|
+
import { createRuleEvaluator, Rule, RuleEvaluationResultEnum } from '../rules';
|
|
3
|
+
|
|
4
|
+
export function evaluateFeatureFlag(
|
|
5
|
+
featureFlag: FeatureFlag,
|
|
6
|
+
attributes: Record<string, unknown>,
|
|
7
|
+
): FeatureFlagEvaluationResult {
|
|
8
|
+
if (!featureFlag.on) {
|
|
9
|
+
return { treatment: featureFlag.offTreatment };
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const treatableRule = findTreatableRule(featureFlag, attributes);
|
|
13
|
+
if (treatableRule) {
|
|
14
|
+
return { treatment: treatableRule.treatment };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return { treatment: featureFlag.defaultTreatment };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function findTreatableRule(featureFlag: FeatureFlag, attributes: Record<string, unknown>): Rule | undefined {
|
|
21
|
+
return featureFlag.rules?.find((rule) => {
|
|
22
|
+
const evaluator = createRuleEvaluator({ rule });
|
|
23
|
+
const result = evaluator(attributes);
|
|
24
|
+
|
|
25
|
+
return result === RuleEvaluationResultEnum.Treatable;
|
|
26
|
+
});
|
|
27
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './feature-flag.evaluator';
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { ConditionLogicEnum, TreatmentEnum } from '../../rules';
|
|
2
|
+
import { evaluateFeatureFlag } from '../feature-flag.evaluator';
|
|
3
|
+
import { FeatureFlag } from '../types';
|
|
4
|
+
import { OperationEnum } from '../../operations/types';
|
|
5
|
+
|
|
6
|
+
type FeatureFlagCreator = (data?: Partial<FeatureFlag>) => FeatureFlag;
|
|
7
|
+
const featureFlagFactory: FeatureFlagCreator = (data?: Partial<FeatureFlag>) => ({
|
|
8
|
+
on: false,
|
|
9
|
+
offTreatment: TreatmentEnum.False,
|
|
10
|
+
defaultTreatment: TreatmentEnum.False,
|
|
11
|
+
...data,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
describe('evaluateFeatureFlag', () => {
|
|
15
|
+
describe('off treatment', () => {
|
|
16
|
+
it('should return false when feature flag is turned off and offTreatment is false', () => {
|
|
17
|
+
const featureFlag: FeatureFlag = featureFlagFactory({
|
|
18
|
+
rules: [
|
|
19
|
+
{
|
|
20
|
+
treatment: TreatmentEnum.True,
|
|
21
|
+
conditions: [
|
|
22
|
+
{
|
|
23
|
+
attribute: 'test',
|
|
24
|
+
op: 'not supported' as any,
|
|
25
|
+
value: { list: ['test'] },
|
|
26
|
+
negate: false,
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
conditionLogic: ConditionLogicEnum.And,
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const result = evaluateFeatureFlag(featureFlag, {});
|
|
35
|
+
|
|
36
|
+
expect(result).toEqual({ treatment: TreatmentEnum.False });
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should return true when feature flag is turned off and offTreatment is true', () => {
|
|
40
|
+
const featureFlag: FeatureFlag = featureFlagFactory({
|
|
41
|
+
offTreatment: TreatmentEnum.True,
|
|
42
|
+
rules: [
|
|
43
|
+
{
|
|
44
|
+
treatment: TreatmentEnum.True,
|
|
45
|
+
conditions: [
|
|
46
|
+
{
|
|
47
|
+
attribute: 'test',
|
|
48
|
+
op: 'not supported' as any,
|
|
49
|
+
value: { list: ['test'] },
|
|
50
|
+
negate: false,
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
conditionLogic: ConditionLogicEnum.And,
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const result = evaluateFeatureFlag(featureFlag, {});
|
|
59
|
+
|
|
60
|
+
expect(result).toEqual({ treatment: TreatmentEnum.True });
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('default treatment', () => {
|
|
65
|
+
it('should return false when feature flag is turned on and no rule is valid and defaultTreatment is false', () => {
|
|
66
|
+
const featureFlag: FeatureFlag = featureFlagFactory({
|
|
67
|
+
on: true,
|
|
68
|
+
rules: [
|
|
69
|
+
{
|
|
70
|
+
treatment: TreatmentEnum.True,
|
|
71
|
+
conditions: [
|
|
72
|
+
{
|
|
73
|
+
attribute: 'test',
|
|
74
|
+
op: 'not supported' as any,
|
|
75
|
+
value: { list: ['test'] },
|
|
76
|
+
negate: false,
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
conditionLogic: ConditionLogicEnum.And,
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const result = evaluateFeatureFlag(featureFlag, {});
|
|
85
|
+
|
|
86
|
+
expect(result).toEqual({ treatment: TreatmentEnum.False });
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should return true when feature flag is turned on and no rule is valid and defaultTreatment is true', () => {
|
|
90
|
+
const featureFlag: FeatureFlag = featureFlagFactory({
|
|
91
|
+
on: true,
|
|
92
|
+
defaultTreatment: TreatmentEnum.True,
|
|
93
|
+
rules: [
|
|
94
|
+
{
|
|
95
|
+
treatment: TreatmentEnum.True,
|
|
96
|
+
conditions: [
|
|
97
|
+
{
|
|
98
|
+
attribute: 'test',
|
|
99
|
+
op: 'not supported' as any,
|
|
100
|
+
value: { list: ['test'] },
|
|
101
|
+
negate: false,
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
conditionLogic: ConditionLogicEnum.And,
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const result = evaluateFeatureFlag(featureFlag, {});
|
|
110
|
+
|
|
111
|
+
expect(result).toEqual({ treatment: TreatmentEnum.True });
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe('complete evaluation', () => {
|
|
116
|
+
it('should return false according to first treatable rule when feature flag is turned on', () => {
|
|
117
|
+
const featureFlag: FeatureFlag = featureFlagFactory({
|
|
118
|
+
on: true,
|
|
119
|
+
rules: [
|
|
120
|
+
{
|
|
121
|
+
treatment: TreatmentEnum.False,
|
|
122
|
+
conditions: [
|
|
123
|
+
{
|
|
124
|
+
attribute: 'attribute',
|
|
125
|
+
op: OperationEnum.InList,
|
|
126
|
+
value: { list: ['test'] },
|
|
127
|
+
negate: false,
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
conditionLogic: ConditionLogicEnum.And,
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const result = evaluateFeatureFlag(featureFlag, { attribute: 'test' });
|
|
136
|
+
|
|
137
|
+
expect(result).toEqual({ treatment: TreatmentEnum.False });
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should return true according to first treatable rule when feature flag is turned on', () => {
|
|
141
|
+
const featureFlag: FeatureFlag = featureFlagFactory({
|
|
142
|
+
on: true,
|
|
143
|
+
rules: [
|
|
144
|
+
{
|
|
145
|
+
treatment: TreatmentEnum.False,
|
|
146
|
+
conditions: [
|
|
147
|
+
{
|
|
148
|
+
attribute: 'test',
|
|
149
|
+
op: 'not supported' as any,
|
|
150
|
+
value: { list: ['test'] },
|
|
151
|
+
negate: false,
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
conditionLogic: ConditionLogicEnum.And,
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
treatment: TreatmentEnum.True,
|
|
158
|
+
conditions: [
|
|
159
|
+
{
|
|
160
|
+
attribute: 'attribute',
|
|
161
|
+
op: OperationEnum.InList,
|
|
162
|
+
value: { list: ['test'] },
|
|
163
|
+
negate: false,
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
conditionLogic: ConditionLogicEnum.And,
|
|
167
|
+
},
|
|
168
|
+
],
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
const result = evaluateFeatureFlag(featureFlag, { attribute: 'test' });
|
|
172
|
+
|
|
173
|
+
expect(result).toEqual({ treatment: TreatmentEnum.True });
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Rule, TreatmentEnum } from '../rules';
|
|
2
|
+
|
|
3
|
+
export interface FeatureFlag {
|
|
4
|
+
on: boolean;
|
|
5
|
+
offTreatment: TreatmentEnum;
|
|
6
|
+
defaultTreatment: TreatmentEnum;
|
|
7
|
+
rules?: Rule[];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface FeatureFlagEvaluationResult {
|
|
11
|
+
treatment: TreatmentEnum;
|
|
12
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { OperationEnum, OperationsMapper } from '../types';
|
|
2
|
+
import { useIsOperation } from './operations';
|
|
3
|
+
import { BooleanOperationPayload } from './types';
|
|
4
|
+
|
|
5
|
+
export * from './operations';
|
|
6
|
+
|
|
7
|
+
export const BooleanOperationsMapper: OperationsMapper = {
|
|
8
|
+
[OperationEnum.Is]: (value) => useIsOperation(value as BooleanOperationPayload),
|
|
9
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { fc, test } from '@fast-check/jest';
|
|
2
|
+
import { useIsOperation } from './operations';
|
|
3
|
+
|
|
4
|
+
describe('Boolean operations', () => {
|
|
5
|
+
test.prop([fc.boolean(), fc.boolean()], { verbose: true })(
|
|
6
|
+
'should return correct validity if the boolean values equal',
|
|
7
|
+
(boolean, attribute) => {
|
|
8
|
+
const result = useIsOperation({ boolean })(attribute);
|
|
9
|
+
|
|
10
|
+
return (attribute === boolean) === result.isValid;
|
|
11
|
+
},
|
|
12
|
+
);
|
|
13
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ConditionValue, OperationEnum, OperationHandler, OperationsMapper } from '../types';
|
|
2
|
+
import { StringOperationsMapper } from '../string';
|
|
3
|
+
import { NumericOperationsMapper } from '../numeric';
|
|
4
|
+
import { DateOperationsMapper } from '../date';
|
|
5
|
+
import { BooleanOperationsMapper } from '../boolean';
|
|
6
|
+
|
|
7
|
+
const operationEnrichersMapper: OperationsMapper = {
|
|
8
|
+
...StringOperationsMapper,
|
|
9
|
+
...NumericOperationsMapper,
|
|
10
|
+
...DateOperationsMapper,
|
|
11
|
+
...BooleanOperationsMapper,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function useOperation(operation: OperationEnum, value: ConditionValue): OperationHandler | undefined {
|
|
15
|
+
const operationContextEnricher = operationEnrichersMapper[operation];
|
|
16
|
+
|
|
17
|
+
return operationContextEnricher ? operationContextEnricher(value) : undefined;
|
|
18
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { useOperation } from '../operation.resolver';
|
|
2
|
+
|
|
3
|
+
describe('OperationResolver', () => {
|
|
4
|
+
it('should return undefined when operation is not supported', () => {
|
|
5
|
+
const operation = useOperation('not supported' as any, { string: 'test' });
|
|
6
|
+
expect(operation).toEqual(undefined);
|
|
7
|
+
});
|
|
8
|
+
});
|