@featurevisor/core 2.18.0 → 2.20.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 +22 -0
- package/coverage/clover.xml +1581 -1119
- package/coverage/coverage-final.json +29 -22
- package/coverage/lcov-report/builder/allocator.ts.html +26 -26
- package/coverage/lcov-report/builder/buildDatafile.ts.html +2017 -0
- package/coverage/lcov-report/builder/buildScopedConditions.ts.html +35 -35
- package/coverage/lcov-report/builder/buildScopedDatafile.ts.html +213 -45
- package/coverage/lcov-report/builder/buildScopedSegments.ts.html +28 -28
- package/coverage/lcov-report/builder/convertToV1.ts.html +583 -0
- package/coverage/lcov-report/builder/getFeatureRanges.ts.html +268 -0
- package/coverage/lcov-report/builder/hashes.ts.html +412 -0
- package/coverage/lcov-report/builder/index.html +90 -30
- package/coverage/lcov-report/builder/mutateVariables.ts.html +66 -45
- package/coverage/lcov-report/builder/mutator.ts.html +95 -95
- package/coverage/lcov-report/builder/revision.ts.html +1 -1
- package/coverage/lcov-report/builder/traffic.ts.html +60 -57
- package/coverage/lcov-report/config/index.html +1 -1
- package/coverage/lcov-report/config/index.ts.html +2 -2
- package/coverage/lcov-report/config/projectConfig.ts.html +28 -28
- package/coverage/lcov-report/datasource/adapter.ts.html +2 -2
- package/coverage/lcov-report/datasource/datasource.ts.html +4 -4
- package/coverage/lcov-report/datasource/filesystemAdapter.ts.html +34 -34
- package/coverage/lcov-report/datasource/index.html +1 -1
- package/coverage/lcov-report/datasource/index.ts.html +1 -1
- package/coverage/lcov-report/index.html +39 -39
- package/coverage/lcov-report/linter/attributeSchema.ts.html +1 -1
- package/coverage/lcov-report/linter/checkCircularDependency.ts.html +1 -1
- package/coverage/lcov-report/linter/checkPercentageExceedingSlot.ts.html +1 -1
- package/coverage/lcov-report/linter/conditionSchema.ts.html +17 -17
- package/coverage/lcov-report/linter/featureSchema.ts.html +686 -401
- package/coverage/lcov-report/linter/groupSchema.ts.html +1 -1
- package/coverage/lcov-report/linter/index.html +27 -27
- package/coverage/lcov-report/linter/lintProject.ts.html +25 -25
- package/coverage/lcov-report/linter/mutationNotation.ts.html +191 -122
- package/coverage/lcov-report/linter/printError.ts.html +1 -1
- package/coverage/lcov-report/linter/schema.ts.html +72 -72
- package/coverage/lcov-report/linter/segmentSchema.ts.html +1 -1
- package/coverage/lcov-report/linter/testSchema.ts.html +5 -5
- package/coverage/lcov-report/list/index.html +1 -1
- package/coverage/lcov-report/list/matrix.ts.html +1 -1
- package/coverage/lcov-report/parsers/index.html +1 -1
- package/coverage/lcov-report/parsers/index.ts.html +4 -4
- package/coverage/lcov-report/parsers/json.ts.html +2 -2
- package/coverage/lcov-report/parsers/yml.ts.html +6 -6
- package/coverage/lcov-report/tester/cliFormat.ts.html +1 -1
- package/coverage/lcov-report/tester/helpers.ts.html +1 -1
- package/coverage/lcov-report/tester/index.html +1 -1
- package/coverage/lcov-report/utils/extractKeys.ts.html +493 -0
- package/coverage/lcov-report/utils/git.ts.html +3 -3
- package/coverage/lcov-report/utils/index.html +54 -9
- package/coverage/lcov-report/utils/index.ts.html +94 -0
- package/coverage/lcov-report/utils/pretty.ts.html +121 -0
- package/coverage/lcov.info +2955 -2095
- package/lib/builder/buildDatafile.js +50 -3
- package/lib/builder/buildDatafile.js.map +1 -1
- package/lib/builder/buildDatafile.spec.d.ts +1 -0
- package/lib/builder/buildDatafile.spec.js +233 -0
- package/lib/builder/buildDatafile.spec.js.map +1 -0
- package/lib/builder/buildScopedDatafile.js +37 -1
- package/lib/builder/buildScopedDatafile.js.map +1 -1
- package/lib/builder/buildScopedDatafile.spec.js +289 -0
- package/lib/builder/buildScopedDatafile.spec.js.map +1 -1
- package/lib/builder/mutateVariables.d.ts +1 -1
- package/lib/builder/mutateVariables.js +4 -1
- package/lib/builder/mutateVariables.js.map +1 -1
- package/lib/builder/mutateVariables.spec.js +29 -0
- package/lib/builder/mutateVariables.spec.js.map +1 -1
- package/lib/builder/traffic.js +1 -0
- package/lib/builder/traffic.js.map +1 -1
- package/lib/builder/traffic.spec.js +26 -0
- package/lib/builder/traffic.spec.js.map +1 -1
- package/lib/generate-code/typescript.js +1 -1
- package/lib/generate-code/typescript.js.map +1 -1
- package/lib/linter/featureSchema.d.ts +228 -22
- package/lib/linter/featureSchema.js +137 -76
- package/lib/linter/featureSchema.js.map +1 -1
- package/lib/linter/featureSchema.spec.js +175 -0
- package/lib/linter/featureSchema.spec.js.map +1 -1
- package/lib/linter/mutationNotation.js +35 -10
- package/lib/linter/mutationNotation.js.map +1 -1
- package/lib/linter/mutationNotation.spec.js +31 -0
- package/lib/linter/mutationNotation.spec.js.map +1 -1
- package/package.json +5 -5
- package/src/builder/buildDatafile.spec.ts +267 -0
- package/src/builder/buildDatafile.ts +85 -4
- package/src/builder/buildScopedDatafile.spec.ts +330 -0
- package/src/builder/buildScopedDatafile.ts +57 -1
- package/src/builder/mutateVariables.spec.ts +45 -0
- package/src/builder/mutateVariables.ts +7 -0
- package/src/builder/traffic.spec.ts +33 -0
- package/src/builder/traffic.ts +1 -0
- package/src/generate-code/typescript.ts +1 -1
- package/src/linter/featureSchema.spec.ts +197 -0
- package/src/linter/featureSchema.ts +204 -109
- package/src/linter/mutationNotation.spec.ts +39 -0
- package/src/linter/mutationNotation.ts +30 -7
|
@@ -956,6 +956,336 @@ describe("core: buildScopedDatafile", function () {
|
|
|
956
956
|
});
|
|
957
957
|
});
|
|
958
958
|
|
|
959
|
+
describe("features with traffic and variableOverrides", function () {
|
|
960
|
+
test("traffic variableOverride with matching segments becomes *", function () {
|
|
961
|
+
const datafile: DatafileContent = {
|
|
962
|
+
schemaVersion: "2",
|
|
963
|
+
revision: "unknown",
|
|
964
|
+
segments: {
|
|
965
|
+
mobile: {
|
|
966
|
+
conditions: [{ attribute: "device", operator: "equals", value: "mobile" }],
|
|
967
|
+
},
|
|
968
|
+
},
|
|
969
|
+
features: {
|
|
970
|
+
feature1: {
|
|
971
|
+
bucketBy: ["userId"],
|
|
972
|
+
traffic: [
|
|
973
|
+
{
|
|
974
|
+
key: "rule1",
|
|
975
|
+
segments: "*",
|
|
976
|
+
percentage: 100000,
|
|
977
|
+
variableOverrides: {
|
|
978
|
+
config: [{ segments: "mobile", value: { source: "mobile" } }],
|
|
979
|
+
},
|
|
980
|
+
},
|
|
981
|
+
],
|
|
982
|
+
},
|
|
983
|
+
},
|
|
984
|
+
};
|
|
985
|
+
|
|
986
|
+
const result = buildScopedDatafile(datafile, { device: "mobile" });
|
|
987
|
+
|
|
988
|
+
expect(result.features.feature1.traffic[0].variableOverrides?.config[0].segments).toEqual(
|
|
989
|
+
"*",
|
|
990
|
+
);
|
|
991
|
+
});
|
|
992
|
+
|
|
993
|
+
test("traffic variableOverride with non-matching segments remains", function () {
|
|
994
|
+
const datafile: DatafileContent = {
|
|
995
|
+
schemaVersion: "2",
|
|
996
|
+
revision: "unknown",
|
|
997
|
+
segments: {
|
|
998
|
+
mobile: {
|
|
999
|
+
conditions: [{ attribute: "device", operator: "equals", value: "mobile" }],
|
|
1000
|
+
},
|
|
1001
|
+
},
|
|
1002
|
+
features: {
|
|
1003
|
+
feature1: {
|
|
1004
|
+
bucketBy: ["userId"],
|
|
1005
|
+
traffic: [
|
|
1006
|
+
{
|
|
1007
|
+
key: "rule1",
|
|
1008
|
+
segments: "*",
|
|
1009
|
+
percentage: 100000,
|
|
1010
|
+
variableOverrides: {
|
|
1011
|
+
config: [{ segments: "mobile", value: { source: "mobile" } }],
|
|
1012
|
+
},
|
|
1013
|
+
},
|
|
1014
|
+
],
|
|
1015
|
+
},
|
|
1016
|
+
},
|
|
1017
|
+
};
|
|
1018
|
+
|
|
1019
|
+
const result = buildScopedDatafile(datafile, { device: "desktop" });
|
|
1020
|
+
|
|
1021
|
+
expect(result.features.feature1.traffic[0].variableOverrides?.config[0].segments).toEqual(
|
|
1022
|
+
"mobile",
|
|
1023
|
+
);
|
|
1024
|
+
});
|
|
1025
|
+
|
|
1026
|
+
test("traffic variableOverride with matching conditions becomes *", function () {
|
|
1027
|
+
const datafile: DatafileContent = {
|
|
1028
|
+
schemaVersion: "2",
|
|
1029
|
+
revision: "unknown",
|
|
1030
|
+
segments: {},
|
|
1031
|
+
features: {
|
|
1032
|
+
feature1: {
|
|
1033
|
+
bucketBy: ["userId"],
|
|
1034
|
+
traffic: [
|
|
1035
|
+
{
|
|
1036
|
+
key: "rule1",
|
|
1037
|
+
segments: "*",
|
|
1038
|
+
percentage: 100000,
|
|
1039
|
+
variableOverrides: {
|
|
1040
|
+
config: [
|
|
1041
|
+
{
|
|
1042
|
+
conditions: [{ attribute: "country", operator: "equals", value: "nl" }],
|
|
1043
|
+
value: { source: "nl" },
|
|
1044
|
+
},
|
|
1045
|
+
],
|
|
1046
|
+
},
|
|
1047
|
+
},
|
|
1048
|
+
],
|
|
1049
|
+
},
|
|
1050
|
+
},
|
|
1051
|
+
};
|
|
1052
|
+
|
|
1053
|
+
const result = buildScopedDatafile(datafile, { country: "nl" });
|
|
1054
|
+
|
|
1055
|
+
expect(result.features.feature1.traffic[0].variableOverrides?.config[0].conditions).toEqual(
|
|
1056
|
+
"*",
|
|
1057
|
+
);
|
|
1058
|
+
});
|
|
1059
|
+
|
|
1060
|
+
test("traffic variableOverride with non-matching conditions remains", function () {
|
|
1061
|
+
const datafile: DatafileContent = {
|
|
1062
|
+
schemaVersion: "2",
|
|
1063
|
+
revision: "unknown",
|
|
1064
|
+
segments: {},
|
|
1065
|
+
features: {
|
|
1066
|
+
feature1: {
|
|
1067
|
+
bucketBy: ["userId"],
|
|
1068
|
+
traffic: [
|
|
1069
|
+
{
|
|
1070
|
+
key: "rule1",
|
|
1071
|
+
segments: "*",
|
|
1072
|
+
percentage: 100000,
|
|
1073
|
+
variableOverrides: {
|
|
1074
|
+
config: [
|
|
1075
|
+
{
|
|
1076
|
+
conditions: [{ attribute: "country", operator: "equals", value: "nl" }],
|
|
1077
|
+
value: { source: "nl" },
|
|
1078
|
+
},
|
|
1079
|
+
],
|
|
1080
|
+
},
|
|
1081
|
+
},
|
|
1082
|
+
],
|
|
1083
|
+
},
|
|
1084
|
+
},
|
|
1085
|
+
};
|
|
1086
|
+
|
|
1087
|
+
const result = buildScopedDatafile(datafile, { country: "de" });
|
|
1088
|
+
|
|
1089
|
+
expect(result.features.feature1.traffic[0].variableOverrides?.config[0].conditions).toEqual([
|
|
1090
|
+
{ attribute: "country", operator: "equals", value: "nl" },
|
|
1091
|
+
]);
|
|
1092
|
+
});
|
|
1093
|
+
|
|
1094
|
+
test("traffic variableOverride supports array/group segments and conditions", function () {
|
|
1095
|
+
const datafile: DatafileContent = {
|
|
1096
|
+
schemaVersion: "2",
|
|
1097
|
+
revision: "unknown",
|
|
1098
|
+
segments: {
|
|
1099
|
+
mobile: {
|
|
1100
|
+
conditions: [{ attribute: "device", operator: "equals", value: "mobile" }],
|
|
1101
|
+
},
|
|
1102
|
+
touch: {
|
|
1103
|
+
conditions: [{ attribute: "touch", operator: "equals", value: true }],
|
|
1104
|
+
},
|
|
1105
|
+
},
|
|
1106
|
+
features: {
|
|
1107
|
+
feature1: {
|
|
1108
|
+
bucketBy: ["userId"],
|
|
1109
|
+
traffic: [
|
|
1110
|
+
{
|
|
1111
|
+
key: "rule1",
|
|
1112
|
+
segments: "*",
|
|
1113
|
+
percentage: 100000,
|
|
1114
|
+
variableOverrides: {
|
|
1115
|
+
config: [
|
|
1116
|
+
{
|
|
1117
|
+
segments: { and: ["mobile", "touch"] },
|
|
1118
|
+
value: { source: "group-segments" },
|
|
1119
|
+
},
|
|
1120
|
+
{
|
|
1121
|
+
conditions: {
|
|
1122
|
+
and: [
|
|
1123
|
+
{ attribute: "country", operator: "equals", value: "nl" },
|
|
1124
|
+
{ attribute: "tier", operator: "equals", value: "premium" },
|
|
1125
|
+
],
|
|
1126
|
+
},
|
|
1127
|
+
value: { source: "group-conditions" },
|
|
1128
|
+
},
|
|
1129
|
+
],
|
|
1130
|
+
},
|
|
1131
|
+
},
|
|
1132
|
+
],
|
|
1133
|
+
},
|
|
1134
|
+
},
|
|
1135
|
+
};
|
|
1136
|
+
|
|
1137
|
+
const result = buildScopedDatafile(datafile, {
|
|
1138
|
+
device: "mobile",
|
|
1139
|
+
touch: true,
|
|
1140
|
+
country: "nl",
|
|
1141
|
+
tier: "premium",
|
|
1142
|
+
});
|
|
1143
|
+
|
|
1144
|
+
expect(result.features.feature1.traffic[0].variableOverrides?.config[0].segments).toEqual(
|
|
1145
|
+
"*",
|
|
1146
|
+
);
|
|
1147
|
+
expect(result.features.feature1.traffic[0].variableOverrides?.config[1].conditions).toEqual(
|
|
1148
|
+
"*",
|
|
1149
|
+
);
|
|
1150
|
+
});
|
|
1151
|
+
|
|
1152
|
+
test("traffic variableOverride supports stringified segments and conditions", function () {
|
|
1153
|
+
const datafile: DatafileContent = {
|
|
1154
|
+
schemaVersion: "2",
|
|
1155
|
+
revision: "unknown",
|
|
1156
|
+
segments: {
|
|
1157
|
+
mobile: {
|
|
1158
|
+
conditions: [{ attribute: "device", operator: "equals", value: "mobile" }],
|
|
1159
|
+
},
|
|
1160
|
+
touch: {
|
|
1161
|
+
conditions: [{ attribute: "touch", operator: "equals", value: true }],
|
|
1162
|
+
},
|
|
1163
|
+
},
|
|
1164
|
+
features: {
|
|
1165
|
+
feature1: {
|
|
1166
|
+
bucketBy: ["userId"],
|
|
1167
|
+
traffic: [
|
|
1168
|
+
{
|
|
1169
|
+
key: "rule1",
|
|
1170
|
+
segments: "*",
|
|
1171
|
+
percentage: 100000,
|
|
1172
|
+
variableOverrides: {
|
|
1173
|
+
config: [
|
|
1174
|
+
{
|
|
1175
|
+
segments: JSON.stringify({ and: ["mobile", "touch"] }),
|
|
1176
|
+
value: { source: "stringified-segments" },
|
|
1177
|
+
},
|
|
1178
|
+
{
|
|
1179
|
+
conditions: JSON.stringify([
|
|
1180
|
+
{ attribute: "country", operator: "equals", value: "nl" },
|
|
1181
|
+
]),
|
|
1182
|
+
value: { source: "stringified-conditions" },
|
|
1183
|
+
},
|
|
1184
|
+
],
|
|
1185
|
+
},
|
|
1186
|
+
},
|
|
1187
|
+
],
|
|
1188
|
+
},
|
|
1189
|
+
},
|
|
1190
|
+
};
|
|
1191
|
+
|
|
1192
|
+
const result = buildScopedDatafile(datafile, {
|
|
1193
|
+
device: "mobile",
|
|
1194
|
+
touch: true,
|
|
1195
|
+
country: "nl",
|
|
1196
|
+
});
|
|
1197
|
+
|
|
1198
|
+
expect(result.features.feature1.traffic[0].variableOverrides?.config[0].segments).toEqual(
|
|
1199
|
+
"*",
|
|
1200
|
+
);
|
|
1201
|
+
expect(result.features.feature1.traffic[0].variableOverrides?.config[1].conditions).toEqual(
|
|
1202
|
+
"*",
|
|
1203
|
+
);
|
|
1204
|
+
});
|
|
1205
|
+
|
|
1206
|
+
test("multiple traffic entries still apply consecutive * dedupe", function () {
|
|
1207
|
+
const datafile: DatafileContent = {
|
|
1208
|
+
schemaVersion: "2",
|
|
1209
|
+
revision: "unknown",
|
|
1210
|
+
segments: {
|
|
1211
|
+
web: {
|
|
1212
|
+
conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
|
|
1213
|
+
},
|
|
1214
|
+
mobile: {
|
|
1215
|
+
conditions: [{ attribute: "device", operator: "equals", value: "mobile" }],
|
|
1216
|
+
},
|
|
1217
|
+
},
|
|
1218
|
+
features: {
|
|
1219
|
+
feature1: {
|
|
1220
|
+
bucketBy: ["userId"],
|
|
1221
|
+
traffic: [
|
|
1222
|
+
{
|
|
1223
|
+
key: "rule1",
|
|
1224
|
+
segments: "web",
|
|
1225
|
+
percentage: 50000,
|
|
1226
|
+
variableOverrides: {
|
|
1227
|
+
config: [{ segments: "mobile", value: { source: "rule1" } }],
|
|
1228
|
+
},
|
|
1229
|
+
},
|
|
1230
|
+
{
|
|
1231
|
+
key: "rule2",
|
|
1232
|
+
segments: "web",
|
|
1233
|
+
percentage: 50000,
|
|
1234
|
+
variableOverrides: {
|
|
1235
|
+
config: [{ segments: "mobile", value: { source: "rule2" } }],
|
|
1236
|
+
},
|
|
1237
|
+
},
|
|
1238
|
+
],
|
|
1239
|
+
},
|
|
1240
|
+
},
|
|
1241
|
+
};
|
|
1242
|
+
|
|
1243
|
+
const result = buildScopedDatafile(datafile, {
|
|
1244
|
+
platform: "web",
|
|
1245
|
+
device: "mobile",
|
|
1246
|
+
});
|
|
1247
|
+
|
|
1248
|
+
expect(result.features.feature1.traffic).toHaveLength(1);
|
|
1249
|
+
expect(result.features.feature1.traffic[0].key).toEqual("rule1");
|
|
1250
|
+
expect(result.features.feature1.traffic[0].variableOverrides?.config[0].segments).toEqual(
|
|
1251
|
+
"*",
|
|
1252
|
+
);
|
|
1253
|
+
});
|
|
1254
|
+
|
|
1255
|
+
test("does not mutate original datafile traffic variableOverrides", function () {
|
|
1256
|
+
const datafile: DatafileContent = {
|
|
1257
|
+
schemaVersion: "2",
|
|
1258
|
+
revision: "unknown",
|
|
1259
|
+
segments: {
|
|
1260
|
+
mobile: {
|
|
1261
|
+
conditions: [{ attribute: "device", operator: "equals", value: "mobile" }],
|
|
1262
|
+
},
|
|
1263
|
+
},
|
|
1264
|
+
features: {
|
|
1265
|
+
feature1: {
|
|
1266
|
+
bucketBy: ["userId"],
|
|
1267
|
+
traffic: [
|
|
1268
|
+
{
|
|
1269
|
+
key: "rule1",
|
|
1270
|
+
segments: "*",
|
|
1271
|
+
percentage: 100000,
|
|
1272
|
+
variableOverrides: {
|
|
1273
|
+
config: [{ segments: "mobile", value: { source: "mobile" } }],
|
|
1274
|
+
},
|
|
1275
|
+
},
|
|
1276
|
+
],
|
|
1277
|
+
},
|
|
1278
|
+
},
|
|
1279
|
+
};
|
|
1280
|
+
|
|
1281
|
+
const original = JSON.stringify(datafile);
|
|
1282
|
+
|
|
1283
|
+
buildScopedDatafile(datafile, { device: "mobile" });
|
|
1284
|
+
|
|
1285
|
+
expect(JSON.stringify(datafile)).toEqual(original);
|
|
1286
|
+
});
|
|
1287
|
+
});
|
|
1288
|
+
|
|
959
1289
|
describe("features with variations and variableOverrides", function () {
|
|
960
1290
|
test("variableOverride with matching segments becomes *", function () {
|
|
961
1291
|
const datafile: DatafileContent = {
|
|
@@ -4,6 +4,23 @@ import { DatafileReader, createLogger } from "@featurevisor/sdk";
|
|
|
4
4
|
import { buildScopedConditions } from "./buildScopedConditions";
|
|
5
5
|
import { buildScopedSegments } from "./buildScopedSegments";
|
|
6
6
|
|
|
7
|
+
function parseIfStringified<T>(value: T): T {
|
|
8
|
+
if (typeof value !== "string" || value === "*") {
|
|
9
|
+
return value;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const firstChar = value[0];
|
|
13
|
+
if (firstChar !== "{" && firstChar !== "[") {
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
return JSON.parse(value) as T;
|
|
19
|
+
} catch {
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
7
24
|
export function buildScopedDatafile(
|
|
8
25
|
originalDatafileContent: DatafileContent,
|
|
9
26
|
context: Context,
|
|
@@ -76,13 +93,52 @@ export function buildScopedDatafile(
|
|
|
76
93
|
|
|
77
94
|
// segments
|
|
78
95
|
if (traffic.segments) {
|
|
96
|
+
const segments = parseIfStringified(traffic.segments);
|
|
79
97
|
feature.traffic[trafficI].segments = buildScopedSegments(
|
|
80
98
|
originalDatafileReader,
|
|
81
|
-
|
|
99
|
+
segments,
|
|
82
100
|
context,
|
|
83
101
|
removeSegments,
|
|
84
102
|
);
|
|
85
103
|
}
|
|
104
|
+
|
|
105
|
+
// variable overrides
|
|
106
|
+
if (traffic.variableOverrides) {
|
|
107
|
+
for (const variableKey in traffic.variableOverrides) {
|
|
108
|
+
const variableOverrides = traffic.variableOverrides[variableKey];
|
|
109
|
+
|
|
110
|
+
for (
|
|
111
|
+
let variableOverrideI = 0;
|
|
112
|
+
variableOverrideI < variableOverrides.length;
|
|
113
|
+
variableOverrideI++
|
|
114
|
+
) {
|
|
115
|
+
const variableOverride = variableOverrides[variableOverrideI];
|
|
116
|
+
|
|
117
|
+
// segments
|
|
118
|
+
if (variableOverride.segments) {
|
|
119
|
+
const segments = parseIfStringified(variableOverride.segments);
|
|
120
|
+
variableOverride.segments = buildScopedSegments(
|
|
121
|
+
originalDatafileReader,
|
|
122
|
+
segments,
|
|
123
|
+
context,
|
|
124
|
+
removeSegments,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// conditions
|
|
129
|
+
if (variableOverride.conditions) {
|
|
130
|
+
const conditions = parseIfStringified(variableOverride.conditions);
|
|
131
|
+
variableOverride.conditions = buildScopedConditions(
|
|
132
|
+
originalDatafileReader,
|
|
133
|
+
conditions,
|
|
134
|
+
context,
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
variableOverrides[variableOverrideI] = variableOverride;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
86
142
|
}
|
|
87
143
|
}
|
|
88
144
|
|
|
@@ -987,6 +987,51 @@ describe("mutateVariables", function () {
|
|
|
987
987
|
const result = resolveMutationsForSingleVariable(schema, "foo", {});
|
|
988
988
|
expect(result).toEqual({});
|
|
989
989
|
});
|
|
990
|
+
|
|
991
|
+
test("uses provided baseValue instead of defaultValue for path mutations", function () {
|
|
992
|
+
const schema: Record<string, VariableSchema> = {
|
|
993
|
+
cfg: {
|
|
994
|
+
type: "object",
|
|
995
|
+
defaultValue: { source: "default", nested: { value: 1 } },
|
|
996
|
+
},
|
|
997
|
+
};
|
|
998
|
+
|
|
999
|
+
const result = resolveMutationsForSingleVariable(
|
|
1000
|
+
schema,
|
|
1001
|
+
"cfg",
|
|
1002
|
+
{
|
|
1003
|
+
"nested.value": 2,
|
|
1004
|
+
},
|
|
1005
|
+
{ source: "rule", nested: { value: 10 }, extra: true },
|
|
1006
|
+
);
|
|
1007
|
+
|
|
1008
|
+
expect(result).toEqual({
|
|
1009
|
+
source: "rule",
|
|
1010
|
+
nested: { value: 2 },
|
|
1011
|
+
extra: true,
|
|
1012
|
+
});
|
|
1013
|
+
});
|
|
1014
|
+
|
|
1015
|
+
test("baseValue with full replacement key still applies replacement first", function () {
|
|
1016
|
+
const schema: Record<string, VariableSchema> = {
|
|
1017
|
+
cfg: {
|
|
1018
|
+
type: "object",
|
|
1019
|
+
defaultValue: { a: 1, b: 2 },
|
|
1020
|
+
},
|
|
1021
|
+
};
|
|
1022
|
+
|
|
1023
|
+
const result = resolveMutationsForSingleVariable(
|
|
1024
|
+
schema,
|
|
1025
|
+
"cfg",
|
|
1026
|
+
{
|
|
1027
|
+
cfg: { a: 100, c: 300 },
|
|
1028
|
+
c: 999,
|
|
1029
|
+
},
|
|
1030
|
+
{ a: 10, b: 20 },
|
|
1031
|
+
);
|
|
1032
|
+
|
|
1033
|
+
expect(result).toEqual({ a: 100, c: 999 });
|
|
1034
|
+
});
|
|
990
1035
|
});
|
|
991
1036
|
|
|
992
1037
|
describe("does not mutate input", function () {
|
|
@@ -89,14 +89,21 @@ export function resolveMutationsForSingleVariable(
|
|
|
89
89
|
variablesSchema: Record<string, VariableSchema> | undefined,
|
|
90
90
|
variableKey: string,
|
|
91
91
|
overrideValue: VariableValue,
|
|
92
|
+
baseValue?: VariableValue,
|
|
92
93
|
): VariableValue {
|
|
93
94
|
if (!variablesSchema || !variablesSchema[variableKey]) return overrideValue;
|
|
94
95
|
if (overrideValue === null || overrideValue === undefined) return overrideValue;
|
|
95
96
|
if (typeof overrideValue !== "object" || Array.isArray(overrideValue)) {
|
|
96
97
|
return overrideValue;
|
|
97
98
|
}
|
|
99
|
+
|
|
98
100
|
const pathMap = overrideValue as Record<string, VariableValue>;
|
|
99
101
|
const flat: Record<string, VariableValue> = {};
|
|
102
|
+
|
|
103
|
+
if (typeof baseValue !== "undefined") {
|
|
104
|
+
flat[variableKey] = JSON.parse(JSON.stringify(baseValue)) as VariableValue;
|
|
105
|
+
}
|
|
106
|
+
|
|
100
107
|
for (const [k, v] of Object.entries(pathMap)) {
|
|
101
108
|
flat[k === variableKey ? variableKey : variableKey + "." + k] = v;
|
|
102
109
|
}
|
|
@@ -910,4 +910,37 @@ describe("core: Traffic", function () {
|
|
|
910
910
|
},
|
|
911
911
|
]);
|
|
912
912
|
});
|
|
913
|
+
|
|
914
|
+
it("should include variableOverrides from parsed rule in traffic", function () {
|
|
915
|
+
const variableOverrides = {
|
|
916
|
+
config: [
|
|
917
|
+
{
|
|
918
|
+
segments: "mobile",
|
|
919
|
+
value: {
|
|
920
|
+
"layout.width": 900,
|
|
921
|
+
},
|
|
922
|
+
},
|
|
923
|
+
],
|
|
924
|
+
};
|
|
925
|
+
|
|
926
|
+
const result = getTraffic(
|
|
927
|
+
[
|
|
928
|
+
{
|
|
929
|
+
value: "control",
|
|
930
|
+
weight: 100,
|
|
931
|
+
},
|
|
932
|
+
],
|
|
933
|
+
[
|
|
934
|
+
{
|
|
935
|
+
key: "1",
|
|
936
|
+
segments: "*",
|
|
937
|
+
percentage: 100,
|
|
938
|
+
variableOverrides,
|
|
939
|
+
},
|
|
940
|
+
],
|
|
941
|
+
undefined,
|
|
942
|
+
);
|
|
943
|
+
|
|
944
|
+
expect(result[0].variableOverrides).toEqual(variableOverrides);
|
|
945
|
+
});
|
|
913
946
|
});
|
package/src/builder/traffic.ts
CHANGED
|
@@ -600,7 +600,7 @@ export async function generateTypeScriptCodeForProject(
|
|
|
600
600
|
for (const attributeKey of attributeFiles) {
|
|
601
601
|
const parsedAttribute = await datasource.readAttribute(attributeKey);
|
|
602
602
|
|
|
603
|
-
if (
|
|
603
|
+
if (parsedAttribute.archived) {
|
|
604
604
|
continue;
|
|
605
605
|
}
|
|
606
606
|
|