Skip to content

Commit b08b7a5

Browse files
authored
feat: add "json" type to FeatureVariable (#372)
1 parent db48d3f commit b08b7a5

File tree

10 files changed

+277
-16
lines changed

10 files changed

+277
-16
lines changed

core-api/src/main/java/com/optimizely/ab/config/FeatureVariable.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,25 +65,30 @@ public static VariableStatus fromString(String variableStatusString) {
6565
public static final String INTEGER_TYPE = "integer";
6666
public static final String DOUBLE_TYPE = "double";
6767
public static final String BOOLEAN_TYPE = "boolean";
68+
public static final String JSON_TYPE = "json";
6869

6970
private final String id;
7071
private final String key;
7172
private final String defaultValue;
7273
private final String type;
7374
@Nullable
75+
private final String subType; // this is for backward-compatibility (json type)
76+
@Nullable
7477
private final VariableStatus status;
7578

7679
@JsonCreator
7780
public FeatureVariable(@JsonProperty("id") String id,
7881
@JsonProperty("key") String key,
7982
@JsonProperty("defaultValue") String defaultValue,
8083
@JsonProperty("status") VariableStatus status,
81-
@JsonProperty("type") String type) {
84+
@JsonProperty("type") String type,
85+
@JsonProperty("subType") String subType) {
8286
this.id = id;
8387
this.key = key;
8488
this.defaultValue = defaultValue;
8589
this.status = status;
8690
this.type = type;
91+
this.subType = subType;
8792
}
8893

8994
@Nullable
@@ -104,6 +109,7 @@ public String getDefaultValue() {
104109
}
105110

106111
public String getType() {
112+
if (type.equals(STRING_TYPE) && subType != null && subType.equals(JSON_TYPE)) return JSON_TYPE;
107113
return type;
108114
}
109115

@@ -114,6 +120,7 @@ public String toString() {
114120
", key='" + key + '\'' +
115121
", defaultValue='" + defaultValue + '\'' +
116122
", type=" + type +
123+
", subType=" + subType +
117124
", status=" + status +
118125
'}';
119126
}
@@ -138,7 +145,8 @@ public int hashCode() {
138145
result = 31 * result + key.hashCode();
139146
result = 31 * result + defaultValue.hashCode();
140147
result = 31 * result + type.hashCode();
141-
result = 31 * result + status.hashCode();
148+
result = 31 * result + (subType != null ? subType.hashCode() : 0);
149+
result = 31 * result + (status != null ? status.hashCode() : 0);
142150
return result;
143151
}
144152
}

core-api/src/main/java/com/optimizely/ab/config/parser/JsonConfigParser.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,12 +345,16 @@ private List<FeatureVariable> parseFeatureVariables(JSONArray featureVariablesJs
345345
String key = FeatureVariableObject.getString("key");
346346
String defaultValue = FeatureVariableObject.getString("defaultValue");
347347
String type = FeatureVariableObject.getString("type");
348+
String subType = null;
349+
if (FeatureVariableObject.has("subType")) {
350+
subType = FeatureVariableObject.getString("subType");
351+
}
348352
FeatureVariable.VariableStatus status = null;
349353
if (FeatureVariableObject.has("status")) {
350354
status = FeatureVariable.VariableStatus.fromString(FeatureVariableObject.getString("status"));
351355
}
352356

353-
featureVariables.add(new FeatureVariable(id, key, defaultValue, status, type));
357+
featureVariables.add(new FeatureVariable(id, key, defaultValue, status, type, subType));
354358
}
355359

356360
return featureVariables;

core-api/src/main/java/com/optimizely/ab/config/parser/JsonSimpleConfigParser.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,9 +335,10 @@ private List<FeatureVariable> parseFeatureVariables(JSONArray featureVariablesJs
335335
String key = (String) featureVariableObject.get("key");
336336
String defaultValue = (String) featureVariableObject.get("defaultValue");
337337
String type = (String) featureVariableObject.get("type");
338+
String subType = (String) featureVariableObject.get("subType");
338339
VariableStatus status = VariableStatus.fromString((String) featureVariableObject.get("status"));
339340

340-
featureVariables.add(new FeatureVariable(id, key, defaultValue, status, type));
341+
featureVariables.add(new FeatureVariable(id, key, defaultValue, status, type, subType));
341342
}
342343

343344
return featureVariables;

core-api/src/test/java/com/optimizely/ab/config/ValidProjectConfigV4.java

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,8 @@ public class ValidProjectConfigV4 {
247247
VARIABLE_DOUBLE_VARIABLE_KEY,
248248
VARIABLE_DOUBLE_DEFAULT_VALUE,
249249
null,
250-
FeatureVariable.DOUBLE_TYPE
250+
FeatureVariable.DOUBLE_TYPE,
251+
null
251252
);
252253
private static final String FEATURE_SINGLE_VARIABLE_INTEGER_ID = "3281420120";
253254
public static final String FEATURE_SINGLE_VARIABLE_INTEGER_KEY = "integer_single_variable_feature";
@@ -259,7 +260,8 @@ public class ValidProjectConfigV4 {
259260
VARIABLE_INTEGER_VARIABLE_KEY,
260261
VARIABLE_INTEGER_DEFAULT_VALUE,
261262
null,
262-
FeatureVariable.INTEGER_TYPE
263+
FeatureVariable.INTEGER_TYPE,
264+
null
263265
);
264266
private static final String FEATURE_SINGLE_VARIABLE_BOOLEAN_ID = "2591051011";
265267
public static final String FEATURE_SINGLE_VARIABLE_BOOLEAN_KEY = "boolean_single_variable_feature";
@@ -271,7 +273,8 @@ public class ValidProjectConfigV4 {
271273
VARIABLE_BOOLEAN_VARIABLE_KEY,
272274
VARIABLE_BOOLEAN_VARIABLE_DEFAULT_VALUE,
273275
null,
274-
FeatureVariable.BOOLEAN_TYPE
276+
FeatureVariable.BOOLEAN_TYPE,
277+
null
275278
);
276279
private static final FeatureFlag FEATURE_FLAG_SINGLE_VARIABLE_BOOLEAN = new FeatureFlag(
277280
FEATURE_SINGLE_VARIABLE_BOOLEAN_ID,
@@ -292,7 +295,8 @@ public class ValidProjectConfigV4 {
292295
VARIABLE_STRING_VARIABLE_KEY,
293296
VARIABLE_STRING_VARIABLE_DEFAULT_VALUE,
294297
null,
295-
FeatureVariable.STRING_TYPE
298+
FeatureVariable.STRING_TYPE,
299+
null
296300
);
297301
private static final String ROLLOUT_1_ID = "1058508303";
298302
private static final String ROLLOUT_1_EVERYONE_ELSE_EXPERIMENT_ID = "1785077004";
@@ -388,7 +392,8 @@ public class ValidProjectConfigV4 {
388392
VARIABLE_FIRST_LETTER_KEY,
389393
VARIABLE_FIRST_LETTER_DEFAULT_VALUE,
390394
null,
391-
FeatureVariable.STRING_TYPE
395+
FeatureVariable.STRING_TYPE,
396+
null
392397
);
393398
private static final String VARIABLE_REST_OF_NAME_ID = "4052219963";
394399
private static final String VARIABLE_REST_OF_NAME_KEY = "rest_of_name";
@@ -398,17 +403,41 @@ public class ValidProjectConfigV4 {
398403
VARIABLE_REST_OF_NAME_KEY,
399404
VARIABLE_REST_OF_NAME_DEFAULT_VALUE,
400405
null,
401-
FeatureVariable.STRING_TYPE
406+
FeatureVariable.STRING_TYPE,
407+
null
408+
);
409+
private static final String VARIABLE_JSON_PATCHED_TYPE_ID = "4111661000";
410+
private static final String VARIABLE_JSON_PATCHED_TYPE_KEY = "json_patched";
411+
private static final String VARIABLE_JSON_PATCHED_TYPE_DEFAULT_VALUE = "{\"k1\":\"v1\",\"k2\":3.5,\"k3\":true,\"k4\":{\"kk1\":\"vv1\",\"kk2\":false}}";
412+
private static final FeatureVariable VARIABLE_JSON_PATCHED_TYPE_VARIABLE = new FeatureVariable(
413+
VARIABLE_JSON_PATCHED_TYPE_ID,
414+
VARIABLE_JSON_PATCHED_TYPE_KEY,
415+
VARIABLE_JSON_PATCHED_TYPE_DEFAULT_VALUE,
416+
null,
417+
FeatureVariable.STRING_TYPE,
418+
FeatureVariable.JSON_TYPE
419+
);
420+
private static final String VARIABLE_JSON_NATIVE_TYPE_ID = "4111661001";
421+
private static final String VARIABLE_JSON_NATIVE_TYPE_KEY = "json_native";
422+
private static final String VARIABLE_JSON_NATIVE_TYPE_DEFAULT_VALUE = "{\"k1\":\"v1\",\"k2\":3.5,\"k3\":true,\"k4\":{\"kk1\":\"vv1\",\"kk2\":false}}";
423+
private static final FeatureVariable VARIABLE_JSON_NATIVE_TYPE_VARIABLE = new FeatureVariable(
424+
VARIABLE_JSON_NATIVE_TYPE_ID,
425+
VARIABLE_JSON_NATIVE_TYPE_KEY,
426+
VARIABLE_JSON_NATIVE_TYPE_DEFAULT_VALUE,
427+
null,
428+
FeatureVariable.JSON_TYPE,
429+
null
402430
);
403-
private static final String VARIABLE_FUTURE_TYPE_ID = "4111661234";
431+
private static final String VARIABLE_FUTURE_TYPE_ID = "4111661002";
404432
private static final String VARIABLE_FUTURE_TYPE_KEY = "future_variable";
405433
private static final String VARIABLE_FUTURE_TYPE_DEFAULT_VALUE = "future_value";
406434
private static final FeatureVariable VARIABLE_FUTURE_TYPE_VARIABLE = new FeatureVariable(
407435
VARIABLE_FUTURE_TYPE_ID,
408436
VARIABLE_FUTURE_TYPE_KEY,
409437
VARIABLE_FUTURE_TYPE_DEFAULT_VALUE,
410438
null,
411-
"future_type"
439+
"future_type",
440+
null
412441
);
413442
private static final String FEATURE_MUTEX_GROUP_FEATURE_ID = "3263342226";
414443
public static final String FEATURE_MUTEX_GROUP_FEATURE_KEY = "mutex_group_feature";
@@ -420,7 +449,8 @@ public class ValidProjectConfigV4 {
420449
VARIABLE_CORRELATING_VARIATION_NAME_KEY,
421450
VARIABLE_CORRELATING_VARIATION_NAME_DEFAULT_VALUE,
422451
null,
423-
FeatureVariable.STRING_TYPE
452+
FeatureVariable.STRING_TYPE,
453+
null
424454
);
425455

426456
// group IDs
@@ -733,6 +763,10 @@ public class ValidProjectConfigV4 {
733763
new FeatureVariableUsageInstance(
734764
VARIABLE_REST_OF_NAME_ID,
735765
"red"
766+
),
767+
new FeatureVariableUsageInstance(
768+
VARIABLE_JSON_PATCHED_TYPE_ID,
769+
"{\"k1\":\"s1\",\"k2\":103.5,\"k3\":false,\"k4\":{\"kk1\":\"ss1\",\"kk2\":true}}"
736770
)
737771
)
738772
);
@@ -750,6 +784,10 @@ public class ValidProjectConfigV4 {
750784
new FeatureVariableUsageInstance(
751785
VARIABLE_REST_OF_NAME_ID,
752786
"eorge"
787+
),
788+
new FeatureVariableUsageInstance(
789+
VARIABLE_JSON_PATCHED_TYPE_ID,
790+
"{\"k1\":\"s2\",\"k2\":203.5,\"k3\":true,\"k4\":{\"kk1\":\"ss2\",\"kk2\":true}}"
753791
)
754792
)
755793
);
@@ -768,6 +806,10 @@ public class ValidProjectConfigV4 {
768806
new FeatureVariableUsageInstance(
769807
VARIABLE_REST_OF_NAME_ID,
770808
"red"
809+
),
810+
new FeatureVariableUsageInstance(
811+
VARIABLE_JSON_PATCHED_TYPE_ID,
812+
"{\"k1\":\"s3\",\"k2\":303.5,\"k3\":true,\"k4\":{\"kk1\":\"ss3\",\"kk2\":false}}"
771813
)
772814
)
773815
);
@@ -785,6 +827,10 @@ public class ValidProjectConfigV4 {
785827
new FeatureVariableUsageInstance(
786828
VARIABLE_REST_OF_NAME_ID,
787829
"eorge"
830+
),
831+
new FeatureVariableUsageInstance(
832+
VARIABLE_JSON_PATCHED_TYPE_ID,
833+
"{\"k1\":\"s4\",\"k2\":403.5,\"k3\":false,\"k4\":{\"kk1\":\"ss4\",\"kk2\":true}}"
788834
)
789835
)
790836
);
@@ -1262,6 +1308,8 @@ public class ValidProjectConfigV4 {
12621308
DatafileProjectConfigTestUtils.createListOfObjects(
12631309
VARIABLE_FIRST_LETTER_VARIABLE,
12641310
VARIABLE_REST_OF_NAME_VARIABLE,
1311+
VARIABLE_JSON_PATCHED_TYPE_VARIABLE,
1312+
VARIABLE_JSON_NATIVE_TYPE_VARIABLE,
12651313
VARIABLE_FUTURE_TYPE_VARIABLE
12661314
)
12671315
);

core-api/src/test/java/com/optimizely/ab/config/parser/GsonConfigParserTest.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import com.google.gson.JsonElement;
2222
import com.google.gson.JsonObject;
2323
import com.google.gson.reflect.TypeToken;
24+
import com.optimizely.ab.config.FeatureFlag;
25+
import com.optimizely.ab.config.FeatureVariable;
2426
import com.optimizely.ab.config.ProjectConfig;
2527
import com.optimizely.ab.config.audience.Audience;
2628
import com.optimizely.ab.config.audience.Condition;
@@ -42,6 +44,7 @@
4244
import static com.optimizely.ab.config.DatafileProjectConfigTestUtils.validProjectConfigV3;
4345
import static com.optimizely.ab.config.DatafileProjectConfigTestUtils.validProjectConfigV4;
4446
import static com.optimizely.ab.config.DatafileProjectConfigTestUtils.verifyProjectConfig;
47+
import static org.junit.Assert.assertEquals;
4548
import static org.junit.Assert.assertNotNull;
4649

4750
/**
@@ -92,6 +95,45 @@ public void parseNullFeatureEnabledProjectConfigV4() throws Exception {
9295

9396
}
9497

98+
@Test
99+
public void parseFeatureVariablesWithJsonPatched() throws Exception {
100+
JsonSimpleConfigParser parser = new JsonSimpleConfigParser();
101+
ProjectConfig actual = parser.parseProjectConfig(validConfigJsonV4());
102+
103+
// "string" type + "json" subType
104+
105+
FeatureFlag featureFlag = actual.getFeatureKeyMapping().get("multi_variate_feature");
106+
FeatureVariable variable = featureFlag.getVariableKeyToFeatureVariableMap().get("json_patched");
107+
108+
assertEquals(variable.getType(), "json");
109+
}
110+
111+
@Test
112+
public void parseFeatureVariablesWithJsonNative() throws Exception {
113+
JsonSimpleConfigParser parser = new JsonSimpleConfigParser();
114+
ProjectConfig actual = parser.parseProjectConfig(validConfigJsonV4());
115+
116+
// native "json" type
117+
118+
FeatureFlag featureFlag = actual.getFeatureKeyMapping().get("multi_variate_feature");
119+
FeatureVariable variable = featureFlag.getVariableKeyToFeatureVariableMap().get("json_native");
120+
121+
assertEquals(variable.getType(), "json");
122+
}
123+
124+
@Test
125+
public void parseFeatureVariablesWithFutureType() throws Exception {
126+
JsonSimpleConfigParser parser = new JsonSimpleConfigParser();
127+
ProjectConfig actual = parser.parseProjectConfig(validConfigJsonV4());
128+
129+
// unknown type
130+
131+
FeatureFlag featureFlag = actual.getFeatureKeyMapping().get("multi_variate_feature");
132+
FeatureVariable variable = featureFlag.getVariableKeyToFeatureVariableMap().get("future_variable");
133+
134+
assertEquals(variable.getType(), "future_type");
135+
}
136+
95137
@Test
96138
public void parseAudience() throws Exception {
97139
JsonObject jsonObject = new JsonObject();

core-api/src/test/java/com/optimizely/ab/config/parser/JacksonConfigParserTest.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
import com.fasterxml.jackson.databind.ObjectMapper;
2020
import com.fasterxml.jackson.databind.module.SimpleModule;
21+
import com.optimizely.ab.config.FeatureFlag;
22+
import com.optimizely.ab.config.FeatureVariable;
2123
import com.optimizely.ab.config.ProjectConfig;
2224
import com.optimizely.ab.config.audience.Audience;
2325
import com.optimizely.ab.config.audience.Condition;
@@ -36,6 +38,7 @@
3638
import static com.optimizely.ab.config.DatafileProjectConfigTestUtils.validProjectConfigV3;
3739
import static com.optimizely.ab.config.DatafileProjectConfigTestUtils.validProjectConfigV4;
3840
import static com.optimizely.ab.config.DatafileProjectConfigTestUtils.verifyProjectConfig;
41+
import static org.junit.Assert.assertEquals;
3942
import static org.junit.Assert.assertNotNull;
4043

4144
/**
@@ -86,6 +89,44 @@ public void parseNullFeatureEnabledProjectConfigV4() throws Exception {
8689

8790
}
8891

92+
@Test
93+
public void parseFeatureVariablesWithJsonPatched() throws Exception {
94+
JsonSimpleConfigParser parser = new JsonSimpleConfigParser();
95+
ProjectConfig actual = parser.parseProjectConfig(validConfigJsonV4());
96+
97+
// "string" type + "json" subType
98+
99+
FeatureFlag featureFlag = actual.getFeatureKeyMapping().get("multi_variate_feature");
100+
FeatureVariable variable = featureFlag.getVariableKeyToFeatureVariableMap().get("json_patched");
101+
102+
assertEquals(variable.getType(), "json");
103+
}
104+
105+
@Test
106+
public void parseFeatureVariablesWithJsonNative() throws Exception {
107+
JsonSimpleConfigParser parser = new JsonSimpleConfigParser();
108+
ProjectConfig actual = parser.parseProjectConfig(validConfigJsonV4());
109+
110+
// native "json" type
111+
112+
FeatureFlag featureFlag = actual.getFeatureKeyMapping().get("multi_variate_feature");
113+
FeatureVariable variable = featureFlag.getVariableKeyToFeatureVariableMap().get("json_native");
114+
115+
assertEquals(variable.getType(), "json");
116+
}
117+
118+
@Test
119+
public void parseFeatureVariablesWithFutureType() throws Exception {
120+
JsonSimpleConfigParser parser = new JsonSimpleConfigParser();
121+
ProjectConfig actual = parser.parseProjectConfig(validConfigJsonV4());
122+
123+
// unknown type
124+
125+
FeatureFlag featureFlag = actual.getFeatureKeyMapping().get("multi_variate_feature");
126+
FeatureVariable variable = featureFlag.getVariableKeyToFeatureVariableMap().get("future_variable");
127+
128+
assertEquals(variable.getType(), "future_type");
129+
}
89130

90131
@Test
91132
public void parseAudience() throws Exception {

0 commit comments

Comments
 (0)