@featurevisor/core 0.22.0 → 0.24.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/src/linter.ts CHANGED
@@ -20,9 +20,14 @@ export function getAttributeJoiSchema(projectConfig: ProjectConfig) {
20
20
  return attributeJoiSchema;
21
21
  }
22
22
 
23
- export function getConditionsJoiSchema(projectConfig: ProjectConfig) {
23
+ export function getConditionsJoiSchema(
24
+ projectConfig: ProjectConfig,
25
+ availableAttributeKeys: string[],
26
+ ) {
24
27
  const plainConditionJoiSchema = Joi.object({
25
- attribute: Joi.string().required(),
28
+ attribute: Joi.string()
29
+ .valid(...availableAttributeKeys)
30
+ .required(),
26
31
  operator: Joi.string()
27
32
  .valid(
28
33
  "equals",
@@ -107,13 +112,13 @@ export function getSegmentJoiSchema(projectConfig: ProjectConfig, conditionsJoiS
107
112
  return segmentJoiSchema;
108
113
  }
109
114
 
110
- export function getGroupJoiSchema(projectConfig: ProjectConfig) {
115
+ export function getGroupJoiSchema(projectConfig: ProjectConfig, availableFeatureKeys: string[]) {
111
116
  const groupJoiSchema = Joi.object({
112
117
  description: Joi.string().required(),
113
118
  slots: Joi.array()
114
119
  .items(
115
120
  Joi.object({
116
- feature: Joi.string(),
121
+ feature: Joi.string().valid(...availableFeatureKeys),
117
122
  percentage: Joi.number().precision(3).min(0).max(100),
118
123
  }),
119
124
  )
@@ -161,7 +166,11 @@ export function getGroupJoiSchema(projectConfig: ProjectConfig) {
161
166
  return groupJoiSchema;
162
167
  }
163
168
 
164
- export function getFeatureJoiSchema(projectConfig: ProjectConfig, conditionsJoiSchema) {
169
+ export function getFeatureJoiSchema(
170
+ projectConfig: ProjectConfig,
171
+ conditionsJoiSchema,
172
+ availableSegmentKeys: string[],
173
+ ) {
165
174
  const variationValueJoiSchema = Joi.alternatives().try(Joi.string(), Joi.number(), Joi.boolean());
166
175
  const variableValueJoiSchema = Joi.alternatives()
167
176
  .try(
@@ -187,7 +196,7 @@ export function getFeatureJoiSchema(projectConfig: ProjectConfig, conditionsJoiS
187
196
  )
188
197
  .allow("");
189
198
 
190
- const plainGroupSegment = Joi.string();
199
+ const plainGroupSegment = Joi.string().valid("*", ...availableSegmentKeys);
191
200
 
192
201
  const andOrNotGroupSegment = Joi.alternatives()
193
202
  .try(
@@ -363,6 +372,10 @@ export function printJoiError(e: Joi.ValidationError) {
363
372
  export async function lintProject(projectConfig: ProjectConfig): Promise<boolean> {
364
373
  let hasError = false;
365
374
 
375
+ const availableAttributeKeys: string[] = [];
376
+ const availableSegmentKeys: string[] = [];
377
+ const availableFeatureKeys: string[] = [];
378
+
366
379
  // lint attributes
367
380
  console.log("Linting attributes...\n");
368
381
  const attributeFilePaths = getYAMLFiles(path.join(projectConfig.attributesDirectoryPath));
@@ -371,6 +384,8 @@ export async function lintProject(projectConfig: ProjectConfig): Promise<boolean
371
384
  for (const filePath of attributeFilePaths) {
372
385
  const key = path.basename(filePath, ".yml");
373
386
  const parsed = parseYaml(fs.readFileSync(filePath, "utf8")) as any;
387
+ availableAttributeKeys.push(key);
388
+
374
389
  console.log(" =>", key);
375
390
 
376
391
  try {
@@ -389,12 +404,14 @@ export async function lintProject(projectConfig: ProjectConfig): Promise<boolean
389
404
  // lint segments
390
405
  console.log("\nLinting segments...\n");
391
406
  const segmentFilePaths = getYAMLFiles(path.join(projectConfig.segmentsDirectoryPath));
392
- const conditionsJoiSchema = getConditionsJoiSchema(projectConfig);
407
+ const conditionsJoiSchema = getConditionsJoiSchema(projectConfig, availableAttributeKeys);
393
408
  const segmentJoiSchema = getSegmentJoiSchema(projectConfig, conditionsJoiSchema);
394
409
 
395
410
  for (const filePath of segmentFilePaths) {
396
411
  const key = path.basename(filePath, ".yml");
397
412
  const parsed = parseYaml(fs.readFileSync(filePath, "utf8")) as any;
413
+ availableSegmentKeys.push(key);
414
+
398
415
  console.log(" =>", key);
399
416
 
400
417
  try {
@@ -414,7 +431,7 @@ export async function lintProject(projectConfig: ProjectConfig): Promise<boolean
414
431
  console.log("\nLinting groups...\n");
415
432
  if (fs.existsSync(projectConfig.groupsDirectoryPath)) {
416
433
  const groupFilePaths = getYAMLFiles(path.join(projectConfig.groupsDirectoryPath));
417
- const groupJoiSchema = getGroupJoiSchema(projectConfig);
434
+ const groupJoiSchema = getGroupJoiSchema(projectConfig, availableFeatureKeys);
418
435
 
419
436
  for (const filePath of groupFilePaths) {
420
437
  const key = path.basename(filePath, ".yml");
@@ -440,11 +457,17 @@ export async function lintProject(projectConfig: ProjectConfig): Promise<boolean
440
457
  // lint features
441
458
  console.log("\nLinting features...\n");
442
459
  const featureFilePaths = getYAMLFiles(path.join(projectConfig.featuresDirectoryPath));
443
- const featureJoiSchema = getFeatureJoiSchema(projectConfig, conditionsJoiSchema);
460
+ const featureJoiSchema = getFeatureJoiSchema(
461
+ projectConfig,
462
+ conditionsJoiSchema,
463
+ availableSegmentKeys,
464
+ );
444
465
 
445
466
  for (const filePath of featureFilePaths) {
446
467
  const key = path.basename(filePath, ".yml");
447
468
  const parsed = parseYaml(fs.readFileSync(filePath, "utf8")) as any;
469
+ availableFeatureKeys.push(key);
470
+
448
471
  console.log(" =>", key);
449
472
 
450
473
  try {
@@ -41,10 +41,7 @@ describe("core: Traffic", function () {
41
41
  {
42
42
  variation: "on",
43
43
  percentage: 80000,
44
- range: {
45
- start: 0,
46
- end: 80000,
47
- },
44
+ range: [0, 80000],
48
45
  },
49
46
  ],
50
47
  },
@@ -87,18 +84,12 @@ describe("core: Traffic", function () {
87
84
  {
88
85
  variation: "on",
89
86
  percentage: 40000,
90
- range: {
91
- start: 0,
92
- end: 40000,
93
- },
87
+ range: [0, 40000],
94
88
  },
95
89
  {
96
90
  variation: "off",
97
91
  percentage: 40000,
98
- range: {
99
- start: 40000,
100
- end: 80000,
101
- },
92
+ range: [40000, 80000],
102
93
  },
103
94
  ],
104
95
  },
@@ -145,26 +136,17 @@ describe("core: Traffic", function () {
145
136
  {
146
137
  variation: "yes",
147
138
  percentage: 33330,
148
- range: {
149
- start: 0,
150
- end: 33330,
151
- },
139
+ range: [0, 33330],
152
140
  },
153
141
  {
154
142
  variation: "no",
155
143
  percentage: 33330,
156
- range: {
157
- start: 33330,
158
- end: 66660,
159
- },
144
+ range: [33330, 66660],
160
145
  },
161
146
  {
162
147
  variation: "maybe",
163
148
  percentage: 33340,
164
- range: {
165
- start: 66660,
166
- end: 100000,
167
- },
149
+ range: [66660, 100000],
168
150
  },
169
151
  ],
170
152
  },
@@ -215,18 +197,12 @@ describe("core: Traffic", function () {
215
197
  {
216
198
  variation: "on",
217
199
  percentage: 40000,
218
- range: {
219
- start: 0,
220
- end: 40000,
221
- },
200
+ range: [0, 40000],
222
201
  },
223
202
  {
224
203
  variation: "off",
225
204
  percentage: 40000,
226
- range: {
227
- start: 40000,
228
- end: 80000,
229
- },
205
+ range: [40000, 80000],
230
206
  },
231
207
  ],
232
208
  },
@@ -244,36 +220,24 @@ describe("core: Traffic", function () {
244
220
  {
245
221
  variation: "on",
246
222
  percentage: 40000,
247
- range: {
248
- start: 0,
249
- end: 40000,
250
- },
223
+ range: [0, 40000],
251
224
  },
252
225
  {
253
226
  variation: "off",
254
227
  percentage: 40000,
255
- range: {
256
- start: 40000,
257
- end: 80000,
258
- },
228
+ range: [40000, 80000],
259
229
  },
260
230
 
261
231
  // new
262
232
  {
263
233
  variation: "on",
264
234
  percentage: 5000,
265
- range: {
266
- start: 80000,
267
- end: 85000,
268
- },
235
+ range: [80000, 85000],
269
236
  },
270
237
  {
271
238
  variation: "off",
272
239
  percentage: 5000,
273
- range: {
274
- start: 85000,
275
- end: 90000,
276
- },
240
+ range: [85000, 90000],
277
241
  },
278
242
  ],
279
243
  },
@@ -324,18 +288,12 @@ describe("core: Traffic", function () {
324
288
  {
325
289
  variation: "on",
326
290
  percentage: 40000,
327
- range: {
328
- start: 0,
329
- end: 40000,
330
- },
291
+ range: [0, 40000],
331
292
  },
332
293
  {
333
294
  variation: "off",
334
295
  percentage: 40000,
335
- range: {
336
- start: 40000,
337
- end: 80000,
338
- },
296
+ range: [40000, 80000],
339
297
  },
340
298
  ],
341
299
  },
@@ -352,18 +310,12 @@ describe("core: Traffic", function () {
352
310
  {
353
311
  variation: "on",
354
312
  percentage: 35000,
355
- range: {
356
- start: 0,
357
- end: 35000,
358
- },
313
+ range: [0, 35000],
359
314
  },
360
315
  {
361
316
  variation: "off",
362
317
  percentage: 35000,
363
- range: {
364
- start: 35000,
365
- end: 70000,
366
- },
318
+ range: [35000, 70000],
367
319
  },
368
320
  ],
369
321
  },
@@ -418,18 +370,12 @@ describe("core: Traffic", function () {
418
370
  {
419
371
  variation: "a",
420
372
  percentage: 40000,
421
- range: {
422
- start: 0,
423
- end: 40000,
424
- },
373
+ range: [0, 40000],
425
374
  },
426
375
  {
427
376
  variation: "b",
428
377
  percentage: 40000,
429
- range: {
430
- start: 40000,
431
- end: 80000,
432
- },
378
+ range: [40000, 80000],
433
379
  },
434
380
  ],
435
381
  },
@@ -446,26 +392,17 @@ describe("core: Traffic", function () {
446
392
  {
447
393
  variation: "a",
448
394
  percentage: 29997,
449
- range: {
450
- start: 0,
451
- end: 29997,
452
- },
395
+ range: [0, 29997],
453
396
  },
454
397
  {
455
398
  variation: "b",
456
399
  percentage: 29997,
457
- range: {
458
- start: 29997,
459
- end: 59994,
460
- },
400
+ range: [29997, 59994],
461
401
  },
462
402
  {
463
403
  variation: "c",
464
404
  percentage: 30006,
465
- range: {
466
- start: 59994,
467
- end: 90000,
468
- },
405
+ range: [59994, 90000],
469
406
  },
470
407
  ],
471
408
  },
@@ -524,18 +461,12 @@ describe("core: Traffic", function () {
524
461
  {
525
462
  variation: "a",
526
463
  percentage: 40000,
527
- range: {
528
- start: 0,
529
- end: 40000,
530
- },
464
+ range: [0, 40000],
531
465
  },
532
466
  {
533
467
  variation: "b",
534
468
  percentage: 40000,
535
- range: {
536
- start: 40000,
537
- end: 80000,
538
- },
469
+ range: [40000, 80000],
539
470
  },
540
471
  ],
541
472
  },
@@ -552,34 +483,22 @@ describe("core: Traffic", function () {
552
483
  {
553
484
  variation: "a",
554
485
  percentage: 25000,
555
- range: {
556
- start: 0,
557
- end: 25000,
558
- },
486
+ range: [0, 25000],
559
487
  },
560
488
  {
561
489
  variation: "b",
562
490
  percentage: 25000,
563
- range: {
564
- start: 25000,
565
- end: 50000,
566
- },
491
+ range: [25000, 50000],
567
492
  },
568
493
  {
569
494
  variation: "c",
570
495
  percentage: 25000,
571
- range: {
572
- start: 50000,
573
- end: 75000,
574
- },
496
+ range: [50000, 75000],
575
497
  },
576
498
  {
577
499
  variation: "d",
578
500
  percentage: 25000,
579
- range: {
580
- start: 75000,
581
- end: 100000,
582
- },
501
+ range: [75000, 100000],
583
502
  },
584
503
  ],
585
504
  },
@@ -638,34 +557,22 @@ describe("core: Traffic", function () {
638
557
  {
639
558
  variation: "a",
640
559
  percentage: 25000,
641
- range: {
642
- start: 0,
643
- end: 25000,
644
- },
560
+ range: [0, 25000],
645
561
  },
646
562
  {
647
563
  variation: "b",
648
564
  percentage: 25000,
649
- range: {
650
- start: 25000,
651
- end: 50000,
652
- },
565
+ range: [25000, 50000],
653
566
  },
654
567
  {
655
568
  variation: "c",
656
569
  percentage: 25000,
657
- range: {
658
- start: 50000,
659
- end: 75000,
660
- },
570
+ range: [50000, 75000],
661
571
  },
662
572
  {
663
573
  variation: "d",
664
574
  percentage: 25000,
665
- range: {
666
- start: 75000,
667
- end: 100000,
668
- },
575
+ range: [75000, 100000],
669
576
  },
670
577
  ],
671
578
  },
@@ -682,18 +589,12 @@ describe("core: Traffic", function () {
682
589
  {
683
590
  variation: "a",
684
591
  percentage: 50000,
685
- range: {
686
- start: 0,
687
- end: 50000,
688
- },
592
+ range: [0, 50000],
689
593
  },
690
594
  {
691
595
  variation: "b",
692
596
  percentage: 50000,
693
- range: {
694
- start: 50000,
695
- end: 100000,
696
- },
597
+ range: [50000, 100000],
697
598
  },
698
599
  ],
699
600
  },
@@ -752,34 +653,22 @@ describe("core: Traffic", function () {
752
653
  {
753
654
  variation: "a",
754
655
  percentage: 25000,
755
- range: {
756
- start: 0,
757
- end: 25000,
758
- },
656
+ range: [0, 25000],
759
657
  },
760
658
  {
761
659
  variation: "b",
762
660
  percentage: 25000,
763
- range: {
764
- start: 25000,
765
- end: 50000,
766
- },
661
+ range: [25000, 50000],
767
662
  },
768
663
  {
769
664
  variation: "c",
770
665
  percentage: 25000,
771
- range: {
772
- start: 50000,
773
- end: 75000,
774
- },
666
+ range: [50000, 75000],
775
667
  },
776
668
  {
777
669
  variation: "d",
778
670
  percentage: 25000,
779
- range: {
780
- start: 75000,
781
- end: 100000,
782
- },
671
+ range: [75000, 100000],
783
672
  },
784
673
  ],
785
674
  },
@@ -796,18 +685,12 @@ describe("core: Traffic", function () {
796
685
  {
797
686
  variation: "a",
798
687
  percentage: 40000,
799
- range: {
800
- start: 0,
801
- end: 40000,
802
- },
688
+ range: [0, 40000],
803
689
  },
804
690
  {
805
691
  variation: "b",
806
692
  percentage: 40000,
807
- range: {
808
- start: 40000,
809
- end: 80000,
810
- },
693
+ range: [40000, 80000],
811
694
  },
812
695
  ],
813
696
  },
package/src/traffic.ts CHANGED
@@ -1,5 +1,13 @@
1
- import { Rule, ExistingFeature, Traffic, Variation, Range, Percentage } from "@featurevisor/types";
2
- import { MAX_BUCKETED_NUMBER } from "@featurevisor/sdk";
1
+ import {
2
+ Rule,
3
+ ExistingFeature,
4
+ Traffic,
5
+ Variation,
6
+ Range,
7
+ Percentage,
8
+ RangeTuple,
9
+ } from "@featurevisor/types";
10
+ import { MAX_BUCKETED_NUMBER, getStartEndFromRange } from "@featurevisor/sdk";
3
11
 
4
12
  import { getAllocation, getUpdatedAvailableRangesAfterFilling } from "./allocator";
5
13
 
@@ -62,7 +70,7 @@ export function getTraffic(
62
70
 
63
71
  // @TODO: may be pass from builder directly?
64
72
  const availableRanges =
65
- ranges && ranges.length > 0 ? ranges : [{ start: 0, end: MAX_BUCKETED_NUMBER }];
73
+ ranges && ranges.length > 0 ? ranges : ([[0, MAX_BUCKETED_NUMBER]] as RangeTuple[]);
66
74
 
67
75
  parsedRules.forEach(function (parsedRule) {
68
76
  const rulePercentage = parsedRule.percentage; // 0 - 100
@@ -113,10 +121,7 @@ export function getTraffic(
113
121
  const result = {
114
122
  variation,
115
123
  percentage, // @TODO remove it in next breaking semver
116
- range: range || {
117
- start: lastEnd,
118
- end: percentage,
119
- },
124
+ range: range ? getStartEndFromRange(range) : ([lastEnd, percentage] as RangeTuple),
120
125
  };
121
126
 
122
127
  existingSum += percentage || 0;