From 8d75ed91d7d1fe50ea36a23940d73743d412600d Mon Sep 17 00:00:00 2001 From: wangjoshuah Date: Fri, 30 Jun 2017 13:42:22 -0700 Subject: [PATCH 1/6] add version 4 to the Project Config. Add features to the Project Config --- .../optimizely/ab/config/ProjectConfig.java | 56 ++++++++++++++++--- 1 file changed, 48 insertions(+), 8 deletions(-) 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 c41738581..5f5c2eb4a 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 @@ -37,7 +37,8 @@ public class ProjectConfig { public enum Version { V2 ("2"), - V3 ("3"); + V3 ("3"), + V4 ("4"); private final String version; @@ -56,11 +57,12 @@ public String toString() { private final String revision; private final String version; private final boolean anonymizeIP; - private final List groups; - private final List experiments; private final List attributes; - private final List events; private final List audiences; + private final List events; + private final List experiments; + private final List features; + private final List groups; private final List liveVariables; // convenience mappings for efficient lookup @@ -74,6 +76,7 @@ public String toString() { private final Map> liveVariableIdToExperimentsMapping; private final Map> variationToLiveVariableUsageInstanceMapping; + // v2 constructor public ProjectConfig(String accountId, String projectId, String version, String revision, List groups, List experiments, List attributes, List eventType, List audiences) { @@ -81,9 +84,39 @@ public ProjectConfig(String accountId, String projectId, String version, String null); } + // v3 constructor public ProjectConfig(String accountId, String projectId, String version, String revision, List groups, List experiments, List attributes, List eventType, List audiences, boolean anonymizeIP, List liveVariables) { + this( + accountId, + projectId, + revision, + version, + anonymizeIP, + attributes, + audiences, + eventType, + experiments, + null, + groups, + liveVariables + ); + } + + // v4 constructor + public ProjectConfig(String accountId, + String projectId, + String revision, + String version, + boolean anonymizeIP, + List attributes, + List audiences, + List events, + List experiments, + List features, + List groups, + List liveVariables) { this.accountId = accountId; this.projectId = projectId; @@ -91,19 +124,26 @@ public ProjectConfig(String accountId, String projectId, String version, String this.revision = revision; this.anonymizeIP = anonymizeIP; + this.attributes = Collections.unmodifiableList(attributes); + this.audiences = Collections.unmodifiableList(audiences); + this.events = Collections.unmodifiableList(events); + if (features == null) { + this.features = Collections.emptyList(); + } + else { + this.features = Collections.unmodifiableList(features); + } this.groups = Collections.unmodifiableList(groups); + List allExperiments = new ArrayList(); allExperiments.addAll(experiments); allExperiments.addAll(aggregateGroupExperiments(groups)); this.experiments = Collections.unmodifiableList(allExperiments); - this.attributes = Collections.unmodifiableList(attributes); - this.events = Collections.unmodifiableList(eventType); - this.audiences = Collections.unmodifiableList(audiences); // generate the name mappers this.experimentKeyMapping = ProjectConfigUtils.generateNameMapping(this.experiments); this.attributeKeyMapping = ProjectConfigUtils.generateNameMapping(attributes); - this.eventNameMapping = ProjectConfigUtils.generateNameMapping(events); + this.eventNameMapping = ProjectConfigUtils.generateNameMapping(this.events); // generate audience id to audience mapping this.audienceIdMapping = ProjectConfigUtils.generateIdMapping(audiences); From e357ddb6b1a161a1ae5654c4e72044c17a03fe7e Mon Sep 17 00:00:00 2001 From: wangjoshuah Date: Fri, 30 Jun 2017 14:32:34 -0700 Subject: [PATCH 2/6] add helper methods to ProjectConfigTestUtils to create a list or map of elements --- .../ab/config/ProjectConfigTestUtils.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) 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 d73efeb23..f91948464 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 @@ -32,6 +32,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; @@ -618,4 +619,26 @@ private static void verifyLiveVariableInstances(List } } } + + public static List createListOfObjects(T ... elements) { + ArrayList list = new ArrayList(elements.length); + for (T element : elements) { + list.add(element); + } + return list; + } + + public static Map createMapOfObjects(Listkeys, Listvalues) { + HashMap map = new HashMap(keys.size()); + if (keys.size() == values.size()) { + Iterator keysIterator = keys.iterator(); + Iterator valuesIterator = values.iterator(); + while (keysIterator.hasNext() && valuesIterator.hasNext()) { + K key = keysIterator.next(); + V value = valuesIterator.next(); + map.put(key, value); + } + } + return map; + } } From 3f40f80f1660af1a79a4813f7d272acdab569c36 Mon Sep 17 00:00:00 2001 From: wangjoshuah Date: Fri, 30 Jun 2017 15:04:08 -0700 Subject: [PATCH 3/6] alphabetize mappings and parameters in ProjectConfig. add feature key mapping --- .../optimizely/ab/config/ProjectConfig.java | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) 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 5f5c2eb4a..7f18ee381 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 @@ -65,14 +65,19 @@ public String toString() { private final List groups; private final List liveVariables; - // convenience mappings for efficient lookup - private final Map experimentKeyMapping; + // key to entity mappings private final Map attributeKeyMapping; - private final Map liveVariableKeyMapping; private final Map eventNameMapping; + private final Map experimentKeyMapping; + private final Map featureKeyMapping; + private final Map liveVariableKeyMapping; + + // id to entity mappings private final Map audienceIdMapping; private final Map experimentIdMapping; private final Map groupIdMapping; + + // other mappings private final Map> liveVariableIdToExperimentsMapping; private final Map> variationToLiveVariableUsageInstanceMapping; @@ -90,10 +95,10 @@ public ProjectConfig(String accountId, String projectId, String version, String List audiences, boolean anonymizeIP, List liveVariables) { this( accountId, + anonymizeIP, projectId, revision, version, - anonymizeIP, attributes, audiences, eventType, @@ -106,10 +111,10 @@ public ProjectConfig(String accountId, String projectId, String version, String // v4 constructor public ProjectConfig(String accountId, + boolean anonymizeIP, String projectId, String revision, String version, - boolean anonymizeIP, List attributes, List audiences, List events, @@ -133,6 +138,7 @@ public ProjectConfig(String accountId, else { this.features = Collections.unmodifiableList(features); } + this.groups = Collections.unmodifiableList(groups); List allExperiments = new ArrayList(); @@ -141,9 +147,10 @@ public ProjectConfig(String accountId, this.experiments = Collections.unmodifiableList(allExperiments); // generate the name mappers - this.experimentKeyMapping = ProjectConfigUtils.generateNameMapping(this.experiments); this.attributeKeyMapping = ProjectConfigUtils.generateNameMapping(attributes); this.eventNameMapping = ProjectConfigUtils.generateNameMapping(this.events); + this.experimentKeyMapping = ProjectConfigUtils.generateNameMapping(this.experiments); + this.featureKeyMapping = ProjectConfigUtils.generateNameMapping(this.features); // generate audience id to audience mapping this.audienceIdMapping = ProjectConfigUtils.generateIdMapping(audiences); From ee5ee948f3c1d160a1547c18a296d1dbf7ef37da Mon Sep 17 00:00:00 2001 From: wangjoshuah Date: Fri, 30 Jun 2017 15:04:41 -0700 Subject: [PATCH 4/6] update v4 test datafile with launched experiment --- .../config/valid-project-config-v4.json | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) 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 8da766ad0..e8311af51 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 @@ -191,6 +191,27 @@ "forcedVariations": { "Harry Potter": "Control" } + }, + { + "id": "3072915611", + "key": "launched_experiment", + "layerId": "3587821424", + "status": "Launched", + "variations": [ + { + "id": "1647582435", + "key": "launch_control", + "variables": [] + } + ], + "trafficAllocation": [ + { + "entityId": "1647582435", + "endOfRang": 8000 + } + ], + "audienceIds": [], + "forcedVariations": {} } ], "groups": [ From 9bd08a214577a2564ebcd775b7ff647b636cfd47 Mon Sep 17 00:00:00 2001 From: wangjoshuah Date: Mon, 3 Jul 2017 10:19:28 -0700 Subject: [PATCH 5/6] refactor feature to featureFlag --- .../main/java/com/optimizely/ab/Optimizely.java | 2 +- .../ab/config/{Feature.java => FeatureFlag.java} | 16 ++++++++-------- .../com/optimizely/ab/config/ProjectConfig.java | 14 +++++++------- .../ab/event/internal/payload/Feature.java | 2 +- .../ab/event/internal/EventBuilderV2Test.java | 2 +- .../config/valid-project-config-v4.json | 2 +- 6 files changed, 19 insertions(+), 19 deletions(-) rename core-api/src/main/java/com/optimizely/ab/config/{Feature.java => FeatureFlag.java} (80%) diff --git a/core-api/src/main/java/com/optimizely/ab/Optimizely.java b/core-api/src/main/java/com/optimizely/ab/Optimizely.java index e048a6511..a329cdb98 100644 --- a/core-api/src/main/java/com/optimizely/ab/Optimizely.java +++ b/core-api/src/main/java/com/optimizely/ab/Optimizely.java @@ -416,7 +416,7 @@ Double getVariableDouble(@Nonnull String variableKey, return null; } - //======== Feature APIs ========// + //======== FeatureFlag APIs ========// /** * Determine whether a boolean feature is enabled. diff --git a/core-api/src/main/java/com/optimizely/ab/config/Feature.java b/core-api/src/main/java/com/optimizely/ab/config/FeatureFlag.java similarity index 80% rename from core-api/src/main/java/com/optimizely/ab/config/Feature.java rename to core-api/src/main/java/com/optimizely/ab/config/FeatureFlag.java index 739c6cbce..66c8b2675 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/Feature.java +++ b/core-api/src/main/java/com/optimizely/ab/config/FeatureFlag.java @@ -23,10 +23,10 @@ import java.util.List; /** - * Represents a Feature definition at the project level + * Represents a FeatureFlag definition at the project level */ @JsonIgnoreProperties(ignoreUnknown = true) -public class Feature implements IdKeyMapped{ +public class FeatureFlag implements IdKeyMapped{ private final String id; private final String key; @@ -35,11 +35,11 @@ public class Feature implements IdKeyMapped{ private final List variables; @JsonCreator - public Feature(@JsonProperty("id") String id, - @JsonProperty("key") String key, - @JsonProperty("layerId") String layerId, - @JsonProperty("experimentIds") List experimentIds, - @JsonProperty("variables") List variables) { + public FeatureFlag(@JsonProperty("id") String id, + @JsonProperty("key") String key, + @JsonProperty("layerId") String layerId, + @JsonProperty("experimentIds") List experimentIds, + @JsonProperty("variables") List variables) { this.id = id; this.key = key; this.layerId = layerId; @@ -69,7 +69,7 @@ public List getVariables() { @Override public String toString() { - return "Feature{" + + return "FeatureFlag{" + "id='" + id + '\'' + ", key='" + key + '\'' + ", layerId='" + layerId + '\'' + 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 7f18ee381..fac0bbdce 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 @@ -61,7 +61,7 @@ public String toString() { private final List audiences; private final List events; private final List experiments; - private final List features; + private final List featureFlags; private final List groups; private final List liveVariables; @@ -69,7 +69,7 @@ public String toString() { private final Map attributeKeyMapping; private final Map eventNameMapping; private final Map experimentKeyMapping; - private final Map featureKeyMapping; + private final Map featureKeyMapping; private final Map liveVariableKeyMapping; // id to entity mappings @@ -119,7 +119,7 @@ public ProjectConfig(String accountId, List audiences, List events, List experiments, - List features, + List featureFlags, List groups, List liveVariables) { @@ -132,11 +132,11 @@ public ProjectConfig(String accountId, this.attributes = Collections.unmodifiableList(attributes); this.audiences = Collections.unmodifiableList(audiences); this.events = Collections.unmodifiableList(events); - if (features == null) { - this.features = Collections.emptyList(); + if (featureFlags == null) { + this.featureFlags = Collections.emptyList(); } else { - this.features = Collections.unmodifiableList(features); + this.featureFlags = Collections.unmodifiableList(featureFlags); } this.groups = Collections.unmodifiableList(groups); @@ -150,7 +150,7 @@ public ProjectConfig(String accountId, this.attributeKeyMapping = ProjectConfigUtils.generateNameMapping(attributes); this.eventNameMapping = ProjectConfigUtils.generateNameMapping(this.events); this.experimentKeyMapping = ProjectConfigUtils.generateNameMapping(this.experiments); - this.featureKeyMapping = ProjectConfigUtils.generateNameMapping(this.features); + this.featureKeyMapping = ProjectConfigUtils.generateNameMapping(this.featureFlags); // generate audience id to audience mapping this.audienceIdMapping = ProjectConfigUtils.generateIdMapping(audiences); diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Feature.java b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Feature.java index 22df21df9..161ee2271 100644 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Feature.java +++ b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Feature.java @@ -103,7 +103,7 @@ public int hashCode() { @Override public String toString() { - return "Feature{" + + return "FeatureFlag{" + "id='" + id + '\'' + ", name='" + name + '\'' + ", type='" + type + '\'' + diff --git a/core-api/src/test/java/com/optimizely/ab/event/internal/EventBuilderV2Test.java b/core-api/src/test/java/com/optimizely/ab/event/internal/EventBuilderV2Test.java index de5a7a379..205ad3a02 100644 --- a/core-api/src/test/java/com/optimizely/ab/event/internal/EventBuilderV2Test.java +++ b/core-api/src/test/java/com/optimizely/ab/event/internal/EventBuilderV2Test.java @@ -130,7 +130,7 @@ public void createImpressionEventIgnoresUnknownAttributes() throws Exception { Impression impression = gson.fromJson(impressionEvent.getBody(), Impression.class); - // verify that no Feature is created for "unknownAtrribute" -> "blahValue" + // verify that no FeatureFlag is created for "unknownAtrribute" -> "blahValue" for (Feature feature : impression.getUserFeatures()) { assertNotEquals(feature.getName(), "unknownAttribute"); assertNotEquals(feature.getValue(), "blahValue"); 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 e8311af51..0c994623c 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 @@ -302,7 +302,7 @@ ] } ], - "features": [ + "featureFlags": [ { "id": "4195505407", "key": "boolean_feature", From bb49b3b4c8d9446f7a302e9a883241d4dec89353 Mon Sep 17 00:00:00 2001 From: wangjoshuah Date: Mon, 3 Jul 2017 11:54:34 -0700 Subject: [PATCH 6/6] fix wrong comment --- .../com/optimizely/ab/event/internal/EventBuilderV2Test.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-api/src/test/java/com/optimizely/ab/event/internal/EventBuilderV2Test.java b/core-api/src/test/java/com/optimizely/ab/event/internal/EventBuilderV2Test.java index 205ad3a02..de5a7a379 100644 --- a/core-api/src/test/java/com/optimizely/ab/event/internal/EventBuilderV2Test.java +++ b/core-api/src/test/java/com/optimizely/ab/event/internal/EventBuilderV2Test.java @@ -130,7 +130,7 @@ public void createImpressionEventIgnoresUnknownAttributes() throws Exception { Impression impression = gson.fromJson(impressionEvent.getBody(), Impression.class); - // verify that no FeatureFlag is created for "unknownAtrribute" -> "blahValue" + // verify that no Feature is created for "unknownAtrribute" -> "blahValue" for (Feature feature : impression.getUserFeatures()) { assertNotEquals(feature.getName(), "unknownAttribute"); assertNotEquals(feature.getValue(), "blahValue");