@featurevisor/core 1.5.1 → 1.7.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/.eslintcache +1 -1
- package/CHANGELOG.md +22 -0
- package/coverage/clover.xml +2 -2
- package/coverage/lcov-report/index.html +1 -1
- package/coverage/lcov-report/lib/builder/allocator.js.html +1 -1
- package/coverage/lcov-report/lib/builder/index.html +1 -1
- package/coverage/lcov-report/lib/builder/traffic.js.html +1 -1
- package/coverage/lcov-report/lib/tester/checkIfObjectsAreEqual.js.html +1 -1
- package/coverage/lcov-report/lib/tester/index.html +1 -1
- package/coverage/lcov-report/lib/tester/matrix.js.html +1 -1
- package/coverage/lcov-report/src/builder/allocator.ts.html +1 -1
- package/coverage/lcov-report/src/builder/index.html +1 -1
- package/coverage/lcov-report/src/builder/traffic.ts.html +1 -1
- package/coverage/lcov-report/src/tester/checkIfObjectsAreEqual.ts.html +1 -1
- package/coverage/lcov-report/src/tester/index.html +1 -1
- package/coverage/lcov-report/src/tester/matrix.ts.html +1 -1
- package/lib/linter/attributeSchema.d.ts +17 -2
- package/lib/linter/attributeSchema.js +13 -11
- package/lib/linter/attributeSchema.js.map +1 -1
- package/lib/linter/checkPercentageExceedingSlot.d.ts +3 -0
- package/lib/linter/checkPercentageExceedingSlot.js +86 -0
- package/lib/linter/checkPercentageExceedingSlot.js.map +1 -0
- package/lib/linter/conditionSchema.d.ts +2 -2
- package/lib/linter/conditionSchema.js +112 -57
- package/lib/linter/conditionSchema.js.map +1 -1
- package/lib/linter/featureSchema.d.ts +229 -2
- package/lib/linter/featureSchema.js +195 -139
- package/lib/linter/featureSchema.js.map +1 -1
- package/lib/linter/groupSchema.d.ts +32 -2
- package/lib/linter/groupSchema.js +28 -97
- package/lib/linter/groupSchema.js.map +1 -1
- package/lib/linter/lintProject.d.ts +5 -1
- package/lib/linter/lintProject.js +213 -139
- package/lib/linter/lintProject.js.map +1 -1
- package/lib/linter/printError.d.ts +2 -0
- package/lib/linter/printError.js +20 -0
- package/lib/linter/printError.js.map +1 -0
- package/lib/linter/segmentSchema.d.ts +14 -2
- package/lib/linter/segmentSchema.js +12 -10
- package/lib/linter/segmentSchema.js.map +1 -1
- package/lib/linter/testSchema.d.ts +90 -2
- package/lib/linter/testSchema.js +49 -38
- package/lib/linter/testSchema.js.map +1 -1
- package/lib/tester/cliFormat.d.ts +1 -0
- package/lib/tester/cliFormat.js +2 -1
- package/lib/tester/cliFormat.js.map +1 -1
- package/package.json +4 -4
- package/src/linter/attributeSchema.ts +11 -9
- package/src/linter/checkPercentageExceedingSlot.ts +41 -0
- package/src/linter/conditionSchema.ts +120 -97
- package/src/linter/featureSchema.ts +241 -177
- package/src/linter/groupSchema.ts +38 -54
- package/src/linter/lintProject.ts +233 -110
- package/src/linter/printError.ts +21 -0
- package/src/linter/segmentSchema.ts +10 -8
- package/src/linter/testSchema.ts +67 -50
- package/src/tester/cliFormat.ts +1 -0
- package/lib/linter/printJoiError.d.ts +0 -2
- package/lib/linter/printJoiError.js +0 -14
- package/lib/linter/printJoiError.js.map +0 -1
- package/src/linter/printJoiError.ts +0 -11
|
@@ -1,3 +1,91 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { z } from "zod";
|
|
2
2
|
import { ProjectConfig } from "../config";
|
|
3
|
-
export declare function
|
|
3
|
+
export declare function getTestsZodSchema(projectConfig: ProjectConfig, availableFeatureKeys: [string, ...string[]], availableSegmentKeys: [string, ...string[]]): z.ZodUnion<[z.ZodObject<{
|
|
4
|
+
segment: z.ZodEffects<z.ZodString, string, string>;
|
|
5
|
+
assertions: z.ZodArray<z.ZodObject<{
|
|
6
|
+
matrix: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
7
|
+
description: z.ZodOptional<z.ZodString>;
|
|
8
|
+
context: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
9
|
+
expectedToMatch: z.ZodBoolean;
|
|
10
|
+
}, "strict", z.ZodTypeAny, {
|
|
11
|
+
matrix?: Record<string, unknown>;
|
|
12
|
+
description?: string;
|
|
13
|
+
context?: Record<string, unknown>;
|
|
14
|
+
expectedToMatch?: boolean;
|
|
15
|
+
}, {
|
|
16
|
+
matrix?: Record<string, unknown>;
|
|
17
|
+
description?: string;
|
|
18
|
+
context?: Record<string, unknown>;
|
|
19
|
+
expectedToMatch?: boolean;
|
|
20
|
+
}>, "many">;
|
|
21
|
+
}, "strict", z.ZodTypeAny, {
|
|
22
|
+
segment?: string;
|
|
23
|
+
assertions?: {
|
|
24
|
+
matrix?: Record<string, unknown>;
|
|
25
|
+
description?: string;
|
|
26
|
+
context?: Record<string, unknown>;
|
|
27
|
+
expectedToMatch?: boolean;
|
|
28
|
+
}[];
|
|
29
|
+
}, {
|
|
30
|
+
segment?: string;
|
|
31
|
+
assertions?: {
|
|
32
|
+
matrix?: Record<string, unknown>;
|
|
33
|
+
description?: string;
|
|
34
|
+
context?: Record<string, unknown>;
|
|
35
|
+
expectedToMatch?: boolean;
|
|
36
|
+
}[];
|
|
37
|
+
}>, z.ZodObject<{
|
|
38
|
+
feature: z.ZodEffects<z.ZodString, string, string>;
|
|
39
|
+
assertions: z.ZodArray<z.ZodObject<{
|
|
40
|
+
matrix: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
41
|
+
description: z.ZodOptional<z.ZodString>;
|
|
42
|
+
at: z.ZodUnion<[z.ZodNumber, z.ZodString]>;
|
|
43
|
+
environment: z.ZodEffects<z.ZodString, string, string>;
|
|
44
|
+
context: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
45
|
+
expectedToBeEnabled: z.ZodBoolean;
|
|
46
|
+
expectedVariation: z.ZodOptional<z.ZodString>;
|
|
47
|
+
expectedVariables: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
48
|
+
}, "strict", z.ZodTypeAny, {
|
|
49
|
+
matrix?: Record<string, unknown>;
|
|
50
|
+
description?: string;
|
|
51
|
+
at?: string | number;
|
|
52
|
+
environment?: string;
|
|
53
|
+
context?: Record<string, unknown>;
|
|
54
|
+
expectedToBeEnabled?: boolean;
|
|
55
|
+
expectedVariation?: string;
|
|
56
|
+
expectedVariables?: Record<string, unknown>;
|
|
57
|
+
}, {
|
|
58
|
+
matrix?: Record<string, unknown>;
|
|
59
|
+
description?: string;
|
|
60
|
+
at?: string | number;
|
|
61
|
+
environment?: string;
|
|
62
|
+
context?: Record<string, unknown>;
|
|
63
|
+
expectedToBeEnabled?: boolean;
|
|
64
|
+
expectedVariation?: string;
|
|
65
|
+
expectedVariables?: Record<string, unknown>;
|
|
66
|
+
}>, "many">;
|
|
67
|
+
}, "strict", z.ZodTypeAny, {
|
|
68
|
+
feature?: string;
|
|
69
|
+
assertions?: {
|
|
70
|
+
matrix?: Record<string, unknown>;
|
|
71
|
+
description?: string;
|
|
72
|
+
at?: string | number;
|
|
73
|
+
environment?: string;
|
|
74
|
+
context?: Record<string, unknown>;
|
|
75
|
+
expectedToBeEnabled?: boolean;
|
|
76
|
+
expectedVariation?: string;
|
|
77
|
+
expectedVariables?: Record<string, unknown>;
|
|
78
|
+
}[];
|
|
79
|
+
}, {
|
|
80
|
+
feature?: string;
|
|
81
|
+
assertions?: {
|
|
82
|
+
matrix?: Record<string, unknown>;
|
|
83
|
+
description?: string;
|
|
84
|
+
at?: string | number;
|
|
85
|
+
environment?: string;
|
|
86
|
+
context?: Record<string, unknown>;
|
|
87
|
+
expectedToBeEnabled?: boolean;
|
|
88
|
+
expectedVariation?: string;
|
|
89
|
+
expectedVariables?: Record<string, unknown>;
|
|
90
|
+
}[];
|
|
91
|
+
}>]>;
|
package/lib/linter/testSchema.js
CHANGED
|
@@ -1,48 +1,59 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
var
|
|
5
|
-
function
|
|
6
|
-
var
|
|
7
|
-
|
|
8
|
-
segment: (
|
|
9
|
-
.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
3
|
+
exports.getTestsZodSchema = void 0;
|
|
4
|
+
var zod_1 = require("zod");
|
|
5
|
+
function getTestsZodSchema(projectConfig, availableFeatureKeys, availableSegmentKeys) {
|
|
6
|
+
var segmentTestZodSchema = zod_1.z
|
|
7
|
+
.object({
|
|
8
|
+
segment: zod_1.z.string().refine(function (value) { return availableSegmentKeys.includes(value); }, function (value) { return ({
|
|
9
|
+
message: "Unknown segment \"".concat(value, "\""),
|
|
10
|
+
}); }),
|
|
11
|
+
assertions: zod_1.z.array(zod_1.z
|
|
12
|
+
.object({
|
|
13
|
+
matrix: zod_1.z.record(zod_1.z.unknown()).optional(),
|
|
14
|
+
description: zod_1.z.string().optional(),
|
|
15
|
+
context: zod_1.z.record(zod_1.z.unknown()),
|
|
16
|
+
expectedToMatch: zod_1.z.boolean(),
|
|
17
|
+
})
|
|
18
|
+
.strict()),
|
|
19
|
+
})
|
|
20
|
+
.strict();
|
|
21
|
+
var featureTestZodSchema = zod_1.z
|
|
22
|
+
.object({
|
|
23
|
+
feature: zod_1.z.string().refine(function (value) { return availableFeatureKeys.includes(value); }, function (value) { return ({
|
|
24
|
+
message: "Unknown feature \"".concat(value, "\""),
|
|
25
|
+
}); }),
|
|
26
|
+
assertions: zod_1.z.array(zod_1.z
|
|
27
|
+
.object({
|
|
28
|
+
matrix: zod_1.z.record(zod_1.z.unknown()).optional(),
|
|
29
|
+
description: zod_1.z.string().optional(),
|
|
30
|
+
at: zod_1.z.union([
|
|
31
|
+
zod_1.z.number().min(0).max(100),
|
|
32
|
+
// because of supporting matrix
|
|
33
|
+
zod_1.z.string(),
|
|
34
|
+
]),
|
|
35
|
+
environment: zod_1.z.string().refine(function (value) {
|
|
28
36
|
if (value.indexOf("${{") === 0) {
|
|
29
37
|
// allow unknown strings for matrix
|
|
30
|
-
return
|
|
38
|
+
return true;
|
|
31
39
|
}
|
|
32
40
|
// otherwise only known environments should be passed
|
|
33
41
|
if (projectConfig.environments.includes(value)) {
|
|
34
|
-
return
|
|
42
|
+
return true;
|
|
35
43
|
}
|
|
36
|
-
return
|
|
37
|
-
})
|
|
38
|
-
.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
return false;
|
|
45
|
+
}, function (value) { return ({
|
|
46
|
+
message: "Unknown environment \"".concat(value, "\""),
|
|
47
|
+
}); }),
|
|
48
|
+
context: zod_1.z.record(zod_1.z.unknown()),
|
|
49
|
+
expectedToBeEnabled: zod_1.z.boolean(),
|
|
50
|
+
expectedVariation: zod_1.z.string().optional(),
|
|
51
|
+
expectedVariables: zod_1.z.record(zod_1.z.unknown()).optional(),
|
|
52
|
+
})
|
|
53
|
+
.strict()),
|
|
54
|
+
})
|
|
55
|
+
.strict();
|
|
56
|
+
return zod_1.z.union([segmentTestZodSchema, featureTestZodSchema]);
|
|
46
57
|
}
|
|
47
|
-
exports.
|
|
58
|
+
exports.getTestsZodSchema = getTestsZodSchema;
|
|
48
59
|
//# sourceMappingURL=testSchema.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testSchema.js","sourceRoot":"","sources":["../../src/linter/testSchema.ts"],"names":[],"mappings":";;;AAAA,
|
|
1
|
+
{"version":3,"file":"testSchema.js","sourceRoot":"","sources":["../../src/linter/testSchema.ts"],"names":[],"mappings":";;;AAAA,2BAAwB;AAIxB,SAAgB,iBAAiB,CAC/B,aAA4B,EAC5B,oBAA2C,EAC3C,oBAA2C;IAE3C,IAAM,oBAAoB,GAAG,OAAC;SAC3B,MAAM,CAAC;QACN,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,MAAM,CACxB,UAAC,KAAK,IAAK,OAAA,oBAAoB,CAAC,QAAQ,CAAC,KAAK,CAAC,EAApC,CAAoC,EAC/C,UAAC,KAAK,IAAK,OAAA,CAAC;YACV,OAAO,EAAE,4BAAoB,KAAK,OAAG;SACtC,CAAC,EAFS,CAET,CACH;QACD,UAAU,EAAE,OAAC,CAAC,KAAK,CACjB,OAAC;aACE,MAAM,CAAC;YACN,MAAM,EAAE,OAAC,CAAC,MAAM,CAAC,OAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE;YACxC,WAAW,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;YAClC,OAAO,EAAE,OAAC,CAAC,MAAM,CAAC,OAAC,CAAC,OAAO,EAAE,CAAC;YAC9B,eAAe,EAAE,OAAC,CAAC,OAAO,EAAE;SAC7B,CAAC;aACD,MAAM,EAAE,CACZ;KACF,CAAC;SACD,MAAM,EAAE,CAAC;IAEZ,IAAM,oBAAoB,GAAG,OAAC;SAC3B,MAAM,CAAC;QACN,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,MAAM,CACxB,UAAC,KAAK,IAAK,OAAA,oBAAoB,CAAC,QAAQ,CAAC,KAAK,CAAC,EAApC,CAAoC,EAC/C,UAAC,KAAK,IAAK,OAAA,CAAC;YACV,OAAO,EAAE,4BAAoB,KAAK,OAAG;SACtC,CAAC,EAFS,CAET,CACH;QACD,UAAU,EAAE,OAAC,CAAC,KAAK,CACjB,OAAC;aACE,MAAM,CAAC;YACN,MAAM,EAAE,OAAC,CAAC,MAAM,CAAC,OAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE;YACxC,WAAW,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;YAClC,EAAE,EAAE,OAAC,CAAC,KAAK,CAAC;gBACV,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;gBAE1B,+BAA+B;gBAC/B,OAAC,CAAC,MAAM,EAAE;aACX,CAAC;YACF,WAAW,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,MAAM,CAC5B,UAAC,KAAK;gBACJ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;oBAC9B,mCAAmC;oBACnC,OAAO,IAAI,CAAC;iBACb;gBAED,qDAAqD;gBACrD,IAAI,aAAa,CAAC,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE;oBAC9C,OAAO,IAAI,CAAC;iBACb;gBAED,OAAO,KAAK,CAAC;YACf,CAAC,EACD,UAAC,KAAK,IAAK,OAAA,CAAC;gBACV,OAAO,EAAE,gCAAwB,KAAK,OAAG;aAC1C,CAAC,EAFS,CAET,CACH;YACD,OAAO,EAAE,OAAC,CAAC,MAAM,CAAC,OAAC,CAAC,OAAO,EAAE,CAAC;YAC9B,mBAAmB,EAAE,OAAC,CAAC,OAAO,EAAE;YAChC,iBAAiB,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;YACxC,iBAAiB,EAAE,OAAC,CAAC,MAAM,CAAC,OAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE;SACpD,CAAC;aACD,MAAM,EAAE,CACZ;KACF,CAAC;SACD,MAAM,EAAE,CAAC;IAEZ,OAAO,OAAC,CAAC,KAAK,CAAC,CAAC,oBAAoB,EAAE,oBAAoB,CAAC,CAAC,CAAC;AAC/D,CAAC;AA1ED,8CA0EC"}
|
package/lib/tester/cliFormat.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CLI_FORMAT_BOLD = exports.CLI_FORMAT_GREEN = exports.CLI_FORMAT_RED = void 0;
|
|
3
|
+
exports.CLI_FORMAT_UNDERLINE = exports.CLI_FORMAT_BOLD = exports.CLI_FORMAT_GREEN = exports.CLI_FORMAT_RED = void 0;
|
|
4
4
|
exports.CLI_FORMAT_RED = "\x1b[31m%s\x1b[0m";
|
|
5
5
|
exports.CLI_FORMAT_GREEN = "\x1b[32m%s\x1b[0m";
|
|
6
6
|
exports.CLI_FORMAT_BOLD = "\x1b[1m%s\x1b[0m";
|
|
7
|
+
exports.CLI_FORMAT_UNDERLINE = "\x1b[4m%s\x1b[0m";
|
|
7
8
|
//# sourceMappingURL=cliFormat.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cliFormat.js","sourceRoot":"","sources":["../../src/tester/cliFormat.ts"],"names":[],"mappings":";;;AAAa,QAAA,cAAc,GAAG,mBAAmB,CAAC;AACrC,QAAA,gBAAgB,GAAG,mBAAmB,CAAC;AAEvC,QAAA,eAAe,GAAG,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"cliFormat.js","sourceRoot":"","sources":["../../src/tester/cliFormat.ts"],"names":[],"mappings":";;;AAAa,QAAA,cAAc,GAAG,mBAAmB,CAAC;AACrC,QAAA,gBAAgB,GAAG,mBAAmB,CAAC;AAEvC,QAAA,eAAe,GAAG,kBAAkB,CAAC;AACrC,QAAA,oBAAoB,GAAG,kBAAkB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@featurevisor/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Core package of Featurevisor for Node.js usage",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -48,14 +48,14 @@
|
|
|
48
48
|
"@featurevisor/site": "^1.5.1",
|
|
49
49
|
"@featurevisor/types": "^1.3.0",
|
|
50
50
|
"axios": "^1.3.4",
|
|
51
|
-
"joi": "^17.8.3",
|
|
52
51
|
"js-yaml": "^4.1.0",
|
|
53
52
|
"mkdirp": "^2.1.3",
|
|
54
|
-
"tar": "^6.1.13"
|
|
53
|
+
"tar": "^6.1.13",
|
|
54
|
+
"zod": "^3.22.4"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@types/js-yaml": "^4.0.5",
|
|
58
58
|
"@types/tar": "^6.1.4"
|
|
59
59
|
},
|
|
60
|
-
"gitHead": "
|
|
60
|
+
"gitHead": "c258fe5fd1c1ea78798a37a0866e574f5a29afdc"
|
|
61
61
|
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { z } from "zod";
|
|
2
2
|
|
|
3
|
-
export function
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
export function getAttributeZodSchema() {
|
|
4
|
+
const attributeZodSchema = z
|
|
5
|
+
.object({
|
|
6
|
+
archived: z.boolean().optional(),
|
|
7
|
+
type: z.enum(["boolean", "string", "integer", "double", "date", "semver"]),
|
|
8
|
+
description: z.string(),
|
|
9
|
+
capture: z.boolean().optional(),
|
|
10
|
+
})
|
|
11
|
+
.strict();
|
|
10
12
|
|
|
11
|
-
return
|
|
13
|
+
return attributeZodSchema;
|
|
12
14
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Group } from "@featurevisor/types";
|
|
2
|
+
|
|
3
|
+
import { Datasource } from "../datasource";
|
|
4
|
+
|
|
5
|
+
// @TODO: ideally in future, this check should be done from Feature level,
|
|
6
|
+
// as well as Group level as done here
|
|
7
|
+
export async function checkForFeatureExceedingGroupSlotPercentage(
|
|
8
|
+
datasource: Datasource,
|
|
9
|
+
group: Group,
|
|
10
|
+
availableFeatureKeys: string[],
|
|
11
|
+
) {
|
|
12
|
+
for (const slot of group.slots) {
|
|
13
|
+
const maxPercentageForRule = slot.percentage;
|
|
14
|
+
|
|
15
|
+
if (slot.feature) {
|
|
16
|
+
const featureKey = slot.feature;
|
|
17
|
+
const featureExists = availableFeatureKeys.indexOf(featureKey) > -1;
|
|
18
|
+
|
|
19
|
+
if (!featureExists) {
|
|
20
|
+
throw new Error(`Unknown feature "${featureKey}"`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const parsedFeature = await datasource.readFeature(featureKey);
|
|
24
|
+
|
|
25
|
+
const environmentKeys = Object.keys(parsedFeature.environments);
|
|
26
|
+
for (const environmentKey of environmentKeys) {
|
|
27
|
+
const environment = parsedFeature.environments[environmentKey];
|
|
28
|
+
const rules = environment.rules;
|
|
29
|
+
|
|
30
|
+
for (const rule of rules) {
|
|
31
|
+
if (rule.percentage > maxPercentageForRule) {
|
|
32
|
+
// @TODO: this does not help with same feature belonging to multiple slots. fix that.
|
|
33
|
+
throw new Error(
|
|
34
|
+
`Feature ${featureKey}'s rule ${rule.key} in ${environmentKey} has a percentage of ${rule.percentage} which is greater than the maximum percentage of ${maxPercentageForRule} for the slot`,
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -1,114 +1,137 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { z } from "zod";
|
|
2
2
|
|
|
3
3
|
import { ProjectConfig } from "../config";
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
const commonOperators: [string, ...string[]] = ["equals", "notEquals"];
|
|
6
|
+
const numericOperators = ["greaterThan", "greaterThanOrEquals", "lessThan", "lessThanOrEquals"];
|
|
7
|
+
const stringOperators = ["contains", "notContains", "startsWith", "endsWith"];
|
|
8
|
+
const semverOperators = [
|
|
9
|
+
"semverEquals",
|
|
10
|
+
"semverNotEquals",
|
|
11
|
+
"semverGreaterThan",
|
|
12
|
+
"semverGreaterThanOrEquals",
|
|
13
|
+
"semverLessThan",
|
|
14
|
+
"semverLessThanOrEquals",
|
|
15
|
+
];
|
|
16
|
+
const dateOperators = ["before", "after"];
|
|
17
|
+
const arrayOperators = ["in", "notIn"];
|
|
18
|
+
|
|
19
|
+
export function getConditionsZodSchema(
|
|
6
20
|
projectConfig: ProjectConfig,
|
|
7
|
-
availableAttributeKeys: string[],
|
|
21
|
+
availableAttributeKeys: [string, ...string[]],
|
|
8
22
|
) {
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
23
|
+
const plainConditionZodSchema = z
|
|
24
|
+
.object({
|
|
25
|
+
attribute: z.string().refine(
|
|
26
|
+
(value) => availableAttributeKeys.includes(value),
|
|
27
|
+
(value) => ({
|
|
28
|
+
message: `Unknown attribute "${value}"`,
|
|
29
|
+
}),
|
|
30
|
+
),
|
|
31
|
+
operator: z.enum([
|
|
32
|
+
...commonOperators,
|
|
33
|
+
...numericOperators,
|
|
34
|
+
...stringOperators,
|
|
35
|
+
...semverOperators,
|
|
36
|
+
...dateOperators,
|
|
37
|
+
...arrayOperators,
|
|
38
|
+
]),
|
|
39
|
+
value: z.union([
|
|
40
|
+
z.string(),
|
|
41
|
+
z.array(z.string()),
|
|
42
|
+
z.number(),
|
|
43
|
+
z.boolean(),
|
|
44
|
+
z.date(),
|
|
45
|
+
z.null(),
|
|
46
|
+
]),
|
|
47
|
+
})
|
|
48
|
+
.superRefine((data, context) => {
|
|
49
|
+
// common
|
|
50
|
+
if (
|
|
51
|
+
commonOperators.includes(data.operator) &&
|
|
52
|
+
!(
|
|
53
|
+
data.value === null ||
|
|
54
|
+
typeof data.value === "string" ||
|
|
55
|
+
typeof data.value === "number" ||
|
|
56
|
+
typeof data.value === "boolean" ||
|
|
57
|
+
data.value instanceof Date ||
|
|
58
|
+
data.value === null
|
|
59
|
+
)
|
|
60
|
+
) {
|
|
61
|
+
context.addIssue({
|
|
62
|
+
code: z.ZodIssueCode.custom,
|
|
63
|
+
message: `when operator is "${data.operator}", value has to be either a string, number, boolean, date or null`,
|
|
64
|
+
path: ["value"],
|
|
65
|
+
});
|
|
66
|
+
}
|
|
17
67
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
68
|
+
// numeric
|
|
69
|
+
if (numericOperators.includes(data.operator) && typeof data.value !== "number") {
|
|
70
|
+
context.addIssue({
|
|
71
|
+
code: z.ZodIssueCode.custom,
|
|
72
|
+
message: `when operator is "${data.operator}", value must be a number`,
|
|
73
|
+
path: ["value"],
|
|
74
|
+
});
|
|
75
|
+
}
|
|
23
76
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
77
|
+
// string
|
|
78
|
+
if (stringOperators.includes(data.operator) && typeof data.value !== "string") {
|
|
79
|
+
context.addIssue({
|
|
80
|
+
code: z.ZodIssueCode.custom,
|
|
81
|
+
message: `when operator is "${data.operator}", value must be a string`,
|
|
82
|
+
path: ["value"],
|
|
83
|
+
});
|
|
84
|
+
}
|
|
29
85
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
86
|
+
// semver
|
|
87
|
+
if (semverOperators.includes(data.operator) && typeof data.value !== "string") {
|
|
88
|
+
context.addIssue({
|
|
89
|
+
code: z.ZodIssueCode.custom,
|
|
90
|
+
message: `when operator is "${data.operator}", value must be a string`,
|
|
91
|
+
path: ["value"],
|
|
92
|
+
});
|
|
93
|
+
}
|
|
37
94
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
95
|
+
// date
|
|
96
|
+
if (dateOperators.includes(data.operator) && !(data.value instanceof Date)) {
|
|
97
|
+
context.addIssue({
|
|
98
|
+
code: z.ZodIssueCode.custom,
|
|
99
|
+
message: `when operator is "${data.operator}", value must be a date`,
|
|
100
|
+
path: ["value"],
|
|
101
|
+
});
|
|
102
|
+
}
|
|
41
103
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
.
|
|
55
|
-
|
|
56
|
-
"greaterThan",
|
|
57
|
-
"greaterThanOrEquals",
|
|
58
|
-
"lessThan",
|
|
59
|
-
"lessThanOrEquals",
|
|
60
|
-
),
|
|
61
|
-
then: Joi.number().required(),
|
|
62
|
-
})
|
|
63
|
-
.when("operator", {
|
|
64
|
-
is: Joi.string().valid("contains", "notContains", "startsWith", "endsWith"),
|
|
65
|
-
then: Joi.string().required(),
|
|
104
|
+
// array
|
|
105
|
+
if (arrayOperators.includes(data.operator) && !Array.isArray(data.value)) {
|
|
106
|
+
context.addIssue({
|
|
107
|
+
code: z.ZodIssueCode.custom,
|
|
108
|
+
message: `when operator is "${data.operator}", value must be an array of strings`,
|
|
109
|
+
path: ["value"],
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const andOrNotConditionZodSchema = z.union([
|
|
115
|
+
z
|
|
116
|
+
.object({
|
|
117
|
+
and: z.array(z.lazy(() => conditionZodSchema)),
|
|
66
118
|
})
|
|
67
|
-
.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
"semverGreaterThan",
|
|
72
|
-
"semverGreaterThanOrEquals",
|
|
73
|
-
"semverLessThan",
|
|
74
|
-
"semverLessThanOrEquals",
|
|
75
|
-
),
|
|
76
|
-
then: Joi.string().required(),
|
|
119
|
+
.strict(),
|
|
120
|
+
z
|
|
121
|
+
.object({
|
|
122
|
+
or: z.array(z.lazy(() => conditionZodSchema)),
|
|
77
123
|
})
|
|
78
|
-
.
|
|
79
|
-
|
|
80
|
-
|
|
124
|
+
.strict(),
|
|
125
|
+
z
|
|
126
|
+
.object({
|
|
127
|
+
not: z.array(z.lazy(() => conditionZodSchema)),
|
|
81
128
|
})
|
|
82
|
-
.
|
|
83
|
-
|
|
84
|
-
then: Joi.array().items(Joi.string()).required(),
|
|
85
|
-
}),
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
const andOrNotConditionJoiSchema = Joi.alternatives()
|
|
89
|
-
.try(
|
|
90
|
-
Joi.object({
|
|
91
|
-
and: Joi.array().items(Joi.link("#andOrNotCondition"), plainConditionJoiSchema),
|
|
92
|
-
}),
|
|
93
|
-
Joi.object({
|
|
94
|
-
or: Joi.array().items(Joi.link("#andOrNotCondition"), plainConditionJoiSchema),
|
|
95
|
-
}),
|
|
96
|
-
Joi.object({
|
|
97
|
-
// @TODO: allow plainConditionJoiSchema as well?
|
|
98
|
-
not: Joi.array().items(Joi.link("#andOrNotCondition"), plainConditionJoiSchema),
|
|
99
|
-
}),
|
|
100
|
-
)
|
|
101
|
-
.id("andOrNotCondition");
|
|
129
|
+
.strict(),
|
|
130
|
+
]);
|
|
102
131
|
|
|
103
|
-
const
|
|
104
|
-
andOrNotConditionJoiSchema,
|
|
105
|
-
plainConditionJoiSchema,
|
|
106
|
-
);
|
|
132
|
+
const conditionZodSchema = z.union([andOrNotConditionZodSchema, plainConditionZodSchema]);
|
|
107
133
|
|
|
108
|
-
const
|
|
109
|
-
conditionJoiSchema,
|
|
110
|
-
Joi.array().items(conditionJoiSchema),
|
|
111
|
-
);
|
|
134
|
+
const conditionsZodSchema = z.union([conditionZodSchema, z.array(conditionZodSchema)]);
|
|
112
135
|
|
|
113
|
-
return
|
|
136
|
+
return conditionsZodSchema;
|
|
114
137
|
}
|