From 950ff3f3656e6922d7d65404cdbb9f06bd015386 Mon Sep 17 00:00:00 2001 From: wangjoshuah Date: Mon, 24 Jul 2017 12:04:02 -0700 Subject: [PATCH 01/12] add variableIdToLiveVariableUsageInstanceMap to Varaitions. --- .../optimizely/ab/config/LiveVariableUsageInstance.java | 2 +- .../src/main/java/com/optimizely/ab/config/Variation.java | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/core-api/src/main/java/com/optimizely/ab/config/LiveVariableUsageInstance.java b/core-api/src/main/java/com/optimizely/ab/config/LiveVariableUsageInstance.java index 05378b808..79cf05620 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/LiveVariableUsageInstance.java +++ b/core-api/src/main/java/com/optimizely/ab/config/LiveVariableUsageInstance.java @@ -24,7 +24,7 @@ * Represents the value of a live variable for a variation */ @JsonIgnoreProperties(ignoreUnknown = true) -public class LiveVariableUsageInstance { +public class LiveVariableUsageInstance implements IdMapped { private final String id; private final String value; diff --git a/core-api/src/main/java/com/optimizely/ab/config/Variation.java b/core-api/src/main/java/com/optimizely/ab/config/Variation.java index 0991a0a5e..c491ef268 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/Variation.java +++ b/core-api/src/main/java/com/optimizely/ab/config/Variation.java @@ -24,6 +24,7 @@ import javax.annotation.Nullable; import java.util.List; +import java.util.Map; /** * Represents the Optimizely Variation configuration. @@ -36,6 +37,7 @@ public class Variation implements IdKeyMapped { private final String id; private final String key; private final List liveVariableUsageInstances; + private final Map variableIdToLiveVariableUsageInstanceMap; public Variation(String id, String key) { this(id, key, null); @@ -48,6 +50,7 @@ public Variation(@JsonProperty("id") String id, this.id = id; this.key = key; this.liveVariableUsageInstances = liveVariableUsageInstances; + this.variableIdToLiveVariableUsageInstanceMap = ProjectConfigUtils.generateIdMapping(liveVariableUsageInstances); } public @Nonnull String getId() { @@ -62,6 +65,10 @@ public Variation(@JsonProperty("id") String id, return liveVariableUsageInstances; } + public Map getVariableIdToLiveVariableUsageInstanceMap() { + return variableIdToLiveVariableUsageInstanceMap; + } + public boolean is(String otherKey) { return key.equals(otherKey); } From 7c27128703592f470b931df1d920f68ead18fad3 Mon Sep 17 00:00:00 2001 From: wangjoshuah Date: Sat, 8 Jul 2017 11:03:52 -0700 Subject: [PATCH 02/12] fix parsing of variations --- .../main/java/com/optimizely/ab/config/Variation.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/config/Variation.java b/core-api/src/main/java/com/optimizely/ab/config/Variation.java index c491ef268..02db51eab 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/Variation.java +++ b/core-api/src/main/java/com/optimizely/ab/config/Variation.java @@ -23,6 +23,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -49,8 +50,13 @@ public Variation(@JsonProperty("id") String id, @JsonProperty("variables") List liveVariableUsageInstances) { this.id = id; this.key = key; - this.liveVariableUsageInstances = liveVariableUsageInstances; - this.variableIdToLiveVariableUsageInstanceMap = ProjectConfigUtils.generateIdMapping(liveVariableUsageInstances); + if (liveVariableUsageInstances == null) { + this.liveVariableUsageInstances = Collections.emptyList(); + } + else { + this.liveVariableUsageInstances = liveVariableUsageInstances; + } + this.variableIdToLiveVariableUsageInstanceMap = ProjectConfigUtils.generateIdMapping(this.liveVariableUsageInstances); } public @Nonnull String getId() { From a8c0dc56e86ac4907a2507eaf9f95a03b4ee9a51 Mon Sep 17 00:00:00 2001 From: wangjoshuah Date: Sun, 9 Jul 2017 10:45:19 -0700 Subject: [PATCH 03/12] Add equality methods to LiveVariable and FeatureFlag models to improve ProjectConfigParsing evaluation. Update the v4 datafile with an empty top level "variables" array and added "status" to all variables in features. Added empty array passed in to ProjectConfig constructor in ValidProjectConfigV4.java ProjectConfigTestUtils now compare feature flags more effectively Signed-off-by: wangjoshuah --- .../com/optimizely/ab/config/FeatureFlag.java | 26 +++++++++++++++++ .../optimizely/ab/config/LiveVariable.java | 24 +++++++++++++++ .../optimizely/ab/config/ProjectConfig.java | 4 +++ .../ProjectConfigJacksonDeserializer.java | 29 +++++++++++++++---- .../ab/config/ProjectConfigTestUtils.java | 17 +++++++++-- .../ab/config/ValidProjectConfigV4.java | 4 +-- .../ab/internal/LogbackVerifier.java | 26 ++++++++++++++--- .../config/valid-project-config-v4.json | 20 ++++++++----- 8 files changed, 129 insertions(+), 21 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/config/FeatureFlag.java b/core-api/src/main/java/com/optimizely/ab/config/FeatureFlag.java index 66c8b2675..c8918f7db 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/FeatureFlag.java +++ b/core-api/src/main/java/com/optimizely/ab/config/FeatureFlag.java @@ -77,4 +77,30 @@ public String toString() { ", variables=" + variables + '}'; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + FeatureFlag that = (FeatureFlag) o; + + if (!id.equals(that.id)) return false; + if (!key.equals(that.key)) return false; + if (!layerId.equals(that.layerId)) return false; + if (!experimentIds.equals(that.experimentIds)) return false; + if (!variables.equals(that.variables)) return false; + return variableKeyToLiveVariableMap.equals(that.variableKeyToLiveVariableMap); + } + + @Override + public int hashCode() { + int result = id.hashCode(); + result = 31 * result + key.hashCode(); + result = 31 * result + layerId.hashCode(); + result = 31 * result + experimentIds.hashCode(); + result = 31 * result + variables.hashCode(); + result = 31 * result + variableKeyToLiveVariableMap.hashCode(); + return result; + } } diff --git a/core-api/src/main/java/com/optimizely/ab/config/LiveVariable.java b/core-api/src/main/java/com/optimizely/ab/config/LiveVariable.java index 74f656b6c..63644a801 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/LiveVariable.java +++ b/core-api/src/main/java/com/optimizely/ab/config/LiveVariable.java @@ -150,4 +150,28 @@ public String toString() { ", status=" + status + '}'; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + LiveVariable variable = (LiveVariable) o; + + if (!id.equals(variable.id)) return false; + if (!key.equals(variable.key)) return false; + if (!defaultValue.equals(variable.defaultValue)) return false; + if (type != variable.type) return false; + return status == variable.status; + } + + @Override + public int hashCode() { + int result = id.hashCode(); + result = 31 * result + key.hashCode(); + result = 31 * result + defaultValue.hashCode(); + result = 31 * result + type.hashCode(); + result = 31 * result + status.hashCode(); + return result; + } } diff --git a/core-api/src/main/java/com/optimizely/ab/config/ProjectConfig.java b/core-api/src/main/java/com/optimizely/ab/config/ProjectConfig.java index fac0bbdce..4e9ad423b 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/ProjectConfig.java +++ b/core-api/src/main/java/com/optimizely/ab/config/ProjectConfig.java @@ -224,6 +224,10 @@ public List getExperimentsForEventKey(String eventKey) { return Collections.emptyList(); } + public List getFeatureFlags() { + return featureFlags; + } + public List getAttributes() { return attributes; } diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/ProjectConfigJacksonDeserializer.java b/core-api/src/main/java/com/optimizely/ab/config/parser/ProjectConfigJacksonDeserializer.java index e8722086a..8a9b2f28a 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/ProjectConfigJacksonDeserializer.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/ProjectConfigJacksonDeserializer.java @@ -23,14 +23,14 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; - import com.optimizely.ab.config.Attribute; -import com.optimizely.ab.config.audience.Audience; import com.optimizely.ab.config.EventType; import com.optimizely.ab.config.Experiment; +import com.optimizely.ab.config.FeatureFlag; import com.optimizely.ab.config.Group; import com.optimizely.ab.config.LiveVariable; import com.optimizely.ab.config.ProjectConfig; +import com.optimizely.ab.config.audience.Audience; import java.io.IOException; import java.util.List; @@ -51,6 +51,7 @@ public ProjectConfig deserialize(JsonParser parser, DeserializationContext conte String projectId = node.get("projectId").textValue(); String revision = node.get("revision").textValue(); String version = node.get("version").textValue(); + int datafileVersion = Integer.parseInt(version); List groups = mapper.readValue(node.get("groups").toString(), new TypeReference>() {}); List experiments = mapper.readValue(node.get("experiments").toString(), @@ -66,13 +67,31 @@ public ProjectConfig deserialize(JsonParser parser, DeserializationContext conte boolean anonymizeIP = false; List liveVariables = null; - if (version.equals(ProjectConfig.Version.V3.toString())) { + if (datafileVersion >= 3) { liveVariables = mapper.readValue(node.get("variables").toString(), new TypeReference>() {}); anonymizeIP = node.get("anonymizeIP").asBoolean(); } - return new ProjectConfig(accountId, projectId, version, revision, groups, experiments, attributes, events, - audiences, anonymizeIP, liveVariables); + List featureFlags = null; + if (datafileVersion >= 4) { + featureFlags = mapper.readValue(node.get("featureFlags").toString(), + new TypeReference>() {}); + } + + return new ProjectConfig( + accountId, + anonymizeIP, + projectId, + revision, + version, + attributes, + audiences, + events, + experiments, + featureFlags, + groups, + liveVariables + ); } } \ No newline at end of file diff --git a/core-api/src/test/java/com/optimizely/ab/config/ProjectConfigTestUtils.java b/core-api/src/test/java/com/optimizely/ab/config/ProjectConfigTestUtils.java index fecc6b2d4..fa4a43a25 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/ProjectConfigTestUtils.java +++ b/core-api/src/test/java/com/optimizely/ab/config/ProjectConfigTestUtils.java @@ -450,12 +450,13 @@ public static void verifyProjectConfig(@CheckForNull ProjectConfig actual, @Nonn assertThat(actual.getVersion(), is(expected.getVersion())); assertThat(actual.getRevision(), is(expected.getRevision())); - verifyGroups(actual.getGroups(), expected.getGroups()); - verifyExperiments(actual.getExperiments(), expected.getExperiments()); verifyAttributes(actual.getAttributes(), expected.getAttributes()); - verifyEvents(actual.getEventTypes(), expected.getEventTypes()); verifyAudiences(actual.getAudiences(), expected.getAudiences()); + verifyEvents(actual.getEventTypes(), expected.getEventTypes()); + verifyExperiments(actual.getExperiments(), expected.getExperiments()); + verifyFeatureFlags(actual.getFeatureFlags(), expected.getFeatureFlags()); verifyLiveVariables(actual.getLiveVariables(), expected.getLiveVariables()); + verifyGroups(actual.getGroups(), expected.getGroups()); } /** @@ -482,6 +483,16 @@ private static void verifyExperiments(List actual, List } } + private static void verifyFeatureFlags(List actual, List expected) { + assertEquals(expected.size(), actual.size()); + for (int i = 0; i < actual.size(); i ++) { + FeatureFlag actualFeatureFlag = actual.get(i); + FeatureFlag expectedFeatureFlag = expected.get(i); + + assertEquals(expectedFeatureFlag, actualFeatureFlag); + } + } + /** * Asserts that the provided variation configs are equivalent. */ diff --git a/core-api/src/test/java/com/optimizely/ab/config/ValidProjectConfigV4.java b/core-api/src/test/java/com/optimizely/ab/config/ValidProjectConfigV4.java index 1607e3d99..e6693cdc3 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/ValidProjectConfigV4.java +++ b/core-api/src/test/java/com/optimizely/ab/config/ValidProjectConfigV4.java @@ -150,7 +150,7 @@ public class ValidProjectConfigV4 { ) ); private static final String FEATURE_MULTI_VARIATE_FEATURE_ID = "3263342226"; - private static final String FEATURE_MULTI_VARIATE_FEATURE_KEY = "multi_variate_feature"; + public static final String FEATURE_MULTI_VARIATE_FEATURE_KEY = "multi_variate_feature"; private static final String VARIABLE_FIRST_LETTER_ID = "675244127"; private static final String VARIABLE_FIRST_LETTER_KEY = "first_letter"; private static final String VARIABLE_FIRST_LETTER_DEFAULT_VALUE = "H"; @@ -608,7 +608,7 @@ public static ProjectConfig generateValidProjectConfigV4() { experiments, featureFlags, groups, - null + Collections.emptyList() ); } } diff --git a/core-api/src/test/java/com/optimizely/ab/internal/LogbackVerifier.java b/core-api/src/test/java/com/optimizely/ab/internal/LogbackVerifier.java index 58d2dd2f5..3ce4f39a7 100644 --- a/core-api/src/test/java/com/optimizely/ab/internal/LogbackVerifier.java +++ b/core-api/src/test/java/com/optimizely/ab/internal/LogbackVerifier.java @@ -11,12 +11,14 @@ import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.Mockito; +import org.mockito.verification.VerificationMode; import org.slf4j.LoggerFactory; import java.util.LinkedList; import java.util.List; import static org.mockito.Matchers.argThat; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; @@ -51,11 +53,22 @@ public void expectMessage(Level level) { } public void expectMessage(Level level, String msg) { - expectMessage(level, msg, null); + expectMessage(level, msg, (Class) null); } public void expectMessage(Level level, String msg, Class throwableClass) { - expectedEvents.add(new ExpectedLogEvent(level, msg, throwableClass)); + expectMessage(level, msg, null, times(1)); + } + + public void expectMessage(Level level, String msg, VerificationMode times) { + expectMessage(level, msg, null, times); + } + + public void expectMessage(Level level, + String msg, + Class throwableClass, + VerificationMode times) { + expectedEvents.add(new ExpectedLogEvent(level, msg, throwableClass, times)); } private void before() { @@ -66,7 +79,7 @@ private void before() { private void verify() throws Throwable { for (final ExpectedLogEvent expectedEvent : expectedEvents) { - Mockito.verify(appender).doAppend(argThat(new ArgumentMatcher() { + Mockito.verify(appender, expectedEvent.times).doAppend(argThat(new ArgumentMatcher() { @Override public boolean matches(final Object argument) { return expectedEvent.matches((ILoggingEvent) argument); @@ -83,11 +96,16 @@ private final static class ExpectedLogEvent { private final String message; private final Level level; private final Class throwableClass; + private final VerificationMode times; - private ExpectedLogEvent(Level level, String message, Class throwableClass) { + private ExpectedLogEvent(Level level, + String message, + Class throwableClass, + VerificationMode times) { this.message = message; this.level = level; this.throwableClass = throwableClass; + this.times = times; } private boolean matches(ILoggingEvent actual) { diff --git a/core-api/src/test/resources/config/valid-project-config-v4.json b/core-api/src/test/resources/config/valid-project-config-v4.json index 0a74c6eb8..2f855be9c 100644 --- a/core-api/src/test/resources/config/valid-project-config-v4.json +++ b/core-api/src/test/resources/config/valid-project-config-v4.json @@ -320,7 +320,8 @@ "id": "4111654444", "key": "double_variable", "type": "double", - "defaultValue": "14.99" + "defaultValue": "14.99", + "status": "active" } ] }, @@ -334,7 +335,8 @@ "id": "593964691", "key": "integer_variable", "type": "integer", - "defaultValue": "7" + "defaultValue": "7", + "status": "active" } ] }, @@ -348,7 +350,8 @@ "id": "3974680341", "key": "boolean_variable", "type": "boolean", - "defaultValue": "true" + "defaultValue": "true", + "status": "active" } ] }, @@ -362,7 +365,8 @@ "id": "2077511132", "key": "string_variable", "type": "string", - "defaultValue": "wingardium leviosa" + "defaultValue": "wingardium leviosa", + "status": "active" } ] }, @@ -376,16 +380,18 @@ "id": "675244127", "key": "first_letter", "type": "string", - "defaultValue": "H" + "defaultValue": "H", + "status": "active" }, { "id": "4052219963", "key": "rest_of_name", "type": "string", - "defaultValue": "arry" + "defaultValue": "arry", + "status": "active" } ] } ], - "liveVariables": [] + "variables": [] } From 6b1c7f76f6f838a6b1c731de377f0beca6796216 Mon Sep 17 00:00:00 2001 From: wangjoshuah Date: Sun, 9 Jul 2017 11:41:59 -0700 Subject: [PATCH 04/12] Add GsonParser for FeatureFlags Fix GsonParsing of FeatureFlags --- .../com/optimizely/ab/config/FeatureFlag.java | 1 + .../parser/FeatureFlagGsonDeserializer.java | 36 +++++++++++++++++++ .../ab/config/parser/GsonConfigParser.java | 15 ++++---- .../ab/config/parser/GsonHelpers.java | 29 +++++++++++++-- .../parser/ProjectConfigGsonDeserializer.java | 26 ++++++++++++-- .../config/parser/GsonConfigParserTest.java | 2 +- 6 files changed, 96 insertions(+), 13 deletions(-) create mode 100644 core-api/src/main/java/com/optimizely/ab/config/parser/FeatureFlagGsonDeserializer.java diff --git a/core-api/src/main/java/com/optimizely/ab/config/FeatureFlag.java b/core-api/src/main/java/com/optimizely/ab/config/FeatureFlag.java index c8918f7db..7205530b3 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/FeatureFlag.java +++ b/core-api/src/main/java/com/optimizely/ab/config/FeatureFlag.java @@ -75,6 +75,7 @@ public String toString() { ", layerId='" + layerId + '\'' + ", experimentIds=" + experimentIds + ", variables=" + variables + + ", variableKeyToLiveVariableMap=" + variableKeyToLiveVariableMap + '}'; } diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/FeatureFlagGsonDeserializer.java b/core-api/src/main/java/com/optimizely/ab/config/parser/FeatureFlagGsonDeserializer.java new file mode 100644 index 000000000..e26623a8b --- /dev/null +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/FeatureFlagGsonDeserializer.java @@ -0,0 +1,36 @@ +/** + * + * Copyright 2017, Optimizely and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.optimizely.ab.config.parser; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.optimizely.ab.config.FeatureFlag; + +import java.lang.reflect.Type; + +public class FeatureFlagGsonDeserializer implements JsonDeserializer { + @Override + public FeatureFlag deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + + JsonObject jsonObject = json.getAsJsonObject(); + return GsonHelpers.parseFeatureFlag(jsonObject, context); + } +} diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/GsonConfigParser.java b/core-api/src/main/java/com/optimizely/ab/config/parser/GsonConfigParser.java index b87c0a16a..e20146520 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/GsonConfigParser.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/GsonConfigParser.java @@ -18,11 +18,11 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; - import com.optimizely.ab.config.Experiment; +import com.optimizely.ab.config.FeatureFlag; import com.optimizely.ab.config.Group; -import com.optimizely.ab.config.audience.Audience; import com.optimizely.ab.config.ProjectConfig; +import com.optimizely.ab.config.audience.Audience; import javax.annotation.Nonnull; @@ -40,11 +40,12 @@ public ProjectConfig parseProjectConfig(@Nonnull String json) throws ConfigParse throw new ConfigParseException("Unable to parse empty json."); } Gson gson = new GsonBuilder() - .registerTypeAdapter(ProjectConfig.class, new ProjectConfigGsonDeserializer()) - .registerTypeAdapter(Audience.class, new AudienceGsonDeserializer()) - .registerTypeAdapter(Group.class, new GroupGsonDeserializer()) - .registerTypeAdapter(Experiment.class, new ExperimentGsonDeserializer()) - .create(); + .registerTypeAdapter(Audience.class, new AudienceGsonDeserializer()) + .registerTypeAdapter(Experiment.class, new ExperimentGsonDeserializer()) + .registerTypeAdapter(FeatureFlag.class, new FeatureFlagGsonDeserializer()) + .registerTypeAdapter(Group.class, new GroupGsonDeserializer()) + .registerTypeAdapter(ProjectConfig.class, new ProjectConfigGsonDeserializer()) + .create(); try { return gson.fromJson(json, ProjectConfig.class); diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/GsonHelpers.java b/core-api/src/main/java/com/optimizely/ab/config/parser/GsonHelpers.java index 7ebdb02d2..7e79037dd 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/GsonHelpers.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/GsonHelpers.java @@ -21,17 +21,18 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; - import com.optimizely.ab.config.Experiment; import com.optimizely.ab.config.Experiment.ExperimentStatus; +import com.optimizely.ab.config.FeatureFlag; +import com.optimizely.ab.config.LiveVariable; import com.optimizely.ab.config.LiveVariableUsageInstance; import com.optimizely.ab.config.TrafficAllocation; import com.optimizely.ab.config.Variation; import java.lang.reflect.Type; import java.util.ArrayList; -import java.util.List; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; @@ -114,4 +115,28 @@ static Experiment parseExperiment(JsonObject experimentJson, String groupId, Jso static Experiment parseExperiment(JsonObject experimentJson, JsonDeserializationContext context) { return parseExperiment(experimentJson, "", context); } + + static FeatureFlag parseFeatureFlag(JsonObject featureFlagJson, JsonDeserializationContext context) { + String id = featureFlagJson.get("id").getAsString(); + String key = featureFlagJson.get("key").getAsString(); + String layerId = featureFlagJson.get("layerId").getAsString(); + + JsonArray experimentIdsJson = featureFlagJson.getAsJsonArray("experimentIds"); + List experimentIds = new ArrayList(); + for (JsonElement experimentIdObj : experimentIdsJson) { + experimentIds.add(experimentIdObj.getAsString()); + } + + Type liveVariableType = new TypeToken>() {}.getType(); + List liveVariables = context.deserialize(featureFlagJson.getAsJsonArray("variables"), + liveVariableType); + + return new FeatureFlag( + id, + key, + layerId, + experimentIds, + liveVariables + ); + } } diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/ProjectConfigGsonDeserializer.java b/core-api/src/main/java/com/optimizely/ab/config/parser/ProjectConfigGsonDeserializer.java index cb0b172c2..b4fc4849a 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/ProjectConfigGsonDeserializer.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/ProjectConfigGsonDeserializer.java @@ -25,6 +25,7 @@ import com.optimizely.ab.config.Attribute; import com.optimizely.ab.config.EventType; import com.optimizely.ab.config.Experiment; +import com.optimizely.ab.config.FeatureFlag; import com.optimizely.ab.config.Group; import com.optimizely.ab.config.LiveVariable; import com.optimizely.ab.config.ProjectConfig; @@ -47,6 +48,7 @@ public ProjectConfig deserialize(JsonElement json, Type typeOfT, JsonDeserializa String projectId = jsonObject.get("projectId").getAsString(); String revision = jsonObject.get("revision").getAsString(); String version = jsonObject.get("version").getAsString(); + int datafileVersion = Integer.parseInt(version); // generic list type tokens Type groupsType = new TypeToken>() {}.getType(); @@ -70,14 +72,32 @@ public ProjectConfig deserialize(JsonElement json, Type typeOfT, JsonDeserializa boolean anonymizeIP = false; // live variables should be null if using V2 List liveVariables = null; - if (version.equals(ProjectConfig.Version.V3.toString())) { + if (datafileVersion >= 3) { Type liveVariablesType = new TypeToken>() {}.getType(); liveVariables = context.deserialize(jsonObject.getAsJsonArray("variables"), liveVariablesType); anonymizeIP = jsonObject.get("anonymizeIP").getAsBoolean(); } - return new ProjectConfig(accountId, projectId, version, revision, groups, experiments, attributes, events, - audiences, anonymizeIP, liveVariables); + List featureFlags = null; + if (datafileVersion >= 4) { + Type featureFlagsType = new TypeToken>() {}.getType(); + featureFlags = context.deserialize(jsonObject.getAsJsonArray("featureFlags"), featureFlagsType); + } + + return new ProjectConfig( + accountId, + anonymizeIP, + projectId, + revision, + version, + attributes, + audiences, + events, + experiments, + featureFlags, + groups, + liveVariables + ); } } diff --git a/core-api/src/test/java/com/optimizely/ab/config/parser/GsonConfigParserTest.java b/core-api/src/test/java/com/optimizely/ab/config/parser/GsonConfigParserTest.java index c170befb9..3c5cc947e 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/parser/GsonConfigParserTest.java +++ b/core-api/src/test/java/com/optimizely/ab/config/parser/GsonConfigParserTest.java @@ -57,7 +57,7 @@ public void parseProjectConfigV3() throws Exception { } @Test - public void parseProjectCOnfigV4() throws Exception { + public void parseProjectConfigV4() throws Exception { GsonConfigParser parser = new GsonConfigParser(); ProjectConfig actual = parser.parseProjectConfig(validConfigJsonV4()); ProjectConfig expected = validProjectConfigV4(); From b0f277c57ef30e12e2673db0c1950dc9c971b532 Mon Sep 17 00:00:00 2001 From: wangjoshuah Date: Sun, 9 Jul 2017 12:23:08 -0700 Subject: [PATCH 05/12] Add org.Json FeatureFlag parsing Fix org.Json ProjectConfig parsing Signed-off-by: wangjoshuah --- .../ab/config/parser/JsonConfigParser.java | 85 ++++++++++++++----- 1 file changed, 66 insertions(+), 19 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/JsonConfigParser.java b/core-api/src/main/java/com/optimizely/ab/config/parser/JsonConfigParser.java index 8a37865d8..b6f24051b 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/JsonConfigParser.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/JsonConfigParser.java @@ -17,35 +17,34 @@ package com.optimizely.ab.config.parser; import com.optimizely.ab.config.Attribute; -import com.optimizely.ab.config.audience.AndCondition; -import com.optimizely.ab.config.audience.Audience; -import com.optimizely.ab.config.audience.Condition; -import com.optimizely.ab.config.audience.NotCondition; -import com.optimizely.ab.config.audience.OrCondition; -import com.optimizely.ab.config.audience.UserAttribute; import com.optimizely.ab.config.EventType; import com.optimizely.ab.config.Experiment; import com.optimizely.ab.config.Experiment.ExperimentStatus; +import com.optimizely.ab.config.FeatureFlag; import com.optimizely.ab.config.Group; import com.optimizely.ab.config.LiveVariable; -import com.optimizely.ab.config.LiveVariableUsageInstance; import com.optimizely.ab.config.LiveVariable.VariableStatus; import com.optimizely.ab.config.LiveVariable.VariableType; +import com.optimizely.ab.config.LiveVariableUsageInstance; import com.optimizely.ab.config.ProjectConfig; import com.optimizely.ab.config.TrafficAllocation; import com.optimizely.ab.config.Variation; - +import com.optimizely.ab.config.audience.AndCondition; +import com.optimizely.ab.config.audience.Audience; +import com.optimizely.ab.config.audience.Condition; +import com.optimizely.ab.config.audience.NotCondition; +import com.optimizely.ab.config.audience.OrCondition; +import com.optimizely.ab.config.audience.UserAttribute; import org.json.JSONArray; import org.json.JSONObject; +import javax.annotation.Nonnull; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; -import javax.annotation.Nonnull; - /** * {@code org.json}-based config parser implementation. */ @@ -60,6 +59,7 @@ public ProjectConfig parseProjectConfig(@Nonnull String json) throws ConfigParse String projectId = rootObject.getString("projectId"); String revision = rootObject.getString("revision"); String version = rootObject.getString("version"); + int datafileVersion = Integer.parseInt(version); List experiments = parseExperiments(rootObject.getJSONArray("experiments")); @@ -72,14 +72,31 @@ public ProjectConfig parseProjectConfig(@Nonnull String json) throws ConfigParse boolean anonymizeIP = false; List liveVariables = null; - if (version.equals(ProjectConfig.Version.V3.toString())) { + if (datafileVersion >= 3) { liveVariables = parseLiveVariables(rootObject.getJSONArray("variables")); anonymizeIP = rootObject.getBoolean("anonymizeIP"); } - return new ProjectConfig(accountId, projectId, version, revision, groups, experiments, attributes, events, - audiences, anonymizeIP, liveVariables); + List featureFlags = null; + if (datafileVersion >= 4) { + featureFlags = parseFeatureFlags(rootObject.getJSONArray("featureFlags")); + } + + return new ProjectConfig( + accountId, + anonymizeIP, + projectId, + revision, + version, + attributes, + audiences, + events, + experiments, + featureFlags, + groups, + liveVariables + ); } catch (Exception e) { throw new ConfigParseException("Unable to parse datafile: " + json, e); } @@ -123,6 +140,41 @@ private List parseExperiments(JSONArray experimentJson, String group return experiments; } + private List parseExperimentIds(JSONArray experimentIdsJson) { + ArrayList experimentIds = new ArrayList(experimentIdsJson.length()); + + for (Object experimentIdObj : experimentIdsJson) { + experimentIds.add((String) experimentIdObj); + } + + return experimentIds; + } + + private List parseFeatureFlags(JSONArray featureFlagJson) { + List featureFlags = new ArrayList(featureFlagJson.length()); + + for (Object obj : featureFlagJson) { + JSONObject featureFlagObject = (JSONObject) obj; + String id = featureFlagObject.getString("id"); + String key = featureFlagObject.getString("key"); + String layerId = featureFlagObject.getString("layerId"); + + List experimentIds = parseExperimentIds(featureFlagObject.getJSONArray("experimentIds")); + + List variables = parseLiveVariables(featureFlagObject.getJSONArray("variables")); + + featureFlags.add(new FeatureFlag( + id, + key, + layerId, + experimentIds, + variables + )); + } + + return featureFlags; + } + private List parseVariations(JSONArray variationJson) { List variations = new ArrayList(variationJson.length()); @@ -187,12 +239,7 @@ private List parseEvents(JSONArray eventJson) { for (Object obj : eventJson) { JSONObject eventObject = (JSONObject)obj; - JSONArray experimentIdsJson = eventObject.getJSONArray("experimentIds"); - List experimentIds = new ArrayList(experimentIdsJson.length()); - - for (Object experimentIdObj : experimentIdsJson) { - experimentIds.add((String)experimentIdObj); - } + List experimentIds = parseExperimentIds(eventObject.getJSONArray("experimentIds")); String id = eventObject.getString("id"); String key = eventObject.getString("key"); From 724d73ec3b4b4262844e4a6a907431d673c34b44 Mon Sep 17 00:00:00 2001 From: wangjoshuah Date: Sun, 9 Jul 2017 11:57:43 -0700 Subject: [PATCH 06/12] Add JsonSimple FeatureFlag parsing Fix JsonSimple ProjectConfig parsing Signed-off-by: wangjoshuah --- .../config/parser/JsonSimpleConfigParser.java | 67 ++++++++++++++++--- 1 file changed, 59 insertions(+), 8 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/JsonSimpleConfigParser.java b/core-api/src/main/java/com/optimizely/ab/config/parser/JsonSimpleConfigParser.java index 2c05e0c25..a87a2d22b 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/JsonSimpleConfigParser.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/JsonSimpleConfigParser.java @@ -20,6 +20,7 @@ import com.optimizely.ab.config.EventType; import com.optimizely.ab.config.Experiment; import com.optimizely.ab.config.Experiment.ExperimentStatus; +import com.optimizely.ab.config.FeatureFlag; import com.optimizely.ab.config.Group; import com.optimizely.ab.config.LiveVariable; import com.optimizely.ab.config.LiveVariable.VariableStatus; @@ -60,6 +61,7 @@ public ProjectConfig parseProjectConfig(@Nonnull String json) throws ConfigParse String projectId = (String)rootObject.get("projectId"); String revision = (String)rootObject.get("revision"); String version = (String)rootObject.get("version"); + int datafileVersion = Integer.parseInt(version); List experiments = parseExperiments((JSONArray)rootObject.get("experiments")); @@ -72,14 +74,31 @@ public ProjectConfig parseProjectConfig(@Nonnull String json) throws ConfigParse boolean anonymizeIP = false; List liveVariables = null; - if (version.equals(ProjectConfig.Version.V3.toString())) { + if (datafileVersion >= 3) { liveVariables = parseLiveVariables((JSONArray)rootObject.get("variables")); anonymizeIP = (Boolean)rootObject.get("anonymizeIP"); } - return new ProjectConfig(accountId, projectId, version, revision, groups, experiments, attributes, events, - audiences, anonymizeIP, liveVariables); + List featureFlags = null; + if (datafileVersion >= 4) { + featureFlags = parseFeatureFlags((JSONArray) rootObject.get("featureFlags")); + } + + return new ProjectConfig( + accountId, + anonymizeIP, + projectId, + revision, + version, + attributes, + audiences, + events, + experiments, + featureFlags, + groups, + liveVariables + ); } catch (Exception e) { throw new ConfigParseException("Unable to parse datafile: " + json, e); } @@ -125,6 +144,42 @@ private List parseExperiments(JSONArray experimentJson, String group return experiments; } + private List parseExperimentIds(JSONArray experimentIdsJsonArray) { + List experimentIds = new ArrayList(experimentIdsJsonArray.size()); + + for (Object experimentIdObj : experimentIdsJsonArray) { + experimentIds.add((String)experimentIdObj); + } + + return experimentIds; + } + + private List parseFeatureFlags(JSONArray featureFlagJson) { + List featureFlags = new ArrayList(featureFlagJson.size()); + + for (Object obj : featureFlagJson) { + JSONObject featureFlagObject = (JSONObject)obj; + String id = (String)featureFlagObject.get("id"); + String key = (String)featureFlagObject.get("key"); + String layerId = (String)featureFlagObject.get("layerId"); + + JSONArray experimentIdsJsonArray = (JSONArray)featureFlagObject.get("experimentIds"); + List experimentIds = parseExperimentIds(experimentIdsJsonArray); + + List liveVariables = parseLiveVariables((JSONArray) featureFlagObject.get("variables")); + + featureFlags.add(new FeatureFlag( + id, + key, + layerId, + experimentIds, + liveVariables + )); + } + + return featureFlags; + } + private List parseVariations(JSONArray variationJson) { List variations = new ArrayList(variationJson.size()); @@ -189,11 +244,7 @@ private List parseEvents(JSONArray eventJson) { for (Object obj : eventJson) { JSONObject eventObject = (JSONObject)obj; JSONArray experimentIdsJson = (JSONArray)eventObject.get("experimentIds"); - List experimentIds = new ArrayList(experimentIdsJson.size()); - - for (Object experimentIdObj : experimentIdsJson) { - experimentIds.add((String)experimentIdObj); - } + List experimentIds = parseExperimentIds(experimentIdsJson); String id = (String)eventObject.get("id"); String key = (String)eventObject.get("key"); From ff3e25e8c89993508686b9848cc1e775b8b6f88c Mon Sep 17 00:00:00 2001 From: wangjoshuah Date: Mon, 24 Jul 2017 12:09:31 -0700 Subject: [PATCH 07/12] add variableKeyToLiveVariableMap to FeatureFlag --- .../main/java/com/optimizely/ab/config/FeatureFlag.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core-api/src/main/java/com/optimizely/ab/config/FeatureFlag.java b/core-api/src/main/java/com/optimizely/ab/config/FeatureFlag.java index 7205530b3..915da05c5 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/FeatureFlag.java +++ b/core-api/src/main/java/com/optimizely/ab/config/FeatureFlag.java @@ -21,6 +21,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; +import java.util.Map; /** * Represents a FeatureFlag definition at the project level @@ -33,6 +34,7 @@ public class FeatureFlag implements IdKeyMapped{ private final String layerId; private final List experimentIds; private final List variables; + private final Map variableKeyToLiveVariableMap; @JsonCreator public FeatureFlag(@JsonProperty("id") String id, @@ -45,6 +47,7 @@ public FeatureFlag(@JsonProperty("id") String id, this.layerId = layerId; this.experimentIds = experimentIds; this.variables = variables; + this.variableKeyToLiveVariableMap = ProjectConfigUtils.generateNameMapping(variables); } public String getId() { @@ -67,6 +70,10 @@ public List getVariables() { return variables; } + public Map getVariableKeyToLiveVariableMap() { + return variableKeyToLiveVariableMap; + } + @Override public String toString() { return "FeatureFlag{" + From 079b0d72f304d70f1b0ba7a1e5992e6d359365ac Mon Sep 17 00:00:00 2001 From: wangjoshuah Date: Tue, 25 Jul 2017 16:01:09 -0700 Subject: [PATCH 08/12] get rid of magic numbers --- .../com/optimizely/ab/config/parser/JsonConfigParser.java | 4 ++-- .../optimizely/ab/config/parser/JsonSimpleConfigParser.java | 4 ++-- .../ab/config/parser/ProjectConfigGsonDeserializer.java | 4 ++-- .../ab/config/parser/ProjectConfigJacksonDeserializer.java | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/JsonConfigParser.java b/core-api/src/main/java/com/optimizely/ab/config/parser/JsonConfigParser.java index b6f24051b..241189400 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/JsonConfigParser.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/JsonConfigParser.java @@ -72,14 +72,14 @@ public ProjectConfig parseProjectConfig(@Nonnull String json) throws ConfigParse boolean anonymizeIP = false; List liveVariables = null; - if (datafileVersion >= 3) { + if (datafileVersion >= Integer.parseInt(ProjectConfig.Version.V3.toString())) { liveVariables = parseLiveVariables(rootObject.getJSONArray("variables")); anonymizeIP = rootObject.getBoolean("anonymizeIP"); } List featureFlags = null; - if (datafileVersion >= 4) { + if (datafileVersion >= Integer.parseInt(ProjectConfig.Version.V4.toString())) { featureFlags = parseFeatureFlags(rootObject.getJSONArray("featureFlags")); } diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/JsonSimpleConfigParser.java b/core-api/src/main/java/com/optimizely/ab/config/parser/JsonSimpleConfigParser.java index a87a2d22b..2c37e9abb 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/JsonSimpleConfigParser.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/JsonSimpleConfigParser.java @@ -74,14 +74,14 @@ public ProjectConfig parseProjectConfig(@Nonnull String json) throws ConfigParse boolean anonymizeIP = false; List liveVariables = null; - if (datafileVersion >= 3) { + if (datafileVersion >= Integer.parseInt(ProjectConfig.Version.V3.toString())) { liveVariables = parseLiveVariables((JSONArray)rootObject.get("variables")); anonymizeIP = (Boolean)rootObject.get("anonymizeIP"); } List featureFlags = null; - if (datafileVersion >= 4) { + if (datafileVersion >= Integer.parseInt(ProjectConfig.Version.V4.toString())) { featureFlags = parseFeatureFlags((JSONArray) rootObject.get("featureFlags")); } diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/ProjectConfigGsonDeserializer.java b/core-api/src/main/java/com/optimizely/ab/config/parser/ProjectConfigGsonDeserializer.java index b4fc4849a..3f4df5210 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/ProjectConfigGsonDeserializer.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/ProjectConfigGsonDeserializer.java @@ -72,7 +72,7 @@ public ProjectConfig deserialize(JsonElement json, Type typeOfT, JsonDeserializa boolean anonymizeIP = false; // live variables should be null if using V2 List liveVariables = null; - if (datafileVersion >= 3) { + if (datafileVersion >= Integer.parseInt(ProjectConfig.Version.V3.toString())) { Type liveVariablesType = new TypeToken>() {}.getType(); liveVariables = context.deserialize(jsonObject.getAsJsonArray("variables"), liveVariablesType); @@ -80,7 +80,7 @@ public ProjectConfig deserialize(JsonElement json, Type typeOfT, JsonDeserializa } List featureFlags = null; - if (datafileVersion >= 4) { + if (datafileVersion >= Integer.parseInt(ProjectConfig.Version.V4.toString())) { Type featureFlagsType = new TypeToken>() {}.getType(); featureFlags = context.deserialize(jsonObject.getAsJsonArray("featureFlags"), featureFlagsType); } diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/ProjectConfigJacksonDeserializer.java b/core-api/src/main/java/com/optimizely/ab/config/parser/ProjectConfigJacksonDeserializer.java index 8a9b2f28a..04503c150 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/ProjectConfigJacksonDeserializer.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/ProjectConfigJacksonDeserializer.java @@ -67,14 +67,14 @@ public ProjectConfig deserialize(JsonParser parser, DeserializationContext conte boolean anonymizeIP = false; List liveVariables = null; - if (datafileVersion >= 3) { + if (datafileVersion >= Integer.parseInt(ProjectConfig.Version.V3.toString())) { liveVariables = mapper.readValue(node.get("variables").toString(), new TypeReference>() {}); anonymizeIP = node.get("anonymizeIP").asBoolean(); } List featureFlags = null; - if (datafileVersion >= 4) { + if (datafileVersion >= Integer.parseInt(ProjectConfig.Version.V4.toString())) { featureFlags = mapper.readValue(node.get("featureFlags").toString(), new TypeReference>() {}); } From f1069635e2c710111ce8d26af77976cdadda463a Mon Sep 17 00:00:00 2001 From: wangjoshuah Date: Tue, 25 Jul 2017 17:00:40 -0700 Subject: [PATCH 09/12] log live variable parsing error when Gson parsing feature flags Signed-off-by: wangjoshuah --- .../ab/config/parser/GsonHelpers.java | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/GsonHelpers.java b/core-api/src/main/java/com/optimizely/ab/config/parser/GsonHelpers.java index 7e79037dd..8f5dbf3a7 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/GsonHelpers.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/GsonHelpers.java @@ -20,7 +20,9 @@ import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; import com.google.gson.reflect.TypeToken; +import com.optimizely.ab.bucketing.DecisionService; import com.optimizely.ab.config.Experiment; import com.optimizely.ab.config.Experiment.ExperimentStatus; import com.optimizely.ab.config.FeatureFlag; @@ -28,6 +30,8 @@ import com.optimizely.ab.config.LiveVariableUsageInstance; import com.optimizely.ab.config.TrafficAllocation; import com.optimizely.ab.config.Variation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.lang.reflect.Type; import java.util.ArrayList; @@ -38,6 +42,8 @@ final class GsonHelpers { + private static final Logger logger = LoggerFactory.getLogger(DecisionService.class); + private static List parseVariations(JsonArray variationJson, JsonDeserializationContext context) { List variations = new ArrayList(variationJson.size()); for (Object obj : variationJson) { @@ -127,9 +133,17 @@ static FeatureFlag parseFeatureFlag(JsonObject featureFlagJson, JsonDeserializat experimentIds.add(experimentIdObj.getAsString()); } - Type liveVariableType = new TypeToken>() {}.getType(); - List liveVariables = context.deserialize(featureFlagJson.getAsJsonArray("variables"), - liveVariableType); + List liveVariables = new ArrayList(); + try { + Type liveVariableType = new TypeToken>() { + }.getType(); + liveVariables = context.deserialize(featureFlagJson.getAsJsonArray("variables"), + liveVariableType); + } + catch (JsonParseException exception) { + logger.warn("Unable to parse variables for feature \"" + key + + "\". JsonParseException: " + exception); + } return new FeatureFlag( id, From 88e972c155fed6726775a5c7d4d03145bb3e4c3c Mon Sep 17 00:00:00 2001 From: wangjoshuah Date: Wed, 26 Jul 2017 13:19:06 -0700 Subject: [PATCH 10/12] remove status keyword from v4 datafile --- .../config/valid-project-config-v4.json | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/core-api/src/test/resources/config/valid-project-config-v4.json b/core-api/src/test/resources/config/valid-project-config-v4.json index 2f855be9c..624d8a538 100644 --- a/core-api/src/test/resources/config/valid-project-config-v4.json +++ b/core-api/src/test/resources/config/valid-project-config-v4.json @@ -320,8 +320,7 @@ "id": "4111654444", "key": "double_variable", "type": "double", - "defaultValue": "14.99", - "status": "active" + "defaultValue": "14.99" } ] }, @@ -335,8 +334,7 @@ "id": "593964691", "key": "integer_variable", "type": "integer", - "defaultValue": "7", - "status": "active" + "defaultValue": "7" } ] }, @@ -350,8 +348,7 @@ "id": "3974680341", "key": "boolean_variable", "type": "boolean", - "defaultValue": "true", - "status": "active" + "defaultValue": "true" } ] }, @@ -365,8 +362,7 @@ "id": "2077511132", "key": "string_variable", "type": "string", - "defaultValue": "wingardium leviosa", - "status": "active" + "defaultValue": "wingardium leviosa" } ] }, @@ -380,15 +376,13 @@ "id": "675244127", "key": "first_letter", "type": "string", - "defaultValue": "H", - "status": "active" + "defaultValue": "H" }, { "id": "4052219963", "key": "rest_of_name", "type": "string", - "defaultValue": "arry", - "status": "active" + "defaultValue": "arry" } ] } From 720e9c028d5d2a714af6ab5c04946f27b6234fa0 Mon Sep 17 00:00:00 2001 From: wangjoshuah Date: Wed, 26 Jul 2017 15:44:16 -0700 Subject: [PATCH 11/12] fix small typo --- .../main/java/com/optimizely/ab/config/parser/GsonHelpers.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/GsonHelpers.java b/core-api/src/main/java/com/optimizely/ab/config/parser/GsonHelpers.java index 8f5dbf3a7..fc75a6437 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/GsonHelpers.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/GsonHelpers.java @@ -135,8 +135,7 @@ static FeatureFlag parseFeatureFlag(JsonObject featureFlagJson, JsonDeserializat List liveVariables = new ArrayList(); try { - Type liveVariableType = new TypeToken>() { - }.getType(); + Type liveVariableType = new TypeToken>() {}.getType(); liveVariables = context.deserialize(featureFlagJson.getAsJsonArray("variables"), liveVariableType); } From bed468b6d8f5bbdbe78ce62f021d9b96c2dc1517 Mon Sep 17 00:00:00 2001 From: wangjoshuah Date: Wed, 26 Jul 2017 16:49:38 -0700 Subject: [PATCH 12/12] adjust org.json parsing --- .../java/com/optimizely/ab/config/LiveVariable.java | 13 +++++-------- .../ab/config/parser/JsonConfigParser.java | 5 ++++- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/config/LiveVariable.java b/core-api/src/main/java/com/optimizely/ab/config/LiveVariable.java index 63644a801..4ae910301 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/LiveVariable.java +++ b/core-api/src/main/java/com/optimizely/ab/config/LiveVariable.java @@ -22,6 +22,8 @@ import com.fasterxml.jackson.annotation.JsonValue; import com.google.gson.annotations.SerializedName; +import javax.annotation.Nullable; + /** * Represents a live variable definition at the project level */ @@ -100,7 +102,7 @@ public static VariableType fromString(String variableTypeString) { private final String key; private final String defaultValue; private final VariableType type; - private final VariableStatus status; + @Nullable private final VariableStatus status; @JsonCreator public LiveVariable(@JsonProperty("id") String id, @@ -111,16 +113,11 @@ public LiveVariable(@JsonProperty("id") String id, this.id = id; this.key = key; this.defaultValue = defaultValue; - if (status == null) { - this.status = VariableStatus.ACTIVE; - } - else { - this.status = status; - } + this.status = status; this.type = type; } - public VariableStatus getStatus() { + public @Nullable VariableStatus getStatus() { return status; } diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/JsonConfigParser.java b/core-api/src/main/java/com/optimizely/ab/config/parser/JsonConfigParser.java index 241189400..697b500dc 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/JsonConfigParser.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/JsonConfigParser.java @@ -320,7 +320,10 @@ private List parseLiveVariables(JSONArray liveVariablesJson) { String key = liveVariableObject.getString("key"); String defaultValue = liveVariableObject.getString("defaultValue"); VariableType type = VariableType.fromString(liveVariableObject.getString("type")); - VariableStatus status = VariableStatus.fromString(liveVariableObject.getString("status")); + VariableStatus status = null; + if (liveVariableObject.has("status")) { + status = VariableStatus.fromString(liveVariableObject.getString("status")); + } liveVariables.add(new LiveVariable(id, key, defaultValue, status, type)); }