@featurevisor/core 0.15.0 → 0.17.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/CHANGELOG.md +19 -0
- package/lib/allocator.d.ts +3 -0
- package/lib/allocator.js +40 -0
- package/lib/allocator.js.map +1 -0
- package/lib/allocator.spec.d.ts +1 -0
- package/lib/allocator.spec.js +85 -0
- package/lib/allocator.spec.js.map +1 -0
- package/lib/builder.js +58 -23
- package/lib/builder.js.map +1 -1
- package/lib/config.d.ts +2 -0
- package/lib/config.js +3 -1
- package/lib/config.js.map +1 -1
- package/lib/linter.d.ts +1 -0
- package/lib/linter.js +122 -41
- package/lib/linter.js.map +1 -1
- package/lib/tester.js +3 -8
- package/lib/tester.js.map +1 -1
- package/lib/traffic.d.ts +8 -2
- package/lib/traffic.js +99 -88
- package/lib/traffic.js.map +1 -1
- package/lib/traffic.spec.js +166 -14
- package/lib/traffic.spec.js.map +1 -1
- package/package.json +5 -5
- package/src/allocator.spec.ts +104 -0
- package/src/allocator.ts +44 -0
- package/src/builder.ts +53 -23
- package/src/config.ts +3 -0
- package/src/linter.ts +100 -16
- package/src/tester.ts +3 -8
- package/src/traffic.spec.ts +167 -16
- package/src/traffic.ts +123 -109
package/src/traffic.spec.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { getNewTraffic } from "./traffic";
|
|
1
|
+
import { getTraffic } from "./traffic";
|
|
3
2
|
|
|
4
3
|
describe("core: Traffic", function () {
|
|
5
4
|
it("should be a function", function () {
|
|
6
|
-
expect(typeof
|
|
5
|
+
expect(typeof getTraffic).toEqual("function");
|
|
7
6
|
});
|
|
8
7
|
|
|
9
8
|
it("should allocate traffic for 100-0 weight on two variations", function () {
|
|
10
|
-
const result =
|
|
9
|
+
const result = getTraffic(
|
|
11
10
|
// parsed variations from YAML
|
|
12
11
|
[
|
|
13
12
|
{
|
|
@@ -42,10 +41,10 @@ describe("core: Traffic", function () {
|
|
|
42
41
|
{
|
|
43
42
|
variation: "on",
|
|
44
43
|
percentage: 80000,
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
range: {
|
|
45
|
+
start: 0,
|
|
46
|
+
end: 80000,
|
|
47
|
+
},
|
|
49
48
|
},
|
|
50
49
|
],
|
|
51
50
|
},
|
|
@@ -53,7 +52,7 @@ describe("core: Traffic", function () {
|
|
|
53
52
|
});
|
|
54
53
|
|
|
55
54
|
it("should allocate traffic for 50-50 weight on two variations", function () {
|
|
56
|
-
const result =
|
|
55
|
+
const result = getTraffic(
|
|
57
56
|
// parsed variations from YAML
|
|
58
57
|
[
|
|
59
58
|
{
|
|
@@ -88,10 +87,18 @@ describe("core: Traffic", function () {
|
|
|
88
87
|
{
|
|
89
88
|
variation: "on",
|
|
90
89
|
percentage: 40000,
|
|
90
|
+
range: {
|
|
91
|
+
start: 0,
|
|
92
|
+
end: 40000,
|
|
93
|
+
},
|
|
91
94
|
},
|
|
92
95
|
{
|
|
93
96
|
variation: "off",
|
|
94
97
|
percentage: 40000,
|
|
98
|
+
range: {
|
|
99
|
+
start: 40000,
|
|
100
|
+
end: 80000,
|
|
101
|
+
},
|
|
95
102
|
},
|
|
96
103
|
],
|
|
97
104
|
},
|
|
@@ -99,7 +106,7 @@ describe("core: Traffic", function () {
|
|
|
99
106
|
});
|
|
100
107
|
|
|
101
108
|
it("should allocate traffic for weight with two decimal places among three variations", function () {
|
|
102
|
-
const result =
|
|
109
|
+
const result = getTraffic(
|
|
103
110
|
// parsed variations from YAML
|
|
104
111
|
[
|
|
105
112
|
{
|
|
@@ -138,14 +145,26 @@ describe("core: Traffic", function () {
|
|
|
138
145
|
{
|
|
139
146
|
variation: "yes",
|
|
140
147
|
percentage: 33330,
|
|
148
|
+
range: {
|
|
149
|
+
start: 0,
|
|
150
|
+
end: 33330,
|
|
151
|
+
},
|
|
141
152
|
},
|
|
142
153
|
{
|
|
143
154
|
variation: "no",
|
|
144
155
|
percentage: 33330,
|
|
156
|
+
range: {
|
|
157
|
+
start: 33330,
|
|
158
|
+
end: 66660,
|
|
159
|
+
},
|
|
145
160
|
},
|
|
146
161
|
{
|
|
147
162
|
variation: "maybe",
|
|
148
163
|
percentage: 33340,
|
|
164
|
+
range: {
|
|
165
|
+
start: 66660,
|
|
166
|
+
end: 100000,
|
|
167
|
+
},
|
|
149
168
|
},
|
|
150
169
|
],
|
|
151
170
|
},
|
|
@@ -153,7 +172,7 @@ describe("core: Traffic", function () {
|
|
|
153
172
|
});
|
|
154
173
|
|
|
155
174
|
it("should allocate against previous known allocation, increasing from 80% to 90%, with same variations and weight", function () {
|
|
156
|
-
const result =
|
|
175
|
+
const result = getTraffic(
|
|
157
176
|
// parsed variations from YAML
|
|
158
177
|
[
|
|
159
178
|
{
|
|
@@ -195,10 +214,18 @@ describe("core: Traffic", function () {
|
|
|
195
214
|
{
|
|
196
215
|
variation: "on",
|
|
197
216
|
percentage: 40000,
|
|
217
|
+
range: {
|
|
218
|
+
start: 0,
|
|
219
|
+
end: 40000,
|
|
220
|
+
},
|
|
198
221
|
},
|
|
199
222
|
{
|
|
200
223
|
variation: "off",
|
|
201
224
|
percentage: 40000,
|
|
225
|
+
range: {
|
|
226
|
+
start: 40000,
|
|
227
|
+
end: 80000,
|
|
228
|
+
},
|
|
202
229
|
},
|
|
203
230
|
],
|
|
204
231
|
},
|
|
@@ -216,20 +243,36 @@ describe("core: Traffic", function () {
|
|
|
216
243
|
{
|
|
217
244
|
variation: "on",
|
|
218
245
|
percentage: 40000,
|
|
246
|
+
range: {
|
|
247
|
+
start: 0,
|
|
248
|
+
end: 40000,
|
|
249
|
+
},
|
|
219
250
|
},
|
|
220
251
|
{
|
|
221
252
|
variation: "off",
|
|
222
253
|
percentage: 40000,
|
|
254
|
+
range: {
|
|
255
|
+
start: 40000,
|
|
256
|
+
end: 80000,
|
|
257
|
+
},
|
|
223
258
|
},
|
|
224
259
|
|
|
225
260
|
// new
|
|
226
261
|
{
|
|
227
262
|
variation: "on",
|
|
228
263
|
percentage: 5000,
|
|
264
|
+
range: {
|
|
265
|
+
start: 80000,
|
|
266
|
+
end: 85000,
|
|
267
|
+
},
|
|
229
268
|
},
|
|
230
269
|
{
|
|
231
270
|
variation: "off",
|
|
232
271
|
percentage: 5000,
|
|
272
|
+
range: {
|
|
273
|
+
start: 85000,
|
|
274
|
+
end: 90000,
|
|
275
|
+
},
|
|
233
276
|
},
|
|
234
277
|
],
|
|
235
278
|
},
|
|
@@ -237,7 +280,7 @@ describe("core: Traffic", function () {
|
|
|
237
280
|
});
|
|
238
281
|
|
|
239
282
|
it("should allocate against previous known allocation, decreasing from 80% to 70%, with same variations and weight", function () {
|
|
240
|
-
const result =
|
|
283
|
+
const result = getTraffic(
|
|
241
284
|
// parsed variations from YAML
|
|
242
285
|
[
|
|
243
286
|
{
|
|
@@ -279,10 +322,18 @@ describe("core: Traffic", function () {
|
|
|
279
322
|
{
|
|
280
323
|
variation: "on",
|
|
281
324
|
percentage: 40000,
|
|
325
|
+
range: {
|
|
326
|
+
start: 0,
|
|
327
|
+
end: 40000,
|
|
328
|
+
},
|
|
282
329
|
},
|
|
283
330
|
{
|
|
284
331
|
variation: "off",
|
|
285
332
|
percentage: 40000,
|
|
333
|
+
range: {
|
|
334
|
+
start: 40000,
|
|
335
|
+
end: 80000,
|
|
336
|
+
},
|
|
286
337
|
},
|
|
287
338
|
],
|
|
288
339
|
},
|
|
@@ -299,10 +350,18 @@ describe("core: Traffic", function () {
|
|
|
299
350
|
{
|
|
300
351
|
variation: "on",
|
|
301
352
|
percentage: 35000,
|
|
353
|
+
range: {
|
|
354
|
+
start: 0,
|
|
355
|
+
end: 35000,
|
|
356
|
+
},
|
|
302
357
|
},
|
|
303
358
|
{
|
|
304
359
|
variation: "off",
|
|
305
360
|
percentage: 35000,
|
|
361
|
+
range: {
|
|
362
|
+
start: 35000,
|
|
363
|
+
end: 70000,
|
|
364
|
+
},
|
|
306
365
|
},
|
|
307
366
|
],
|
|
308
367
|
},
|
|
@@ -310,7 +369,7 @@ describe("core: Traffic", function () {
|
|
|
310
369
|
});
|
|
311
370
|
|
|
312
371
|
it("should allocate against previous known allocation, increasing from 80% to 90%, with new added variation", function () {
|
|
313
|
-
const result =
|
|
372
|
+
const result = getTraffic(
|
|
314
373
|
// parsed variations from YAML
|
|
315
374
|
[
|
|
316
375
|
{
|
|
@@ -356,10 +415,18 @@ describe("core: Traffic", function () {
|
|
|
356
415
|
{
|
|
357
416
|
variation: "a",
|
|
358
417
|
percentage: 40000,
|
|
418
|
+
range: {
|
|
419
|
+
start: 0,
|
|
420
|
+
end: 40000,
|
|
421
|
+
},
|
|
359
422
|
},
|
|
360
423
|
{
|
|
361
424
|
variation: "b",
|
|
362
425
|
percentage: 40000,
|
|
426
|
+
range: {
|
|
427
|
+
start: 40000,
|
|
428
|
+
end: 80000,
|
|
429
|
+
},
|
|
363
430
|
},
|
|
364
431
|
],
|
|
365
432
|
},
|
|
@@ -376,14 +443,26 @@ describe("core: Traffic", function () {
|
|
|
376
443
|
{
|
|
377
444
|
variation: "a",
|
|
378
445
|
percentage: 29997,
|
|
446
|
+
range: {
|
|
447
|
+
start: 0,
|
|
448
|
+
end: 29997,
|
|
449
|
+
},
|
|
379
450
|
},
|
|
380
451
|
{
|
|
381
452
|
variation: "b",
|
|
382
453
|
percentage: 29997,
|
|
454
|
+
range: {
|
|
455
|
+
start: 29997,
|
|
456
|
+
end: 59994,
|
|
457
|
+
},
|
|
383
458
|
},
|
|
384
459
|
{
|
|
385
460
|
variation: "c",
|
|
386
461
|
percentage: 30006,
|
|
462
|
+
range: {
|
|
463
|
+
start: 59994,
|
|
464
|
+
end: 90000,
|
|
465
|
+
},
|
|
387
466
|
},
|
|
388
467
|
],
|
|
389
468
|
},
|
|
@@ -391,7 +470,7 @@ describe("core: Traffic", function () {
|
|
|
391
470
|
});
|
|
392
471
|
|
|
393
472
|
it("should allocate against previous known allocation, increasing from 80% to 100%, with new added variation, totalling 4 variations", function () {
|
|
394
|
-
const result =
|
|
473
|
+
const result = getTraffic(
|
|
395
474
|
// parsed variations from YAML
|
|
396
475
|
[
|
|
397
476
|
{
|
|
@@ -441,10 +520,18 @@ describe("core: Traffic", function () {
|
|
|
441
520
|
{
|
|
442
521
|
variation: "a",
|
|
443
522
|
percentage: 40000,
|
|
523
|
+
range: {
|
|
524
|
+
start: 0,
|
|
525
|
+
end: 40000,
|
|
526
|
+
},
|
|
444
527
|
},
|
|
445
528
|
{
|
|
446
529
|
variation: "b",
|
|
447
530
|
percentage: 40000,
|
|
531
|
+
range: {
|
|
532
|
+
start: 40000,
|
|
533
|
+
end: 80000,
|
|
534
|
+
},
|
|
448
535
|
},
|
|
449
536
|
],
|
|
450
537
|
},
|
|
@@ -461,18 +548,34 @@ describe("core: Traffic", function () {
|
|
|
461
548
|
{
|
|
462
549
|
variation: "a",
|
|
463
550
|
percentage: 25000,
|
|
551
|
+
range: {
|
|
552
|
+
start: 0,
|
|
553
|
+
end: 25000,
|
|
554
|
+
},
|
|
464
555
|
},
|
|
465
556
|
{
|
|
466
557
|
variation: "b",
|
|
467
558
|
percentage: 25000,
|
|
559
|
+
range: {
|
|
560
|
+
start: 25000,
|
|
561
|
+
end: 50000,
|
|
562
|
+
},
|
|
468
563
|
},
|
|
469
564
|
{
|
|
470
565
|
variation: "c",
|
|
471
566
|
percentage: 25000,
|
|
567
|
+
range: {
|
|
568
|
+
start: 50000,
|
|
569
|
+
end: 75000,
|
|
570
|
+
},
|
|
472
571
|
},
|
|
473
572
|
{
|
|
474
573
|
variation: "d",
|
|
475
574
|
percentage: 25000,
|
|
575
|
+
range: {
|
|
576
|
+
start: 75000,
|
|
577
|
+
end: 100000,
|
|
578
|
+
},
|
|
476
579
|
},
|
|
477
580
|
],
|
|
478
581
|
},
|
|
@@ -480,7 +583,7 @@ describe("core: Traffic", function () {
|
|
|
480
583
|
});
|
|
481
584
|
|
|
482
585
|
it("should allocate against previous known allocation, staying at same 100%, removing variations from 4 to 2", function () {
|
|
483
|
-
const result =
|
|
586
|
+
const result = getTraffic(
|
|
484
587
|
// parsed variations from YAML
|
|
485
588
|
[
|
|
486
589
|
{
|
|
@@ -530,18 +633,34 @@ describe("core: Traffic", function () {
|
|
|
530
633
|
{
|
|
531
634
|
variation: "a",
|
|
532
635
|
percentage: 25000,
|
|
636
|
+
range: {
|
|
637
|
+
start: 0,
|
|
638
|
+
end: 25000,
|
|
639
|
+
},
|
|
533
640
|
},
|
|
534
641
|
{
|
|
535
642
|
variation: "b",
|
|
536
643
|
percentage: 25000,
|
|
644
|
+
range: {
|
|
645
|
+
start: 25000,
|
|
646
|
+
end: 50000,
|
|
647
|
+
},
|
|
537
648
|
},
|
|
538
649
|
{
|
|
539
650
|
variation: "c",
|
|
540
651
|
percentage: 25000,
|
|
652
|
+
range: {
|
|
653
|
+
start: 50000,
|
|
654
|
+
end: 75000,
|
|
655
|
+
},
|
|
541
656
|
},
|
|
542
657
|
{
|
|
543
658
|
variation: "d",
|
|
544
659
|
percentage: 25000,
|
|
660
|
+
range: {
|
|
661
|
+
start: 75000,
|
|
662
|
+
end: 100000,
|
|
663
|
+
},
|
|
545
664
|
},
|
|
546
665
|
],
|
|
547
666
|
},
|
|
@@ -558,10 +677,18 @@ describe("core: Traffic", function () {
|
|
|
558
677
|
{
|
|
559
678
|
variation: "a",
|
|
560
679
|
percentage: 50000,
|
|
680
|
+
range: {
|
|
681
|
+
start: 0,
|
|
682
|
+
end: 50000,
|
|
683
|
+
},
|
|
561
684
|
},
|
|
562
685
|
{
|
|
563
686
|
variation: "b",
|
|
564
687
|
percentage: 50000,
|
|
688
|
+
range: {
|
|
689
|
+
start: 50000,
|
|
690
|
+
end: 100000,
|
|
691
|
+
},
|
|
565
692
|
},
|
|
566
693
|
],
|
|
567
694
|
},
|
|
@@ -569,7 +696,7 @@ describe("core: Traffic", function () {
|
|
|
569
696
|
});
|
|
570
697
|
|
|
571
698
|
it("should allocate against previous known allocation, decreasing from 100% to 80%, removing variations from 4 to 2", function () {
|
|
572
|
-
const result =
|
|
699
|
+
const result = getTraffic(
|
|
573
700
|
// parsed variations from YAML
|
|
574
701
|
[
|
|
575
702
|
{
|
|
@@ -619,18 +746,34 @@ describe("core: Traffic", function () {
|
|
|
619
746
|
{
|
|
620
747
|
variation: "a",
|
|
621
748
|
percentage: 25000,
|
|
749
|
+
range: {
|
|
750
|
+
start: 0,
|
|
751
|
+
end: 25000,
|
|
752
|
+
},
|
|
622
753
|
},
|
|
623
754
|
{
|
|
624
755
|
variation: "b",
|
|
625
756
|
percentage: 25000,
|
|
757
|
+
range: {
|
|
758
|
+
start: 25000,
|
|
759
|
+
end: 50000,
|
|
760
|
+
},
|
|
626
761
|
},
|
|
627
762
|
{
|
|
628
763
|
variation: "c",
|
|
629
764
|
percentage: 25000,
|
|
765
|
+
range: {
|
|
766
|
+
start: 50000,
|
|
767
|
+
end: 75000,
|
|
768
|
+
},
|
|
630
769
|
},
|
|
631
770
|
{
|
|
632
771
|
variation: "d",
|
|
633
772
|
percentage: 25000,
|
|
773
|
+
range: {
|
|
774
|
+
start: 75000,
|
|
775
|
+
end: 100000,
|
|
776
|
+
},
|
|
634
777
|
},
|
|
635
778
|
],
|
|
636
779
|
},
|
|
@@ -647,10 +790,18 @@ describe("core: Traffic", function () {
|
|
|
647
790
|
{
|
|
648
791
|
variation: "a",
|
|
649
792
|
percentage: 40000,
|
|
793
|
+
range: {
|
|
794
|
+
start: 0,
|
|
795
|
+
end: 40000,
|
|
796
|
+
},
|
|
650
797
|
},
|
|
651
798
|
{
|
|
652
799
|
variation: "b",
|
|
653
800
|
percentage: 40000,
|
|
801
|
+
range: {
|
|
802
|
+
start: 40000,
|
|
803
|
+
end: 80000,
|
|
804
|
+
},
|
|
654
805
|
},
|
|
655
806
|
],
|
|
656
807
|
},
|
package/src/traffic.ts
CHANGED
|
@@ -1,140 +1,154 @@
|
|
|
1
|
-
import { Rule, ExistingFeature, Traffic, Variation } from "@featurevisor/types";
|
|
1
|
+
import { Rule, ExistingFeature, Traffic, Variation, Range, Percentage } from "@featurevisor/types";
|
|
2
2
|
import { MAX_BUCKETED_NUMBER } from "@featurevisor/sdk";
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
import { getAllocation, getUpdatedAvailableRangesAfterFilling } from "./allocator";
|
|
5
|
+
|
|
6
|
+
export function detectIfVariationsChanged(
|
|
7
|
+
yamlVariations: Variation[], // as exists in latest YAML
|
|
8
|
+
existingFeature?: ExistingFeature, // from state file
|
|
9
|
+
): boolean {
|
|
10
|
+
if (!existingFeature) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
JSON.stringify(
|
|
16
|
+
existingFeature.variations.map(({ value, weight }) => ({
|
|
17
|
+
value,
|
|
18
|
+
weight,
|
|
19
|
+
})),
|
|
20
|
+
) !== JSON.stringify(yamlVariations.map(({ value, weight }) => ({ value, weight })))
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function getRulePercentageDiff(
|
|
25
|
+
trafficPercentage: Percentage, // 0 to 100k
|
|
26
|
+
existingTrafficRule,
|
|
27
|
+
): number {
|
|
28
|
+
if (!existingTrafficRule) {
|
|
29
|
+
return 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const existingPercentage = existingTrafficRule.percentage;
|
|
33
|
+
|
|
34
|
+
return trafficPercentage - existingPercentage;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function detectIfRangesChanged(
|
|
38
|
+
availableRanges: Range[], // as exists in latest YAML
|
|
39
|
+
existingFeature?: ExistingFeature, // from state file
|
|
40
|
+
): boolean {
|
|
41
|
+
if (!existingFeature) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!existingFeature.ranges) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return JSON.stringify(existingFeature.ranges) !== JSON.stringify(availableRanges);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function getTraffic(
|
|
5
53
|
// from current YAML
|
|
6
54
|
variations: Variation[],
|
|
7
55
|
parsedRules: Rule[],
|
|
8
|
-
|
|
9
56
|
// from previous release
|
|
10
57
|
existingFeature: ExistingFeature | undefined,
|
|
58
|
+
// ranges from group slots
|
|
59
|
+
ranges?: Range[],
|
|
11
60
|
): Traffic[] {
|
|
12
61
|
const result: Traffic[] = [];
|
|
13
62
|
|
|
14
|
-
|
|
15
|
-
|
|
63
|
+
// @TODO: may be pass from builder directly?
|
|
64
|
+
const availableRanges =
|
|
65
|
+
ranges && ranges.length > 0 ? ranges : [{ start: 0, end: MAX_BUCKETED_NUMBER }];
|
|
66
|
+
|
|
67
|
+
parsedRules.forEach(function (parsedRule) {
|
|
68
|
+
const rulePercentage = parsedRule.percentage; // 0 - 100
|
|
16
69
|
|
|
17
70
|
const traffic: Traffic = {
|
|
18
|
-
key:
|
|
71
|
+
key: parsedRule.key, // @TODO: not needed in datafile. keep it for now
|
|
19
72
|
segments:
|
|
20
|
-
typeof
|
|
21
|
-
? JSON.stringify(
|
|
22
|
-
:
|
|
23
|
-
percentage:
|
|
73
|
+
typeof parsedRule.segments !== "string"
|
|
74
|
+
? JSON.stringify(parsedRule.segments)
|
|
75
|
+
: parsedRule.segments,
|
|
76
|
+
percentage: rulePercentage * (MAX_BUCKETED_NUMBER / 100),
|
|
24
77
|
allocation: [],
|
|
25
78
|
};
|
|
26
79
|
|
|
27
|
-
|
|
28
|
-
|
|
80
|
+
// overrides
|
|
81
|
+
if (parsedRule.variables) {
|
|
82
|
+
traffic.variables = parsedRule.variables;
|
|
29
83
|
}
|
|
30
84
|
|
|
31
|
-
if (
|
|
32
|
-
traffic.variation =
|
|
85
|
+
if (parsedRule.variation) {
|
|
86
|
+
traffic.variation = parsedRule.variation;
|
|
33
87
|
}
|
|
34
88
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
89
|
+
// detect changes
|
|
90
|
+
const variationsChanged = detectIfVariationsChanged(variations, existingFeature);
|
|
91
|
+
const existingTrafficRule = existingFeature?.traffic.find((t) => t.key === parsedRule.key);
|
|
92
|
+
const rulePercentageDiff = getRulePercentageDiff(traffic.percentage, existingTrafficRule);
|
|
93
|
+
const rangesChanged = detectIfRangesChanged(availableRanges, existingFeature);
|
|
94
|
+
|
|
95
|
+
const needsRebucketing =
|
|
96
|
+
!existingTrafficRule || // new rule
|
|
97
|
+
variationsChanged || // variations changed
|
|
98
|
+
rulePercentageDiff <= 0 || // percentage decreased
|
|
99
|
+
rangesChanged; // belongs to a group, and group ranges changed
|
|
100
|
+
|
|
101
|
+
let updatedAvailableRanges = JSON.parse(JSON.stringify(availableRanges));
|
|
102
|
+
|
|
103
|
+
let lastEnd = 0;
|
|
104
|
+
if (existingTrafficRule && !needsRebucketing) {
|
|
105
|
+
// increase: build on top of existing allocations
|
|
106
|
+
let existingSum = 0;
|
|
107
|
+
|
|
108
|
+
traffic.allocation = existingTrafficRule.allocation.map(function ({
|
|
109
|
+
variation,
|
|
110
|
+
percentage, // @TODO: remove it in next breaking semver
|
|
111
|
+
range,
|
|
112
|
+
}) {
|
|
113
|
+
const result = {
|
|
114
|
+
variation,
|
|
115
|
+
percentage, // @TODO remove it in next breaking semver
|
|
116
|
+
range: range || {
|
|
117
|
+
start: lastEnd,
|
|
118
|
+
end: percentage,
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
existingSum += percentage || 0;
|
|
123
|
+
lastEnd = lastEnd + (percentage || 0);
|
|
124
|
+
|
|
125
|
+
return result;
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
updatedAvailableRanges = getUpdatedAvailableRangesAfterFilling(availableRanges, existingSum);
|
|
74
129
|
}
|
|
75
130
|
|
|
76
|
-
variations.forEach((variation)
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
((variation.weight as number) / 100) *
|
|
80
|
-
rolloutPercentage *
|
|
81
|
-
(MAX_BUCKETED_NUMBER / 100)
|
|
82
|
-
).toFixed(2),
|
|
83
|
-
);
|
|
131
|
+
variations.forEach(function (variation) {
|
|
132
|
+
const weight = variation.weight as number;
|
|
133
|
+
const percentage = weight * (MAX_BUCKETED_NUMBER / 100);
|
|
84
134
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
});
|
|
135
|
+
let toFillValue = needsRebucketing
|
|
136
|
+
? percentage * (rulePercentage / 100) // whole value
|
|
137
|
+
: (weight / 100) * rulePercentageDiff; // incrementing
|
|
138
|
+
const rangesToFill = getAllocation(updatedAvailableRanges, toFillValue);
|
|
90
139
|
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// const prevTotalWeightForVariation = existingTrafficRollout.allocation
|
|
95
|
-
// .filter((a) => a.variation === variation.value)
|
|
96
|
-
// .reduce((acc, curr) => acc + curr.percentage, 0);
|
|
97
|
-
|
|
98
|
-
// const diffWeightForVariation = (variation.weight as number) - prevTotalWeightForVariation / (MAX_BUCKETED_NUMBER / 100));
|
|
99
|
-
|
|
100
|
-
if (diffPercentage === 0) {
|
|
101
|
-
// no change
|
|
102
|
-
traffic.allocation.push({
|
|
103
|
-
variation: variation.value,
|
|
104
|
-
percentage: newPercentage,
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (diffPercentage > 0) {
|
|
111
|
-
// increase - need to consistently bucket
|
|
140
|
+
rangesToFill.forEach(function (range) {
|
|
112
141
|
traffic.allocation.push({
|
|
113
142
|
variation: variation.value,
|
|
114
|
-
percentage:
|
|
115
|
-
|
|
116
|
-
(variation.weight as number) *
|
|
117
|
-
(diffPercentage / 100) *
|
|
118
|
-
(MAX_BUCKETED_NUMBER / 100)
|
|
119
|
-
).toFixed(2),
|
|
120
|
-
),
|
|
143
|
+
percentage: toFillValue, // @TODO remove it in next breaking semver
|
|
144
|
+
range,
|
|
121
145
|
});
|
|
146
|
+
});
|
|
122
147
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
// decrease - need to re-bucket
|
|
128
|
-
|
|
129
|
-
// @TODO: can we maintain as much pre bucketed values as possible? to be close to consistent bucketing?
|
|
130
|
-
|
|
131
|
-
traffic.allocation.push({
|
|
132
|
-
variation: variation.value,
|
|
133
|
-
percentage: newPercentage,
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
148
|
+
updatedAvailableRanges = getUpdatedAvailableRangesAfterFilling(
|
|
149
|
+
updatedAvailableRanges,
|
|
150
|
+
toFillValue,
|
|
151
|
+
);
|
|
138
152
|
});
|
|
139
153
|
|
|
140
154
|
result.push(traffic);
|