From ed55d4382f26ba19b5ebeee9243c01f7ed0c468c Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Fri, 25 Jun 2021 09:44:42 -0400 Subject: [PATCH 01/65] [OASIS-7798] - Add support for attributes and events --- .../optimizelyconfig/OptimizelyAttribute.java | 53 ++++++++++++++++ .../ab/optimizelyconfig/OptimizelyConfig.java | 34 +++++++++++ .../OptimizelyConfigService.java | 38 ++++++++++++ .../ab/optimizelyconfig/OptimizelyEvent.java | 61 +++++++++++++++++++ .../OptimizelyAttributeTest.java | 39 ++++++++++++ .../OptimizelyConfigServiceTest.java | 40 +++++++++++- .../optimizelyconfig/OptimizelyEventTest.java | 26 ++++++++ 7 files changed, 290 insertions(+), 1 deletion(-) create mode 100644 core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyAttribute.java create mode 100644 core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyEvent.java create mode 100644 core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyAttributeTest.java create mode 100644 core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyEventTest.java diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyAttribute.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyAttribute.java new file mode 100644 index 000000000..2c142bc86 --- /dev/null +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyAttribute.java @@ -0,0 +1,53 @@ +/**************************************************************************** + * Copyright 2021, Optimizely, Inc. 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.optimizelyconfig; + +import com.optimizely.ab.config.IdKeyMapped; + +/** + * Represents the Attribute's map {@link OptimizelyConfig} + */ +public class OptimizelyAttribute implements IdKeyMapped { + + private String id; + private String key; + + public OptimizelyAttribute(String id, + String key) { + this.id = id; + this.key = key; + } + + public String getId() { return id; } + + public String getKey() { return key; } + + @Override + public boolean equals(Object obj) { + if (obj == null || getClass() != obj.getClass()) return false; + if (obj == this) return true; + OptimizelyAttribute optimizelyAttribute = (OptimizelyAttribute) obj; + return id.equals(optimizelyAttribute.getId()) && + key.equals(optimizelyAttribute.getKey()); + } + + @Override + public int hashCode() { + int hash = id.hashCode(); + hash = 31 * hash + key.hashCode(); + return hash; + } +} diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java index c1640ff44..edb5f5812 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java @@ -17,6 +17,8 @@ import com.fasterxml.jackson.annotation.JsonInclude; +import com.optimizely.ab.config.Attribute; +import com.optimizely.ab.config.EventType; import java.util.*; @@ -28,6 +30,8 @@ public class OptimizelyConfig { private Map experimentsMap; private Map featuresMap; + private List attributes; + private List events; private String revision; private String sdkKey; private String environmentKey; @@ -53,6 +57,32 @@ public OptimizelyConfig(Map experimentsMap, this.datafile = datafile; } + public OptimizelyConfig(Map experimentsMap, + Map featuresMap, + String revision, String sdkKey, String environmentKey, + List attributes, + List events) { + this(experimentsMap, featuresMap, revision, sdkKey, environmentKey, attributes, events, null); + } + + public OptimizelyConfig(Map experimentsMap, + Map featuresMap, + String revision, + String sdkKey, + String environmentKey, + List attributes, + List events, + String datafile) { + this.experimentsMap = experimentsMap; + this.featuresMap = featuresMap; + this.revision = revision; + this.sdkKey = sdkKey; + this.environmentKey = environmentKey; + this.attributes = attributes; + this.events = events; + this.datafile = datafile; + } + public Map getExperimentsMap() { return experimentsMap; } @@ -61,6 +91,10 @@ public Map getFeaturesMap() { return featuresMap; } + public List getAttributes() { return attributes; } + + public List getEvents() { return events; } + public String getRevision() { return revision; } diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java index af1965ce1..88e246d24 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java @@ -34,6 +34,8 @@ public OptimizelyConfigService(ProjectConfig projectConfig) { projectConfig.getRevision(), projectConfig.getSdkKey(), projectConfig.getEnvironmentKey(), + projectConfig.getAttributes(), + projectConfig.getEventTypes(), projectConfig.toDatafile() ); } @@ -215,4 +217,40 @@ Map getFeatureVariablesMap(List fea return featureVariableKeyMap; } + + @VisibleForTesting + Map getAttributesMap(List attributes) { + if (attributes == null) { + return Collections.emptyMap(); + } + + Map attributeKeyMap = new HashMap<>(); + + for(Attribute attribute : attributes) { + attributeKeyMap.put(attribute.getKey(), new OptimizelyAttribute( + attribute.getId(), + attribute.getKey() + )); + } + return attributeKeyMap; + } + + @VisibleForTesting + Map getEventsMap(List events) { + if (events == null) { + return Collections.emptyMap(); + } + + Map eventsKeyMap = new HashMap<>(); + + for(EventType event : events) { + eventsKeyMap.put(event.getKey(), new OptimizelyEvent( + event.getId(), + event.getKey(), + event.getExperimentIds() + )); + } + + return eventsKeyMap; + } } diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyEvent.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyEvent.java new file mode 100644 index 000000000..9edda8700 --- /dev/null +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyEvent.java @@ -0,0 +1,61 @@ +/**************************************************************************** + * Copyright 2021, Optimizely, Inc. 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.optimizelyconfig; + +import com.optimizely.ab.config.IdKeyMapped; + +import java.util.List; + +/** + * Represents the Events's map {@link OptimizelyConfig} + */ +public class OptimizelyEvent implements IdKeyMapped { + + private String id; + private String key; + private List experimentIds; + + public OptimizelyEvent(String id, + String key, + List experimentIds) { + this.id = id; + this.key = key; + this.experimentIds = experimentIds; + } + + public String getId() { return id; } + + public String getKey() { return key; } + + public List getExperimentIds() { return experimentIds; } + + @Override + public boolean equals(Object obj) { + if (obj == null || getClass() != obj.getClass()) return false; + if (obj == this) return true; + OptimizelyEvent optimizelyEvent = (OptimizelyEvent) obj; + return id.equals(optimizelyEvent.getId()) && + key.equals(optimizelyEvent.getKey()) && + experimentIds.equals(optimizelyEvent.getExperimentIds()); + } + + @Override + public int hashCode() { + int hash = id.hashCode(); + hash = 31 * hash + experimentIds.hashCode(); + return hash; + } +} diff --git a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyAttributeTest.java b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyAttributeTest.java new file mode 100644 index 000000000..904d5e2d7 --- /dev/null +++ b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyAttributeTest.java @@ -0,0 +1,39 @@ +/**************************************************************************** + * Copyright 2021, Optimizely, Inc. 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.optimizelyconfig; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class OptimizelyAttributeTest { + + @Test + public void testOptimizelyAttribute() { + OptimizelyAttribute optimizelyAttribute1 = new OptimizelyAttribute( + "5", + "test_attribute" + ); + OptimizelyAttribute optimizelyAttribute2 = new OptimizelyAttribute( + "5", + "test_attribute" + ); + assertEquals("5", optimizelyAttribute1.getId()); + assertEquals("test_attribute", optimizelyAttribute1.getKey()); + assertEquals(optimizelyAttribute1, optimizelyAttribute2); + assertEquals(optimizelyAttribute1.hashCode(), optimizelyAttribute2.hashCode()); + } +} diff --git a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java index 52bc06181..120883232 100644 --- a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java +++ b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java @@ -156,6 +156,22 @@ public void testGetMergedVariablesMap() { assertEquals(expectedOptimizelyVariableMap, optimizelyVariableMap); } + @Test + public void testGetAttributesMap(){ + Map attributesMap = optimizelyConfigService.getAttributesMap(projectConfig.getAttributes()); + Map expectedAttributesMap = optimizelyConfigService.getAttributesMap(expectedConfig.getAttributes()); + assertEquals(expectedAttributesMap.size(), attributesMap.size()); + assertEquals(expectedAttributesMap, attributesMap); + } + + @Test + public void testGetEventsMap(){ + Map eventsMap = optimizelyConfigService.getEventsMap(projectConfig.getEventTypes()); + Map expectedEventsMap = optimizelyConfigService.getEventsMap(expectedConfig.getEvents()); + assertEquals(expectedEventsMap.size(), eventsMap.size()); + assertEquals(expectedEventsMap, eventsMap); + } + private ProjectConfig generateOptimizelyConfig() { return new DatafileProjectConfig( "2360254204", @@ -526,7 +542,29 @@ OptimizelyConfig getExpectedConfig() { optimizelyFeatureMap, "1480511547", "ValidProjectConfigV4", - "production" + "production", + asList( + new Attribute( + "553339214", + "house" + ), + new Attribute( + "58339410", + "nationality" + ) + ), + asList( + new EventType( + "3785620495", + "basic_event", + asList("1323241596", "2738374745", "3042640549", "3262035800", "3072915611") + ), + new EventType( + "3195631717", + "event_with_paused_experiment", + asList("2667098701") + ) + ) ); } } diff --git a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyEventTest.java b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyEventTest.java new file mode 100644 index 000000000..9e292b17a --- /dev/null +++ b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyEventTest.java @@ -0,0 +1,26 @@ +package com.optimizely.ab.optimizelyconfig; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static java.util.Arrays.asList; + +public class OptimizelyEventTest { + @Test + public void testOptimizelyEvent() { + OptimizelyEvent optimizelyEvent1 = new OptimizelyEvent( + "5", + "test_event", + asList("123","234","345") + ); + OptimizelyEvent optimizelyEvent2 = new OptimizelyEvent( + "5", + "test_event", + asList("123","234","345") + ); + assertEquals("5", optimizelyEvent1.getId()); + assertEquals("test_event", optimizelyEvent1.getKey()); + assertEquals(optimizelyEvent1, optimizelyEvent2); + assertEquals(optimizelyEvent1.hashCode(), optimizelyEvent2.hashCode()); + } +} From ebf3a7b1944bc8bb8357349d5dd8e4e8696911f2 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Fri, 25 Jun 2021 10:21:13 -0400 Subject: [PATCH 02/65] Add copyright header to OptimizelyEventTest --- .../ab/optimizelyconfig/OptimizelyEventTest.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyEventTest.java b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyEventTest.java index 9e292b17a..5bd5d9a4c 100644 --- a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyEventTest.java +++ b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyEventTest.java @@ -1,5 +1,19 @@ +/**************************************************************************** + * Copyright 2020-2021, Optimizely, Inc. 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.optimizelyconfig; - import org.junit.Test; import static org.junit.Assert.assertEquals; From a5eb5c93c33103015d59d8b72311c8c652622b60 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Tue, 29 Jun 2021 09:22:59 -0400 Subject: [PATCH 03/65] Changes made to remove getAttributesMap and getEventsMap as well as change lists to use OptimizelyAttribute and OptimizelyEvent. Convert Attribute and EventTypes to lists of OptimizleyAttribute and OptimizelyEvent lists before calling OptimizelyConfig constructor. --- .../ab/optimizelyconfig/OptimizelyConfig.java | 16 ++--- .../OptimizelyConfigService.java | 61 +++++++------------ .../OptimizelyConfigServiceTest.java | 24 ++------ 3 files changed, 35 insertions(+), 66 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java index edb5f5812..aa786d999 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java @@ -30,8 +30,8 @@ public class OptimizelyConfig { private Map experimentsMap; private Map featuresMap; - private List attributes; - private List events; + private List attributes; + private List events; private String revision; private String sdkKey; private String environmentKey; @@ -60,8 +60,8 @@ public OptimizelyConfig(Map experimentsMap, public OptimizelyConfig(Map experimentsMap, Map featuresMap, String revision, String sdkKey, String environmentKey, - List attributes, - List events) { + List attributes, + List events) { this(experimentsMap, featuresMap, revision, sdkKey, environmentKey, attributes, events, null); } @@ -70,8 +70,8 @@ public OptimizelyConfig(Map experimentsMap, String revision, String sdkKey, String environmentKey, - List attributes, - List events, + List attributes, + List events, String datafile) { this.experimentsMap = experimentsMap; this.featuresMap = featuresMap; @@ -91,9 +91,9 @@ public Map getFeaturesMap() { return featuresMap; } - public List getAttributes() { return attributes; } + public List getAttributes() { return attributes; } - public List getEvents() { return events; } + public List getEvents() { return events; } public String getRevision() { return revision; diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java index 88e246d24..0ef672e46 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java @@ -27,15 +27,36 @@ public class OptimizelyConfigService { public OptimizelyConfigService(ProjectConfig projectConfig) { this.projectConfig = projectConfig; + List optimizelyAttributes = new ArrayList<>(); + List optimizelyEvents = new ArrayList<>(); + Map experimentsMap = getExperimentsMap(); + + for(Attribute attribute : projectConfig.getAttributes()){ + OptimizelyAttribute copyAttribute = new OptimizelyAttribute( + attribute.getId(), + attribute.getKey() + ); + optimizelyAttributes.add(copyAttribute); + } + + for(EventType event : projectConfig.getEventTypes()){ + OptimizelyEvent copyEvent = new OptimizelyEvent( + event.getId(), + event.getKey(), + event.getExperimentIds() + ); + optimizelyEvents.add(copyEvent); + } + optimizelyConfig = new OptimizelyConfig( experimentsMap, getFeaturesMap(experimentsMap), projectConfig.getRevision(), projectConfig.getSdkKey(), projectConfig.getEnvironmentKey(), - projectConfig.getAttributes(), - projectConfig.getEventTypes(), + optimizelyAttributes, + optimizelyEvents, projectConfig.toDatafile() ); } @@ -217,40 +238,4 @@ Map getFeatureVariablesMap(List fea return featureVariableKeyMap; } - - @VisibleForTesting - Map getAttributesMap(List attributes) { - if (attributes == null) { - return Collections.emptyMap(); - } - - Map attributeKeyMap = new HashMap<>(); - - for(Attribute attribute : attributes) { - attributeKeyMap.put(attribute.getKey(), new OptimizelyAttribute( - attribute.getId(), - attribute.getKey() - )); - } - return attributeKeyMap; - } - - @VisibleForTesting - Map getEventsMap(List events) { - if (events == null) { - return Collections.emptyMap(); - } - - Map eventsKeyMap = new HashMap<>(); - - for(EventType event : events) { - eventsKeyMap.put(event.getKey(), new OptimizelyEvent( - event.getId(), - event.getKey(), - event.getExperimentIds() - )); - } - - return eventsKeyMap; - } } diff --git a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java index 120883232..6e07505b5 100644 --- a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java +++ b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java @@ -156,22 +156,6 @@ public void testGetMergedVariablesMap() { assertEquals(expectedOptimizelyVariableMap, optimizelyVariableMap); } - @Test - public void testGetAttributesMap(){ - Map attributesMap = optimizelyConfigService.getAttributesMap(projectConfig.getAttributes()); - Map expectedAttributesMap = optimizelyConfigService.getAttributesMap(expectedConfig.getAttributes()); - assertEquals(expectedAttributesMap.size(), attributesMap.size()); - assertEquals(expectedAttributesMap, attributesMap); - } - - @Test - public void testGetEventsMap(){ - Map eventsMap = optimizelyConfigService.getEventsMap(projectConfig.getEventTypes()); - Map expectedEventsMap = optimizelyConfigService.getEventsMap(expectedConfig.getEvents()); - assertEquals(expectedEventsMap.size(), eventsMap.size()); - assertEquals(expectedEventsMap, eventsMap); - } - private ProjectConfig generateOptimizelyConfig() { return new DatafileProjectConfig( "2360254204", @@ -544,22 +528,22 @@ OptimizelyConfig getExpectedConfig() { "ValidProjectConfigV4", "production", asList( - new Attribute( + new OptimizelyAttribute( "553339214", "house" ), - new Attribute( + new OptimizelyAttribute( "58339410", "nationality" ) ), asList( - new EventType( + new OptimizelyEvent( "3785620495", "basic_event", asList("1323241596", "2738374745", "3042640549", "3262035800", "3072915611") ), - new EventType( + new OptimizelyEvent( "3195631717", "event_with_paused_experiment", asList("2667098701") From 13c5a968b237d7a7fa533417506805232c9184f6 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Mon, 19 Jul 2021 14:35:51 -0400 Subject: [PATCH 04/65] Clean up serializers for default case in switch statement, same for conditionUtils. Audience implementation and addition of OptimizelyAudience class. Updated OptimizelyConfig with Audiences and added implementation in OptimizelyConfigService. Updated OptimizelyExperiment to get the serialized audienceConditions list from experiment. Test cases will come in additional Commits. --- .../com/optimizely/ab/config/Experiment.java | 103 +++++++++++++++- .../parser/ConditionJacksonDeserializer.java | 3 - .../ab/config/parser/JsonConfigParser.java | 10 +- .../ab/internal/ConditionUtils.java | 25 +--- .../optimizelyconfig/OptimizelyAudience.java | 65 +++++++++++ .../ab/optimizelyconfig/OptimizelyConfig.java | 10 +- .../OptimizelyConfigService.java | 110 +++++++++++++++++- .../OptimizelyExperiment.java | 5 + .../optimizelyconfig/OptimizelyFeature.java | 16 +++ .../OptimizelyConfigServiceTest.java | 7 ++ 10 files changed, 317 insertions(+), 37 deletions(-) create mode 100644 core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyAudience.java diff --git a/core-api/src/main/java/com/optimizely/ab/config/Experiment.java b/core-api/src/main/java/com/optimizely/ab/config/Experiment.java index e77becd39..ceb89e14c 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/Experiment.java +++ b/core-api/src/main/java/com/optimizely/ab/config/Experiment.java @@ -18,12 +18,12 @@ import com.fasterxml.jackson.annotation.*; import com.optimizely.ab.annotations.VisibleForTesting; -import com.optimizely.ab.config.audience.AudienceIdCondition; -import com.optimizely.ab.config.audience.Condition; +import com.optimizely.ab.config.audience.*; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; @@ -44,7 +44,7 @@ public class Experiment implements IdKeyMapped { private final String groupId; private final List audienceIds; - private final Condition audienceConditions; + private final Condition audienceConditions; private final List variations; private final List trafficAllocation; @@ -173,6 +173,103 @@ public boolean isLaunched() { return status.equals(ExperimentStatus.LAUNCHED.toString()); } + public String serializeConditions(Map audiencesMap) { + + Condition condition = this.audienceConditions; + + return this.serialize(condition, audiencesMap); + } + + private String getNameFromAudienceId(String audienceId, Map audiencesMap) { + String audienceName = "\"" + audiencesMap.get(audienceId) + "\""; + return audienceName != null ? audienceName : "\"" + audienceId + "\""; + } + + private String getOperandOrAudienceId(Condition condition, Map audiencesMap){ + String operand = ""; + if(condition != null) { + if (condition instanceof AndCondition) { + operand = "AND"; + } else if (condition instanceof NotCondition) { + operand = "NOT"; + } else if (condition instanceof OrCondition) { + operand = "OR"; + } else if (condition instanceof AudienceIdCondition) { + String audienceName = this.getNameFromAudienceId(((AudienceIdCondition) condition).getAudienceId(), + audiencesMap); + operand = audienceName; + } + } + return operand; + } + + public String serialize(Condition condition, Map audiencesMap) { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(""); + List conditions; + + String operand = this.getOperandOrAudienceId(condition, audiencesMap); + switch(operand){ + case("AND"): + conditions = ((AndCondition) condition).getConditions(); + stringBuilder.append(this.helper(operand, conditions, audiencesMap)); + break; + case("OR"): + conditions = ((OrCondition) condition).getConditions(); + stringBuilder.append(this.helper(operand, conditions, audiencesMap)); + break; + case("NOT"): + stringBuilder.append(operand + " "); + Condition notCondition = ((NotCondition) condition).getCondition(); + if(notCondition instanceof AudienceIdCondition) { + stringBuilder.append(serialize(notCondition, audiencesMap)); + }else { + stringBuilder.append("(" + serialize(notCondition, audiencesMap)+ ")"); + } + break; + default: + stringBuilder.append(operand); + break; + } + + return stringBuilder.toString(); + } + + public String helper(String operand, List conditions, Map audiencesMap) { + StringBuilder stringBuilder = new StringBuilder(); + int ctr = 0; + if(conditions.isEmpty()) { + return ""; + }else if(conditions.size() == 1) { + return serialize(conditions.get(0), audiencesMap); + } + if(conditions.size() > 1) { + for (Condition con : conditions) { + ctr++; + if(ctr + 1 <= conditions.size()) { + if(con instanceof AudienceIdCondition) { + String audienceName = this.getNameFromAudienceId(((AudienceIdCondition) con).getAudienceId(), + audiencesMap); + stringBuilder.append( audienceName + " "); + }else { + stringBuilder.append("(" + serialize(con, audiencesMap) + ") "); + } + stringBuilder.append(operand); + stringBuilder.append(" "); + }else { + if(con instanceof AudienceIdCondition) { + String audienceName = this.getNameFromAudienceId(((AudienceIdCondition) con).getAudienceId(), + audiencesMap); + stringBuilder.append(audienceName); + }else { + stringBuilder.append("(" + serialize(con, audiencesMap) + ")"); + } + } + } + } + return stringBuilder.toString(); + } + @Override public String toString() { return "Experiment{" + diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/ConditionJacksonDeserializer.java b/core-api/src/main/java/com/optimizely/ab/config/parser/ConditionJacksonDeserializer.java index ca57ac0af..26dd59160 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/ConditionJacksonDeserializer.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/ConditionJacksonDeserializer.java @@ -121,9 +121,6 @@ protected static Condition parseConditions(Class clazz, ObjectMapper obje case "and": condition = new AndCondition(conditions); break; - case "or": - condition = new OrCondition(conditions); - break; case "not": condition = new NotCondition(conditions.isEmpty() ? new NullCondition() : conditions.get(0)); break; 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 c33f30a68..58a0f0a83 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 @@ -16,6 +16,7 @@ */ package com.optimizely.ab.config.parser; +import com.fasterxml.jackson.core.JsonProcessingException; import com.optimizely.ab.config.*; import com.optimizely.ab.config.Experiment.ExperimentStatus; import com.optimizely.ab.config.audience.Audience; @@ -117,11 +118,11 @@ public ProjectConfig parseProjectConfig(@Nonnull String json) throws ConfigParse //======== Helper methods ========// - private List parseExperiments(JSONArray experimentJson) { + private List parseExperiments(JSONArray experimentJson) throws JsonProcessingException { return parseExperiments(experimentJson, ""); } - private List parseExperiments(JSONArray experimentJson, String groupId) { + private List parseExperiments(JSONArray experimentJson, String groupId) throws JsonProcessingException { List experiments = new ArrayList(experimentJson.length()); for (int i = 0; i < experimentJson.length(); i++) { @@ -142,6 +143,7 @@ private List parseExperiments(JSONArray experimentJson, String group } Condition conditions = null; + if (experimentObject.has("audienceConditions")) { Object jsonCondition = experimentObject.get("audienceConditions"); conditions = ConditionUtils.parseConditions(AudienceIdCondition.class, jsonCondition); @@ -326,7 +328,7 @@ private List parseTypedAudiences(JSONArray audienceJson) { return audiences; } - private List parseGroups(JSONArray groupJson) { + private List parseGroups(JSONArray groupJson) throws JsonProcessingException { List groups = new ArrayList(groupJson.length()); for (int i = 0; i < groupJson.length(); i++) { @@ -384,7 +386,7 @@ private List parseFeatureVariableInstances(JSONArr return featureVariableUsageInstances; } - private List parseRollouts(JSONArray rolloutsJson) { + private List parseRollouts(JSONArray rolloutsJson) throws JsonProcessingException { List rollouts = new ArrayList(rolloutsJson.length()); for (int i = 0; i < rolloutsJson.length(); i++) { diff --git a/core-api/src/main/java/com/optimizely/ab/internal/ConditionUtils.java b/core-api/src/main/java/com/optimizely/ab/internal/ConditionUtils.java index d9af5b58b..d9d7e11f4 100644 --- a/core-api/src/main/java/com/optimizely/ab/internal/ConditionUtils.java +++ b/core-api/src/main/java/com/optimizely/ab/internal/ConditionUtils.java @@ -147,23 +147,7 @@ static public Condition parseConditions(Class clazz, List rawObje conditions.add(parseConditions(clazz, obj)); } - Condition condition; - switch (operand) { - case "and": - condition = new AndCondition(conditions); - break; - case "or": - condition = new OrCondition(conditions); - break; - case "not": - condition = new NotCondition(conditions.isEmpty() ? new NullCondition() : conditions.get(0)); - break; - default: - condition = new OrCondition(conditions); - break; - } - - return condition; + return buildCondition(operand, conditions); } static public String operand(Object object) { @@ -210,14 +194,15 @@ static public Condition parseConditions(Class clazz, org.json.JSONArray c conditions.add(parseConditions(clazz, obj)); } + return buildCondition(operand, conditions); + } + + private static Condition buildCondition(String operand, List conditions) { Condition condition; switch (operand) { case "and": condition = new AndCondition(conditions); break; - case "or": - condition = new OrCondition(conditions); - break; case "not": condition = new NotCondition(conditions.isEmpty() ? new NullCondition() : conditions.get(0)); break; diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyAudience.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyAudience.java new file mode 100644 index 000000000..da9db0a1b --- /dev/null +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyAudience.java @@ -0,0 +1,65 @@ +/**************************************************************************** + * Copyright 2021, Optimizely, Inc. 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.optimizelyconfig; + +import com.optimizely.ab.config.IdKeyMapped; +import com.optimizely.ab.config.audience.Condition; + +import java.util.List; + +/** + * Represents the Audiences list {@link OptimizelyConfig} + */ +public class OptimizelyAudience implements IdKeyMapped{ + + private String id; + private String name; + private String conditions; + + public OptimizelyAudience(String id, + String name, + String conditions) { + this.id = id; + this.name = name; + this.conditions = conditions; + } + + public String getId() { return id; } + + public String getName() { return name; } + + public String getKey() { return name; } + + public String getConditions() { return conditions; } + + @Override + public boolean equals(Object obj) { + if (obj == null || getClass() != obj.getClass()) return false; + if (obj == this) return true; + OptimizelyAudience optimizelyAudience = (OptimizelyAudience) obj; + return id.equals(optimizelyAudience.getId()) && + name.equals(optimizelyAudience.getKey()) && + conditions.equals(optimizelyAudience.getConditions()); + } + + @Override + public int hashCode() { + int hash = id.hashCode(); + hash = 31 * hash + conditions.hashCode(); + return hash; + } + +} diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java index aa786d999..f2498cd98 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java @@ -32,6 +32,7 @@ public class OptimizelyConfig { private Map featuresMap; private List attributes; private List events; + private List audiences; private String revision; private String sdkKey; private String environmentKey; @@ -61,8 +62,9 @@ public OptimizelyConfig(Map experimentsMap, Map featuresMap, String revision, String sdkKey, String environmentKey, List attributes, - List events) { - this(experimentsMap, featuresMap, revision, sdkKey, environmentKey, attributes, events, null); + List events, + List audiences) { + this(experimentsMap, featuresMap, revision, sdkKey, environmentKey, attributes, events, audiences, null); } public OptimizelyConfig(Map experimentsMap, @@ -72,6 +74,7 @@ public OptimizelyConfig(Map experimentsMap, String environmentKey, List attributes, List events, + List audiences, String datafile) { this.experimentsMap = experimentsMap; this.featuresMap = featuresMap; @@ -80,6 +83,7 @@ public OptimizelyConfig(Map experimentsMap, this.environmentKey = environmentKey; this.attributes = attributes; this.events = events; + this.audiences = audiences; this.datafile = datafile; } @@ -95,6 +99,8 @@ public Map getFeaturesMap() { public List getEvents() { return events; } + public List getAudiences() { return audiences; } + public String getRevision() { return revision; } diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java index 0ef672e46..29519668f 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java @@ -17,15 +17,22 @@ import com.optimizely.ab.annotations.VisibleForTesting; import com.optimizely.ab.config.*; +import com.optimizely.ab.config.audience.Audience; +import com.optimizely.ab.config.audience.AudienceIdCondition; +import com.optimizely.ab.config.audience.Condition; + import java.util.*; +import java.util.stream.Collectors; public class OptimizelyConfigService { private ProjectConfig projectConfig; private OptimizelyConfig optimizelyConfig; + private List audiences; public OptimizelyConfigService(ProjectConfig projectConfig) { this.projectConfig = projectConfig; + this.audiences = getAudiencesList(projectConfig.getTypedAudiences(), projectConfig.getAudiences()); List optimizelyAttributes = new ArrayList<>(); List optimizelyEvents = new ArrayList<>(); @@ -40,7 +47,7 @@ public OptimizelyConfigService(ProjectConfig projectConfig) { optimizelyAttributes.add(copyAttribute); } - for(EventType event : projectConfig.getEventTypes()){ + for(EventType event : projectConfig.getEventTypes()) { OptimizelyEvent copyEvent = new OptimizelyEvent( event.getId(), event.getKey(), @@ -57,6 +64,7 @@ public OptimizelyConfigService(ProjectConfig projectConfig) { projectConfig.getEnvironmentKey(), optimizelyAttributes, optimizelyEvents, + this.audiences, projectConfig.toDatafile() ); } @@ -100,12 +108,25 @@ Map getExperimentsMap() { return Collections.emptyMap(); } Map featureExperimentMap = new HashMap<>(); + Map audiencesMap = new HashMap<>(); + + // Build audienceMap as [id:name] + for(OptimizelyAudience audience: this.audiences) { + audiencesMap.put( + audience.getId(), + audience.getName() + ); + } + for (Experiment experiment : experiments) { - featureExperimentMap.put(experiment.getKey(), new OptimizelyExperiment( + OptimizelyExperiment optimizelyExperiment = new OptimizelyExperiment( experiment.getId(), experiment.getKey(), getVariationsMap(experiment.getVariations(), experiment.getId()) - )); + ); + + optimizelyExperiment.setAudiences(experiment.serializeConditions(audiencesMap)); + featureExperimentMap.put(experiment.getKey(), optimizelyExperiment); } return featureExperimentMap; } @@ -195,16 +216,57 @@ Map getFeaturesMap(Map Map optimizelyFeatureKeyMap = new HashMap<>(); for (FeatureFlag featureFlag : featureFlags) { - optimizelyFeatureKeyMap.put(featureFlag.getKey(), new OptimizelyFeature( + Map experimentsMapForFeature = + getExperimentsMapForFeature(featureFlag.getExperimentIds(), allExperimentsMap); + OptimizelyFeature optimizelyFeature = new OptimizelyFeature( featureFlag.getId(), featureFlag.getKey(), getExperimentsMapForFeature(featureFlag.getExperimentIds(), allExperimentsMap), getFeatureVariablesMap(featureFlag.getVariables()) - )); + ); + + List experimentRules = + new ArrayList(experimentsMapForFeature.values()); + List deliveryRules = + this.getDeliveryRules(this.projectConfig.getRollouts(), featureFlag.getRolloutId()); + + optimizelyFeature.setDeliveryRules(deliveryRules); + optimizelyFeature.setExperimentRules(experimentRules); + + optimizelyFeatureKeyMap.put(featureFlag.getKey(), optimizelyFeature); } return optimizelyFeatureKeyMap; } + List getDeliveryRules(List rollouts, String rolloutId) { + + List deliveryRules = new ArrayList(); + + Map audiencesMap = new HashMap<>(); + + Rollout rollout = rollouts.stream().filter(r -> r.getId() == rolloutId).collect(Collectors.toList()).get(0); + + if(rollout != null) { + for(OptimizelyAudience optimizelyAudience: this.audiences) { + audiencesMap.put(optimizelyAudience.getId(), optimizelyAudience.getName()); + } + + List rolloutExperiments = rollout.getExperiments(); + for(Experiment experiment: rolloutExperiments) { + OptimizelyExperiment optimizelyExperiment = new OptimizelyExperiment( + experiment.getId(), + experiment.getKey(), + this.getVariationsMap(experiment.getVariations(), experiment.getId()) + ); + + optimizelyExperiment.setAudiences(experiment.serializeConditions(audiencesMap)); + deliveryRules.add(optimizelyExperiment); + } + } + + return Collections.emptyList(); + } + @VisibleForTesting Map getExperimentsMapForFeature(List experimentIds, Map allExperimentsMap) { if (experimentIds == null) { @@ -238,4 +300,42 @@ Map getFeatureVariablesMap(List fea return featureVariableKeyMap; } + + @VisibleForTesting + List getAudiencesList(List typedAudiences, List audiences) { + List audiencesList = new ArrayList<>(); + + /* + * This method merges typedAudiences with audiences from the Project + * config. Precedence is given to typedAudiences over audiences. + * + * Returns: + * A new list with the merged audiences as OptimizelyAudience objects. + * */ + + // Convert existing Typed Audiences to OptimizelyAudience Objects + for(Audience audience: typedAudiences) { + OptimizelyAudience optimizelyAudience = new OptimizelyAudience( + audience.getId(), + audience.getName(), + audience.getConditions().toString() + ); + audiencesList.add(optimizelyAudience); + } + + for(Audience audience: audiences) { + if(typedAudiences.stream().filter(a -> a.getId() == audience.getId()).collect(Collectors.toList()).size() == 0) { + if(audience.getId() != "$opt_dummy_audience") { + OptimizelyAudience optimizelyAudience = new OptimizelyAudience( + audience.getId(), + audience.getName(), + audience.getConditions().toString() + ); + audiencesList.add(optimizelyAudience); + } + } + } + + return audiencesList; + } } diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyExperiment.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyExperiment.java index fd66e9aab..b511dde8b 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyExperiment.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyExperiment.java @@ -26,6 +26,7 @@ public class OptimizelyExperiment implements IdKeyMapped { private String id; private String key; + private String audiences = ""; private Map variationsMap; public OptimizelyExperiment(String id, String key, Map variationsMap) { @@ -42,10 +43,14 @@ public String getKey() { return key; } + public String getAudiences() { return audiences; } + public Map getVariationsMap() { return variationsMap; } + public void setAudiences(String audiences) { this.audiences = audiences; } + @Override public boolean equals(Object obj) { if (obj == null || getClass() != obj.getClass()) return false; diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java index 954f7b14e..3fb525266 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java @@ -17,6 +17,7 @@ import com.optimizely.ab.config.IdKeyMapped; +import java.util.List; import java.util.Map; /** @@ -27,6 +28,9 @@ public class OptimizelyFeature implements IdKeyMapped { private String id; private String key; + private List deliveryRules; + private List experimentRules; + private Map experimentsMap; private Map variablesMap; @@ -56,6 +60,18 @@ public Map getVariablesMap() { return variablesMap; } + public List getExperimentRules() { return experimentRules; } + + public List getDeliveryRules() { return deliveryRules; } + + public void setExperimentRules(List experimentRules) { + this.experimentRules = experimentRules; + } + + public void setDeliveryRules(List deliveryRules) { + this.deliveryRules = deliveryRules; + } + @Override public boolean equals(Object obj) { if (obj == null || getClass() != obj.getClass()) return false; diff --git a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java index 6e07505b5..d44ab9b57 100644 --- a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java +++ b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java @@ -548,6 +548,13 @@ OptimizelyConfig getExpectedConfig() { "event_with_paused_experiment", asList("2667098701") ) + ), + asList( + new OptimizelyAudience( + "123456", + "test_audience_1", + "['and', ['or', '1', '2'], '3']" + ) ) ); } From 5b83801520689d19742291d0779424c393661c1b Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Mon, 19 Jul 2021 15:44:32 -0400 Subject: [PATCH 05/65] Added spaces before else statement, Changed compare of strings to use.equals(). --- .../java/com/optimizely/ab/config/Experiment.java | 12 ++++++------ .../ab/optimizelyconfig/OptimizelyConfigService.java | 5 +++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/config/Experiment.java b/core-api/src/main/java/com/optimizely/ab/config/Experiment.java index ceb89e14c..28c33a4f0 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/Experiment.java +++ b/core-api/src/main/java/com/optimizely/ab/config/Experiment.java @@ -181,8 +181,8 @@ public String serializeConditions(Map audiencesMap) { } private String getNameFromAudienceId(String audienceId, Map audiencesMap) { - String audienceName = "\"" + audiencesMap.get(audienceId) + "\""; - return audienceName != null ? audienceName : "\"" + audienceId + "\""; + String audienceName = audiencesMap.get(audienceId); + return audienceName.isEmpty() ? "\"" + audienceName + "\"" : "\"" + audienceId + "\""; } private String getOperandOrAudienceId(Condition condition, Map audiencesMap){ @@ -223,7 +223,7 @@ public String serialize(Condition condition, Map audiencesMap) { Condition notCondition = ((NotCondition) condition).getCondition(); if(notCondition instanceof AudienceIdCondition) { stringBuilder.append(serialize(notCondition, audiencesMap)); - }else { + } else { stringBuilder.append("(" + serialize(notCondition, audiencesMap)+ ")"); } break; @@ -251,17 +251,17 @@ public String helper(String operand, List conditions, Map) con).getAudienceId(), audiencesMap); stringBuilder.append( audienceName + " "); - }else { + } else { stringBuilder.append("(" + serialize(con, audiencesMap) + ") "); } stringBuilder.append(operand); stringBuilder.append(" "); - }else { + } else { if(con instanceof AudienceIdCondition) { String audienceName = this.getNameFromAudienceId(((AudienceIdCondition) con).getAudienceId(), audiencesMap); stringBuilder.append(audienceName); - }else { + } else { stringBuilder.append("(" + serialize(con, audiencesMap) + ")"); } } diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java index 29519668f..7436ede2d 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java @@ -244,7 +244,7 @@ List getDeliveryRules(List rollouts, String rollo Map audiencesMap = new HashMap<>(); - Rollout rollout = rollouts.stream().filter(r -> r.getId() == rolloutId).collect(Collectors.toList()).get(0); + Rollout rollout = rollouts.stream().filter(r -> r.getId().equals(rolloutId)).collect(Collectors.toList()).get(0); if(rollout != null) { for(OptimizelyAudience optimizelyAudience: this.audiences) { @@ -262,6 +262,7 @@ List getDeliveryRules(List rollouts, String rollo optimizelyExperiment.setAudiences(experiment.serializeConditions(audiencesMap)); deliveryRules.add(optimizelyExperiment); } + return deliveryRules; } return Collections.emptyList(); @@ -324,7 +325,7 @@ List getAudiencesList(List typedAudiences, List a.getId() == audience.getId()).collect(Collectors.toList()).size() == 0) { + if(typedAudiences.stream().filter(a -> a.getId().equals(audience.getId())).collect(Collectors.toList()).size() == 0) { if(audience.getId() != "$opt_dummy_audience") { OptimizelyAudience optimizelyAudience = new OptimizelyAudience( audience.getId(), From 0aa4ad422ad6dbc0f75cae32594c2cd8d641c813 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Mon, 19 Jul 2021 16:57:56 -0400 Subject: [PATCH 06/65] Correct logic in retrieving name from audienceId --- core-api/src/main/java/com/optimizely/ab/config/Experiment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-api/src/main/java/com/optimizely/ab/config/Experiment.java b/core-api/src/main/java/com/optimizely/ab/config/Experiment.java index 28c33a4f0..cab07c0ed 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/Experiment.java +++ b/core-api/src/main/java/com/optimizely/ab/config/Experiment.java @@ -182,7 +182,7 @@ public String serializeConditions(Map audiencesMap) { private String getNameFromAudienceId(String audienceId, Map audiencesMap) { String audienceName = audiencesMap.get(audienceId); - return audienceName.isEmpty() ? "\"" + audienceName + "\"" : "\"" + audienceId + "\""; + return !audienceName.isEmpty() ? "\"" + audienceName + "\"" : "\"" + audienceId + "\""; } private String getOperandOrAudienceId(Condition condition, Map audiencesMap){ From 4fa26ddf2d1e0ac6fa6d3743752b79c84a3d157c Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Mon, 19 Jul 2021 17:01:32 -0400 Subject: [PATCH 07/65] Remove uneeded import and exceptions from jsonConfigParser. --- .../optimizely/ab/config/parser/JsonConfigParser.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 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 58a0f0a83..98399c523 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 @@ -16,7 +16,6 @@ */ package com.optimizely.ab.config.parser; -import com.fasterxml.jackson.core.JsonProcessingException; import com.optimizely.ab.config.*; import com.optimizely.ab.config.Experiment.ExperimentStatus; import com.optimizely.ab.config.audience.Audience; @@ -118,11 +117,11 @@ public ProjectConfig parseProjectConfig(@Nonnull String json) throws ConfigParse //======== Helper methods ========// - private List parseExperiments(JSONArray experimentJson) throws JsonProcessingException { + private List parseExperiments(JSONArray experimentJson){ return parseExperiments(experimentJson, ""); } - private List parseExperiments(JSONArray experimentJson, String groupId) throws JsonProcessingException { + private List parseExperiments(JSONArray experimentJson, String groupId){ List experiments = new ArrayList(experimentJson.length()); for (int i = 0; i < experimentJson.length(); i++) { @@ -328,7 +327,7 @@ private List parseTypedAudiences(JSONArray audienceJson) { return audiences; } - private List parseGroups(JSONArray groupJson) throws JsonProcessingException { + private List parseGroups(JSONArray groupJson){ List groups = new ArrayList(groupJson.length()); for (int i = 0; i < groupJson.length(); i++) { @@ -386,7 +385,7 @@ private List parseFeatureVariableInstances(JSONArr return featureVariableUsageInstances; } - private List parseRollouts(JSONArray rolloutsJson) throws JsonProcessingException { + private List parseRollouts(JSONArray rolloutsJson){ List rollouts = new ArrayList(rolloutsJson.length()); for (int i = 0; i < rolloutsJson.length(); i++) { From d064f8db407a4ac1643e07554c7f48eb2f8e1728 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Mon, 19 Jul 2021 17:03:14 -0400 Subject: [PATCH 08/65] Fix Format fo brackets. --- .../com/optimizely/ab/config/parser/JsonConfigParser.java | 6 +++--- 1 file changed, 3 insertions(+), 3 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 98399c523..3f64ff29c 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 @@ -121,7 +121,7 @@ private List parseExperiments(JSONArray experimentJson){ return parseExperiments(experimentJson, ""); } - private List parseExperiments(JSONArray experimentJson, String groupId){ + private List parseExperiments(JSONArray experimentJson, String groupId) { List experiments = new ArrayList(experimentJson.length()); for (int i = 0; i < experimentJson.length(); i++) { @@ -327,7 +327,7 @@ private List parseTypedAudiences(JSONArray audienceJson) { return audiences; } - private List parseGroups(JSONArray groupJson){ + private List parseGroups(JSONArray groupJson) { List groups = new ArrayList(groupJson.length()); for (int i = 0; i < groupJson.length(); i++) { @@ -385,7 +385,7 @@ private List parseFeatureVariableInstances(JSONArr return featureVariableUsageInstances; } - private List parseRollouts(JSONArray rolloutsJson){ + private List parseRollouts(JSONArray rolloutsJson) { List rollouts = new ArrayList(rolloutsJson.length()); for (int i = 0; i < rolloutsJson.length(); i++) { From 346b458243c1e74ee54806c648200197346f7fa3 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Mon, 19 Jul 2021 17:06:28 -0400 Subject: [PATCH 09/65] Formatting --- .../java/com/optimizely/ab/config/parser/JsonConfigParser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3f64ff29c..f5a98dae2 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 @@ -117,7 +117,7 @@ public ProjectConfig parseProjectConfig(@Nonnull String json) throws ConfigParse //======== Helper methods ========// - private List parseExperiments(JSONArray experimentJson){ + private List parseExperiments(JSONArray experimentJson) { return parseExperiments(experimentJson, ""); } From 3c71919a30907e9b42b77ac4befe8c25109b3a3b Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Mon, 19 Jul 2021 17:10:13 -0400 Subject: [PATCH 10/65] Simplify method to create audiences list and reduce time complexity. --- .../OptimizelyConfigService.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java index 7436ede2d..ee457e528 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java @@ -315,6 +315,7 @@ List getAudiencesList(List typedAudiences, List idLookupMap = new HashMap<>(); for(Audience audience: typedAudiences) { OptimizelyAudience optimizelyAudience = new OptimizelyAudience( audience.getId(), @@ -322,21 +323,20 @@ List getAudiencesList(List typedAudiences, List a.getId().equals(audience.getId())).collect(Collectors.toList()).size() == 0) { - if(audience.getId() != "$opt_dummy_audience") { - OptimizelyAudience optimizelyAudience = new OptimizelyAudience( - audience.getId(), - audience.getName(), - audience.getConditions().toString() - ); - audiencesList.add(optimizelyAudience); - } + if(!idLookupMap.containsKey(audience.getId()) && audience.getId() != "$opt_dummy_audience") { + OptimizelyAudience optimizelyAudience = new OptimizelyAudience( + audience.getId(), + audience.getName(), + audience.getConditions().toString() + ); + audiencesList.add(optimizelyAudience); } } - + return audiencesList; } } From 54138455bf1588012353aad1d1c8d5fb8a573cfd Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Tue, 20 Jul 2021 09:28:25 -0400 Subject: [PATCH 11/65] Added null chaeck and spaces where required to follow formatting.: --- .../com/optimizely/ab/config/Experiment.java | 24 +++++------ .../OptimizelyConfigService.java | 40 ++++++++++--------- 2 files changed, 34 insertions(+), 30 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/config/Experiment.java b/core-api/src/main/java/com/optimizely/ab/config/Experiment.java index cab07c0ed..ea9dced10 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/Experiment.java +++ b/core-api/src/main/java/com/optimizely/ab/config/Experiment.java @@ -187,7 +187,7 @@ private String getNameFromAudienceId(String audienceId, Map audi private String getOperandOrAudienceId(Condition condition, Map audiencesMap){ String operand = ""; - if(condition != null) { + if (condition != null) { if (condition instanceof AndCondition) { operand = "AND"; } else if (condition instanceof NotCondition) { @@ -209,19 +209,19 @@ public String serialize(Condition condition, Map audiencesMap) { List conditions; String operand = this.getOperandOrAudienceId(condition, audiencesMap); - switch(operand){ - case("AND"): + switch (operand){ + case ("AND"): conditions = ((AndCondition) condition).getConditions(); stringBuilder.append(this.helper(operand, conditions, audiencesMap)); break; - case("OR"): + case ("OR"): conditions = ((OrCondition) condition).getConditions(); stringBuilder.append(this.helper(operand, conditions, audiencesMap)); break; - case("NOT"): + case ("NOT"): stringBuilder.append(operand + " "); Condition notCondition = ((NotCondition) condition).getCondition(); - if(notCondition instanceof AudienceIdCondition) { + if (notCondition instanceof AudienceIdCondition) { stringBuilder.append(serialize(notCondition, audiencesMap)); } else { stringBuilder.append("(" + serialize(notCondition, audiencesMap)+ ")"); @@ -238,16 +238,16 @@ public String serialize(Condition condition, Map audiencesMap) { public String helper(String operand, List conditions, Map audiencesMap) { StringBuilder stringBuilder = new StringBuilder(); int ctr = 0; - if(conditions.isEmpty()) { + if (conditions.isEmpty()) { return ""; - }else if(conditions.size() == 1) { + } else if(conditions.size() == 1) { return serialize(conditions.get(0), audiencesMap); } - if(conditions.size() > 1) { + if (conditions.size() > 1) { for (Condition con : conditions) { ctr++; - if(ctr + 1 <= conditions.size()) { - if(con instanceof AudienceIdCondition) { + if (ctr + 1 <= conditions.size()) { + if (con instanceof AudienceIdCondition) { String audienceName = this.getNameFromAudienceId(((AudienceIdCondition) con).getAudienceId(), audiencesMap); stringBuilder.append( audienceName + " "); @@ -257,7 +257,7 @@ public String helper(String operand, List conditions, Map) con).getAudienceId(), audiencesMap); stringBuilder.append(audienceName); diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java index ee457e528..ada969457 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java @@ -39,7 +39,7 @@ public OptimizelyConfigService(ProjectConfig projectConfig) { Map experimentsMap = getExperimentsMap(); - for(Attribute attribute : projectConfig.getAttributes()){ + for (Attribute attribute : projectConfig.getAttributes()) { OptimizelyAttribute copyAttribute = new OptimizelyAttribute( attribute.getId(), attribute.getKey() @@ -47,7 +47,7 @@ public OptimizelyConfigService(ProjectConfig projectConfig) { optimizelyAttributes.add(copyAttribute); } - for(EventType event : projectConfig.getEventTypes()) { + for (EventType event : projectConfig.getEventTypes()) { OptimizelyEvent copyEvent = new OptimizelyEvent( event.getId(), event.getKey(), @@ -111,7 +111,7 @@ Map getExperimentsMap() { Map audiencesMap = new HashMap<>(); // Build audienceMap as [id:name] - for(OptimizelyAudience audience: this.audiences) { + for (OptimizelyAudience audience: this.audiences) { audiencesMap.put( audience.getId(), audience.getName() @@ -246,13 +246,13 @@ List getDeliveryRules(List rollouts, String rollo Rollout rollout = rollouts.stream().filter(r -> r.getId().equals(rolloutId)).collect(Collectors.toList()).get(0); - if(rollout != null) { - for(OptimizelyAudience optimizelyAudience: this.audiences) { + if (rollout != null) { + for (OptimizelyAudience optimizelyAudience: this.audiences) { audiencesMap.put(optimizelyAudience.getId(), optimizelyAudience.getName()); } List rolloutExperiments = rollout.getExperiments(); - for(Experiment experiment: rolloutExperiments) { + for (Experiment experiment: rolloutExperiments) { OptimizelyExperiment optimizelyExperiment = new OptimizelyExperiment( experiment.getId(), experiment.getKey(), @@ -316,24 +316,28 @@ List getAudiencesList(List typedAudiences, List idLookupMap = new HashMap<>(); - for(Audience audience: typedAudiences) { - OptimizelyAudience optimizelyAudience = new OptimizelyAudience( - audience.getId(), - audience.getName(), - audience.getConditions().toString() - ); - audiencesList.add(optimizelyAudience); - idLookupMap.put(audience.getId(), audience.getId()); - } - - for(Audience audience: audiences) { - if(!idLookupMap.containsKey(audience.getId()) && audience.getId() != "$opt_dummy_audience") { + if (!typedAudiences.isEmpty() && typedAudiences != null) { + for (Audience audience : typedAudiences) { OptimizelyAudience optimizelyAudience = new OptimizelyAudience( audience.getId(), audience.getName(), audience.getConditions().toString() ); audiencesList.add(optimizelyAudience); + idLookupMap.put(audience.getId(), audience.getId()); + } + } + + if (!audiences.isEmpty() && audiences != null) { + for (Audience audience : audiences) { + if (!idLookupMap.containsKey(audience.getId()) && audience.getId() != "$opt_dummy_audience") { + OptimizelyAudience optimizelyAudience = new OptimizelyAudience( + audience.getId(), + audience.getName(), + audience.getConditions().toString() + ); + audiencesList.add(optimizelyAudience); + } } } From 0c84679600d1d3cb077ddd10b58f461af3d498d0 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Tue, 20 Jul 2021 09:53:24 -0400 Subject: [PATCH 12/65] Updated Null checks as per spotbugsMain --- .../OptimizelyConfigService.java | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java index ada969457..e1151e924 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java @@ -304,19 +304,16 @@ Map getFeatureVariablesMap(List fea @VisibleForTesting List getAudiencesList(List typedAudiences, List audiences) { - List audiencesList = new ArrayList<>(); - /* - * This method merges typedAudiences with audiences from the Project - * config. Precedence is given to typedAudiences over audiences. - * - * Returns: - * A new list with the merged audiences as OptimizelyAudience objects. - * */ - - // Convert existing Typed Audiences to OptimizelyAudience Objects + * This method merges typedAudiences with audiences from the Project + * config. Precedence is given to typedAudiences over audiences. + * + * Returns: + * A new list with the merged audiences as OptimizelyAudience objects. + * */ + List audiencesList = new ArrayList<>(); Map idLookupMap = new HashMap<>(); - if (!typedAudiences.isEmpty() && typedAudiences != null) { + if (typedAudiences != null) { for (Audience audience : typedAudiences) { OptimizelyAudience optimizelyAudience = new OptimizelyAudience( audience.getId(), @@ -328,7 +325,7 @@ List getAudiencesList(List typedAudiences, List Date: Tue, 20 Jul 2021 10:20:41 -0400 Subject: [PATCH 13/65] Added more null checks to optimizelyConfigService. --- .../OptimizelyConfigService.java | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java index e1151e924..ab599becb 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java @@ -39,21 +39,25 @@ public OptimizelyConfigService(ProjectConfig projectConfig) { Map experimentsMap = getExperimentsMap(); - for (Attribute attribute : projectConfig.getAttributes()) { - OptimizelyAttribute copyAttribute = new OptimizelyAttribute( - attribute.getId(), - attribute.getKey() - ); - optimizelyAttributes.add(copyAttribute); + if (projectConfig.getAttributes() != null) { + for (Attribute attribute : projectConfig.getAttributes()) { + OptimizelyAttribute copyAttribute = new OptimizelyAttribute( + attribute.getId(), + attribute.getKey() + ); + optimizelyAttributes.add(copyAttribute); + } } - for (EventType event : projectConfig.getEventTypes()) { - OptimizelyEvent copyEvent = new OptimizelyEvent( - event.getId(), - event.getKey(), - event.getExperimentIds() - ); - optimizelyEvents.add(copyEvent); + if (projectConfig.getEventTypes() != null) { + for (EventType event : projectConfig.getEventTypes()) { + OptimizelyEvent copyEvent = new OptimizelyEvent( + event.getId(), + event.getKey(), + event.getExperimentIds() + ); + optimizelyEvents.add(copyEvent); + } } optimizelyConfig = new OptimizelyConfig( From 83c32e1eb75415d237f6c70a4b91b6171d9f51f5 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Tue, 20 Jul 2021 10:57:13 -0400 Subject: [PATCH 14/65] Update experimentsMap with null check on audiences. --- .../ab/optimizelyconfig/OptimizelyConfigService.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java index ab599becb..1156af644 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java @@ -115,11 +115,13 @@ Map getExperimentsMap() { Map audiencesMap = new HashMap<>(); // Build audienceMap as [id:name] - for (OptimizelyAudience audience: this.audiences) { - audiencesMap.put( - audience.getId(), - audience.getName() - ); + if (this.audiences != null) { + for (OptimizelyAudience audience : this.audiences) { + audiencesMap.put( + audience.getId(), + audience.getName() + ); + } } for (Experiment experiment : experiments) { From 725e8a76da026f5c3b6a52848b93b8461f49f4db Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Tue, 20 Jul 2021 13:13:42 -0400 Subject: [PATCH 15/65] Change Logic for empty list in DeliverRules. --- .../ab/optimizelyconfig/OptimizelyConfigService.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java index 1156af644..ba43d8029 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java @@ -247,11 +247,13 @@ Map getFeaturesMap(Map List getDeliveryRules(List rollouts, String rolloutId) { List deliveryRules = new ArrayList(); + Rollout rollout = null; Map audiencesMap = new HashMap<>(); - - Rollout rollout = rollouts.stream().filter(r -> r.getId().equals(rolloutId)).collect(Collectors.toList()).get(0); - + if (rollouts != null) { + List retrieved = rollouts.stream().filter(r -> r.getId().equals(rolloutId)).collect(Collectors.toList()); + rollout = retrieved.isEmpty() ? null : retrieved.get(0); + } if (rollout != null) { for (OptimizelyAudience optimizelyAudience: this.audiences) { audiencesMap.put(optimizelyAudience.getId(), optimizelyAudience.getName()); From 5f0de11a77dd45cc3be4fcd44a33ebeb3c8b8426 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Tue, 20 Jul 2021 15:45:33 -0400 Subject: [PATCH 16/65] Added testcases to check all scenarios of audience conditions. --- .../com/optimizely/ab/config/Experiment.java | 2 +- .../optimizely/ab/config/ExperimentTest.java | 179 ++++++++++++++++++ 2 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 core-api/src/test/java/com/optimizely/ab/config/ExperimentTest.java diff --git a/core-api/src/main/java/com/optimizely/ab/config/Experiment.java b/core-api/src/main/java/com/optimizely/ab/config/Experiment.java index ea9dced10..24592a7b7 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/Experiment.java +++ b/core-api/src/main/java/com/optimizely/ab/config/Experiment.java @@ -177,7 +177,7 @@ public String serializeConditions(Map audiencesMap) { Condition condition = this.audienceConditions; - return this.serialize(condition, audiencesMap); + return condition instanceof EmptyCondition ? "" : this.serialize(condition, audiencesMap); } private String getNameFromAudienceId(String audienceId, Map audiencesMap) { diff --git a/core-api/src/test/java/com/optimizely/ab/config/ExperimentTest.java b/core-api/src/test/java/com/optimizely/ab/config/ExperimentTest.java new file mode 100644 index 000000000..c24b226e0 --- /dev/null +++ b/core-api/src/test/java/com/optimizely/ab/config/ExperimentTest.java @@ -0,0 +1,179 @@ +package com.optimizely.ab.config; + +import com.optimizely.ab.config.audience.*; +import org.junit.Test; +import static org.junit.Assert.*; + +import java.util.*; + +public class ExperimentTest { + + @Test + public void testStringifyConditionScenarios() { + List audienceConditionsScenarios = getAudienceConditionsList(); + Map expectedScenarioStringsMap = getExpectedScenariosMap(); + Map audiencesMap = new HashMap<>(); + audiencesMap.put("1", "us"); + audiencesMap.put("2", "female"); + audiencesMap.put("3", "adult"); + audiencesMap.put("11", "fr"); + audiencesMap.put("12", "male"); + audiencesMap.put("13", "kid"); + + if (expectedScenarioStringsMap.size() == audienceConditionsScenarios.size()) { + for (int i = 0; i < audienceConditionsScenarios.size() - 1; i++) { + Experiment experiment = makeMockExperimentWithStatus(Experiment.ExperimentStatus.RUNNING, + audienceConditionsScenarios.get(i)); + String audiences = experiment.serializeConditions(audiencesMap); + assertEquals(audiences, expectedScenarioStringsMap.get(i+1)); + } + } + + } + + public Map getExpectedScenariosMap() { + Map expectedScenarioStringsMap = new HashMap<>(); + expectedScenarioStringsMap.put(1, ""); + expectedScenarioStringsMap.put(2, "\"us\" OR \"female\""); + expectedScenarioStringsMap.put(3, "\"us\" AND \"female\" AND \"adult\""); + expectedScenarioStringsMap.put(4, "NOT \"us\""); + expectedScenarioStringsMap.put(5, "\"us\""); + expectedScenarioStringsMap.put(6, "\"us\""); + expectedScenarioStringsMap.put(7, "\"us\""); + expectedScenarioStringsMap.put(8, "\"us\" OR \"female\""); + expectedScenarioStringsMap.put(9, "(\"us\" OR \"female\") AND \"adult\""); + expectedScenarioStringsMap.put(10, "(\"us\" OR (\"female\" AND \"adult\")) AND (\"fr\" AND (\"male\" OR \"kid\"))"); + expectedScenarioStringsMap.put(11, "NOT (\"us\" AND \"female\")"); + expectedScenarioStringsMap.put(12, "\"us\" OR \"100000\""); + + return expectedScenarioStringsMap; + } + + public List getAudienceConditionsList() { + AudienceIdCondition one = new AudienceIdCondition("1"); + AudienceIdCondition two = new AudienceIdCondition("2"); + AudienceIdCondition three = new AudienceIdCondition("3"); + AudienceIdCondition eleven = new AudienceIdCondition("11"); + AudienceIdCondition twelve = new AudienceIdCondition("12"); + AudienceIdCondition thirteen = new AudienceIdCondition("13"); + + // Scenario 1 - [] + EmptyCondition scenario1 = new EmptyCondition(); + + // Scenario 2 - ["or", "1", "2"] + List scenario2List = new ArrayList<>(); + scenario2List.add(one); + scenario2List.add(two); + OrCondition scenario2 = new OrCondition(scenario2List); + + // Scenario 3 - ["and", "1", "2", "3"] + List scenario3List = new ArrayList<>(); + scenario3List.add(one); + scenario3List.add(two); + scenario3List.add(three); + AndCondition scenario3 = new AndCondition(scenario3List); + + // Scenario 4 - ["not", "1"] + NotCondition scenario4 = new NotCondition(one); + + // Scenario 5 - ["or", "1"] + List scenario5List = new ArrayList<>(); + scenario5List.add(one); + OrCondition scenario5 = new OrCondition(scenario5List); + + // Scenario 6 - ["and", "1"] + List scenario6List = new ArrayList<>(); + scenario6List.add(one); + AndCondition scenario6 = new AndCondition(scenario6List); + + // Scenario 7 - ["1"] + AudienceIdCondition scenario7 = one; + + // Scenario 8 - ["1", "2"] + // Defaults to Or in Datafile Parsing resulting in an OrCondition + // Same as Scenario 2 + + OrCondition scenario8 = scenario2; + + // Scenario 9 - ["and", ["or", "1", "2"], "3"] + List Scenario9List = new ArrayList<>(); + Scenario9List.add(scenario2); + Scenario9List.add(three); + AndCondition scenario9 = new AndCondition(Scenario9List); + + // Scenario 10 - ["and", ["or", "1", ["and", "2", "3"]], ["and", "11, ["or", "12", "13"]]] + List scenario10List = new ArrayList<>(); + + List or1213List = new ArrayList<>(); + or1213List.add(twelve); + or1213List.add(thirteen); + OrCondition or1213 = new OrCondition(or1213List); + + List and11Or1213List = new ArrayList<>(); + and11Or1213List.add(eleven); + and11Or1213List.add(or1213); + AndCondition and11Or1213 = new AndCondition(and11Or1213List); + + List and23List = new ArrayList<>(); + and23List.add(two); + and23List.add(three); + AndCondition and23 = new AndCondition(and23List); + + List or1And23List = new ArrayList<>(); + or1And23List.add(one); + or1And23List.add(and23); + OrCondition or1And23 = new OrCondition(or1And23List); + + scenario10List.add(or1And23); + scenario10List.add(and11Or1213); + AndCondition scenario10 = new AndCondition(scenario10List); + + // Scenario 11 - ["not", ["and", "1", "2"]] + List and12List = new ArrayList<>(); + and12List.add(one); + and12List.add(two); + AndCondition and12 = new AndCondition(and12List); + + NotCondition scenario11 = new NotCondition(and12); + + // Scenario 12 - ["or", "1", "100000"] + List scenario12List = new ArrayList<>(); + scenario12List.add(one); + AudienceIdCondition unknownAudience = new AudienceIdCondition("100000"); + scenario12List.add(unknownAudience); + + OrCondition scenario12 = new OrCondition(scenario12List); + + // Scenario 13 - Empty String "" already accounted for in Datafile parsing + + + List conditionTestScenarios = new ArrayList<>(); + conditionTestScenarios.add(scenario1); + conditionTestScenarios.add(scenario2); + conditionTestScenarios.add(scenario3); + conditionTestScenarios.add(scenario4); + conditionTestScenarios.add(scenario5); + conditionTestScenarios.add(scenario6); + conditionTestScenarios.add(scenario7); + conditionTestScenarios.add(scenario8); + conditionTestScenarios.add(scenario9); + conditionTestScenarios.add(scenario10); + conditionTestScenarios.add(scenario11); + conditionTestScenarios.add(scenario12); + + return conditionTestScenarios; + } + + private Experiment makeMockExperimentWithStatus(Experiment.ExperimentStatus status, Condition audienceConditions) { + return new Experiment("12345", + "mockExperimentKey", + status.toString(), + "layerId", + Collections.emptyList(), + audienceConditions, + Collections.emptyList(), + Collections.emptyMap(), + Collections.emptyList() + ); + } +} From 48a1b42784c745bd9eec0ff0169061cd19410146 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Tue, 20 Jul 2021 16:01:42 -0400 Subject: [PATCH 17/65] Update license information. --- .../com/optimizely/ab/config/Experiment.java | 2 +- .../parser/ConditionJacksonDeserializer.java | 2 +- .../optimizely/ab/internal/ConditionUtils.java | 2 +- .../ab/optimizelyconfig/OptimizelyFeature.java | 2 +- .../com/optimizely/ab/config/ExperimentTest.java | 16 ++++++++++++++++ 5 files changed, 20 insertions(+), 4 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/config/Experiment.java b/core-api/src/main/java/com/optimizely/ab/config/Experiment.java index 24592a7b7..d246a6307 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/Experiment.java +++ b/core-api/src/main/java/com/optimizely/ab/config/Experiment.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016-2019, Optimizely and contributors + * Copyright 2016-2019, 2021, 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. diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/ConditionJacksonDeserializer.java b/core-api/src/main/java/com/optimizely/ab/config/parser/ConditionJacksonDeserializer.java index 26dd59160..f443d9d07 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/ConditionJacksonDeserializer.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/ConditionJacksonDeserializer.java @@ -1,6 +1,6 @@ /** * - * Copyright 2018-2019, Optimizely and contributors + * Copyright 2018-2019, 2021, 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. diff --git a/core-api/src/main/java/com/optimizely/ab/internal/ConditionUtils.java b/core-api/src/main/java/com/optimizely/ab/internal/ConditionUtils.java index d9d7e11f4..32ab45cc4 100644 --- a/core-api/src/main/java/com/optimizely/ab/internal/ConditionUtils.java +++ b/core-api/src/main/java/com/optimizely/ab/internal/ConditionUtils.java @@ -1,6 +1,6 @@ /** * - * Copyright 2018-2019,2021, Optimizely and contributors + * Copyright 2018-2019, 2021, 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. diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java index 3fb525266..c54d8a16e 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java @@ -1,5 +1,5 @@ /**************************************************************************** - * Copyright 2020, Optimizely, Inc. and contributors * + * Copyright 2020-2021, Optimizely, Inc. and contributors * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * diff --git a/core-api/src/test/java/com/optimizely/ab/config/ExperimentTest.java b/core-api/src/test/java/com/optimizely/ab/config/ExperimentTest.java index c24b226e0..c6eecfd37 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/ExperimentTest.java +++ b/core-api/src/test/java/com/optimizely/ab/config/ExperimentTest.java @@ -1,3 +1,19 @@ +/** + * + * Copyright 2021, 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; import com.optimizely.ab.config.audience.*; From b1fd2b58e9a6c4f36b526e62685a487384812de2 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Wed, 21 Jul 2021 13:23:27 -0400 Subject: [PATCH 18/65] Remove key from OptimizelyAudience --- .../optimizely/ab/optimizelyconfig/OptimizelyAudience.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyAudience.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyAudience.java index da9db0a1b..d874b900e 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyAudience.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyAudience.java @@ -23,7 +23,7 @@ /** * Represents the Audiences list {@link OptimizelyConfig} */ -public class OptimizelyAudience implements IdKeyMapped{ +public class OptimizelyAudience{ private String id; private String name; @@ -41,8 +41,6 @@ public OptimizelyAudience(String id, public String getName() { return name; } - public String getKey() { return name; } - public String getConditions() { return conditions; } @Override @@ -51,7 +49,7 @@ public boolean equals(Object obj) { if (obj == this) return true; OptimizelyAudience optimizelyAudience = (OptimizelyAudience) obj; return id.equals(optimizelyAudience.getId()) && - name.equals(optimizelyAudience.getKey()) && + name.equals(optimizelyAudience.getName()) && conditions.equals(optimizelyAudience.getConditions()); } From f3b3d257031e2da6094208b238f2c627fc8352e6 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Thu, 22 Jul 2021 08:43:32 -0400 Subject: [PATCH 19/65] Some cleanup to suggested constructors and multiple methods. --- .../com/optimizely/ab/config/Experiment.java | 11 ++-- .../ab/optimizelyconfig/OptimizelyConfig.java | 34 ++-------- .../OptimizelyConfigService.java | 64 +++++++++---------- .../OptimizelyExperiment.java | 8 +-- .../optimizelyconfig/OptimizelyFeature.java | 18 +++--- .../OptimizelyConfigServiceTest.java | 20 ++++-- .../OptimizelyConfigTest.java | 12 +++- .../OptimizelyExperimentTest.java | 3 +- .../OptimizelyFeatureTest.java | 11 +++- 9 files changed, 83 insertions(+), 98 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/config/Experiment.java b/core-api/src/main/java/com/optimizely/ab/config/Experiment.java index d246a6307..dd0beda5f 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/Experiment.java +++ b/core-api/src/main/java/com/optimizely/ab/config/Experiment.java @@ -44,7 +44,7 @@ public class Experiment implements IdKeyMapped { private final String groupId; private final List audienceIds; - private final Condition audienceConditions; + private final Condition audienceConditions; private final List variations; private final List trafficAllocation; @@ -212,11 +212,11 @@ public String serialize(Condition condition, Map audiencesMap) { switch (operand){ case ("AND"): conditions = ((AndCondition) condition).getConditions(); - stringBuilder.append(this.helper(operand, conditions, audiencesMap)); + stringBuilder.append(this.getNameOrNextCondition(operand, conditions, audiencesMap)); break; case ("OR"): conditions = ((OrCondition) condition).getConditions(); - stringBuilder.append(this.helper(operand, conditions, audiencesMap)); + stringBuilder.append(this.getNameOrNextCondition(operand, conditions, audiencesMap)); break; case ("NOT"): stringBuilder.append(operand + " "); @@ -235,15 +235,14 @@ public String serialize(Condition condition, Map audiencesMap) { return stringBuilder.toString(); } - public String helper(String operand, List conditions, Map audiencesMap) { + public String getNameOrNextCondition(String operand, List conditions, Map audiencesMap) { StringBuilder stringBuilder = new StringBuilder(); int ctr = 0; if (conditions.isEmpty()) { return ""; } else if(conditions.size() == 1) { return serialize(conditions.get(0), audiencesMap); - } - if (conditions.size() > 1) { + } else { for (Condition con : conditions) { ctr++; if (ctr + 1 <= conditions.size()) { diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java index f2498cd98..5e8eb82dc 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java @@ -38,35 +38,6 @@ public class OptimizelyConfig { private String environmentKey; private String datafile; - public OptimizelyConfig(Map experimentsMap, - Map featuresMap, - String revision, String sdkKey, String environmentKey) { - this(experimentsMap, featuresMap, revision, sdkKey, environmentKey, null); - } - - public OptimizelyConfig(Map experimentsMap, - Map featuresMap, - String revision, - String sdkKey, - String environmentKey, - String datafile) { - this.experimentsMap = experimentsMap; - this.featuresMap = featuresMap; - this.revision = revision; - this.sdkKey = sdkKey; - this.environmentKey = environmentKey; - this.datafile = datafile; - } - - public OptimizelyConfig(Map experimentsMap, - Map featuresMap, - String revision, String sdkKey, String environmentKey, - List attributes, - List events, - List audiences) { - this(experimentsMap, featuresMap, revision, sdkKey, environmentKey, attributes, events, audiences, null); - } - public OptimizelyConfig(Map experimentsMap, Map featuresMap, String revision, @@ -122,7 +93,10 @@ public boolean equals(Object obj) { OptimizelyConfig optimizelyConfig = (OptimizelyConfig) obj; return revision.equals(optimizelyConfig.getRevision()) && experimentsMap.equals(optimizelyConfig.getExperimentsMap()) && - featuresMap.equals(optimizelyConfig.getFeaturesMap()); + featuresMap.equals(optimizelyConfig.getFeaturesMap()) && + attributes.equals(optimizelyConfig.getAttributes()) && + events.equals(optimizelyConfig.getEvents()) && + audiences.equals(optimizelyConfig.getAudiences()); } @Override diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java index ba43d8029..63f0c42fa 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java @@ -29,6 +29,7 @@ public class OptimizelyConfigService { private ProjectConfig projectConfig; private OptimizelyConfig optimizelyConfig; private List audiences; + private Map audiencesMap; public OptimizelyConfigService(ProjectConfig projectConfig) { this.projectConfig = projectConfig; @@ -60,6 +61,18 @@ public OptimizelyConfigService(ProjectConfig projectConfig) { } } + audiencesMap = new HashMap<>(); + + // Build audienceMap as [id:name] + if (this.audiences != null) { + for (OptimizelyAudience audience : this.audiences) { + audiencesMap.put( + audience.getId(), + audience.getName() + ); + } + } + optimizelyConfig = new OptimizelyConfig( experimentsMap, getFeaturesMap(experimentsMap), @@ -112,26 +125,15 @@ Map getExperimentsMap() { return Collections.emptyMap(); } Map featureExperimentMap = new HashMap<>(); - Map audiencesMap = new HashMap<>(); - - // Build audienceMap as [id:name] - if (this.audiences != null) { - for (OptimizelyAudience audience : this.audiences) { - audiencesMap.put( - audience.getId(), - audience.getName() - ); - } - } for (Experiment experiment : experiments) { OptimizelyExperiment optimizelyExperiment = new OptimizelyExperiment( experiment.getId(), experiment.getKey(), - getVariationsMap(experiment.getVariations(), experiment.getId()) + getVariationsMap(experiment.getVariations(), experiment.getId()), + experiment.serializeConditions(this.audiencesMap) ); - optimizelyExperiment.setAudiences(experiment.serializeConditions(audiencesMap)); featureExperimentMap.put(experiment.getKey(), optimizelyExperiment); } return featureExperimentMap; @@ -224,50 +226,42 @@ Map getFeaturesMap(Map for (FeatureFlag featureFlag : featureFlags) { Map experimentsMapForFeature = getExperimentsMapForFeature(featureFlag.getExperimentIds(), allExperimentsMap); - OptimizelyFeature optimizelyFeature = new OptimizelyFeature( - featureFlag.getId(), - featureFlag.getKey(), - getExperimentsMapForFeature(featureFlag.getExperimentIds(), allExperimentsMap), - getFeatureVariablesMap(featureFlag.getVariables()) - ); List experimentRules = new ArrayList(experimentsMapForFeature.values()); List deliveryRules = - this.getDeliveryRules(this.projectConfig.getRollouts(), featureFlag.getRolloutId()); + this.getDeliveryRules(featureFlag.getRolloutId()); - optimizelyFeature.setDeliveryRules(deliveryRules); - optimizelyFeature.setExperimentRules(experimentRules); + OptimizelyFeature optimizelyFeature = new OptimizelyFeature( + featureFlag.getId(), + featureFlag.getKey(), + experimentsMapForFeature, + getFeatureVariablesMap(featureFlag.getVariables()), + experimentRules, + deliveryRules + ); optimizelyFeatureKeyMap.put(featureFlag.getKey(), optimizelyFeature); } return optimizelyFeatureKeyMap; } - List getDeliveryRules(List rollouts, String rolloutId) { + List getDeliveryRules(String rolloutId) { List deliveryRules = new ArrayList(); - Rollout rollout = null; - Map audiencesMap = new HashMap<>(); - if (rollouts != null) { - List retrieved = rollouts.stream().filter(r -> r.getId().equals(rolloutId)).collect(Collectors.toList()); - rollout = retrieved.isEmpty() ? null : retrieved.get(0); - } - if (rollout != null) { - for (OptimizelyAudience optimizelyAudience: this.audiences) { - audiencesMap.put(optimizelyAudience.getId(), optimizelyAudience.getName()); - } + Rollout rollout = projectConfig.getRolloutIdMapping().get(rolloutId); + if (rollout != null) { List rolloutExperiments = rollout.getExperiments(); for (Experiment experiment: rolloutExperiments) { OptimizelyExperiment optimizelyExperiment = new OptimizelyExperiment( experiment.getId(), experiment.getKey(), - this.getVariationsMap(experiment.getVariations(), experiment.getId()) + this.getVariationsMap(experiment.getVariations(), experiment.getId()), + experiment.serializeConditions(this.audiencesMap) ); - optimizelyExperiment.setAudiences(experiment.serializeConditions(audiencesMap)); deliveryRules.add(optimizelyExperiment); } return deliveryRules; diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyExperiment.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyExperiment.java index b511dde8b..0f5b9e193 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyExperiment.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyExperiment.java @@ -29,10 +29,11 @@ public class OptimizelyExperiment implements IdKeyMapped { private String audiences = ""; private Map variationsMap; - public OptimizelyExperiment(String id, String key, Map variationsMap) { + public OptimizelyExperiment(String id, String key, Map variationsMap, String audiences) { this.id = id; this.key = key; this.variationsMap = variationsMap; + this.audiences = audiences; } public String getId() { @@ -49,8 +50,6 @@ public Map getVariationsMap() { return variationsMap; } - public void setAudiences(String audiences) { this.audiences = audiences; } - @Override public boolean equals(Object obj) { if (obj == null || getClass() != obj.getClass()) return false; @@ -58,7 +57,8 @@ public boolean equals(Object obj) { OptimizelyExperiment optimizelyExperiment = (OptimizelyExperiment) obj; return id.equals(optimizelyExperiment.getId()) && key.equals(optimizelyExperiment.getKey()) && - variationsMap.equals(optimizelyExperiment.getVariationsMap()); + variationsMap.equals(optimizelyExperiment.getVariationsMap()) && + audiences.equals(optimizelyExperiment.getAudiences()); } @Override diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java index c54d8a16e..1328af22c 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java @@ -37,11 +37,15 @@ public class OptimizelyFeature implements IdKeyMapped { public OptimizelyFeature(String id, String key, Map experimentsMap, - Map variablesMap) { + Map variablesMap, + List experimentRules, + List deliveryRules) { this.id = id; this.key = key; this.experimentsMap = experimentsMap; this.variablesMap = variablesMap; + this.experimentRules = experimentRules; + this.deliveryRules = deliveryRules; } public String getId() { @@ -64,14 +68,6 @@ public Map getVariablesMap() { public List getDeliveryRules() { return deliveryRules; } - public void setExperimentRules(List experimentRules) { - this.experimentRules = experimentRules; - } - - public void setDeliveryRules(List deliveryRules) { - this.deliveryRules = deliveryRules; - } - @Override public boolean equals(Object obj) { if (obj == null || getClass() != obj.getClass()) return false; @@ -80,7 +76,9 @@ public boolean equals(Object obj) { return id.equals(optimizelyFeature.getId()) && key.equals(optimizelyFeature.getKey()) && experimentsMap.equals(optimizelyFeature.getExperimentsMap()) && - variablesMap.equals(optimizelyFeature.getVariablesMap()); + variablesMap.equals(optimizelyFeature.getVariablesMap()) && + deliveryRules.equals(optimizelyFeature.getDeliveryRules()) && + experimentRules.equals(optimizelyFeature.getExperimentRules()); } @Override diff --git a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java index d44ab9b57..07aab156f 100644 --- a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java +++ b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java @@ -385,7 +385,8 @@ OptimizelyConfig getExpectedConfig() { }} ) ); - }} + }}, + "" ) ); optimizelyExperimentMap.put( @@ -412,7 +413,8 @@ OptimizelyConfig getExpectedConfig() { Collections.emptyMap() ) ); - }} + }}, + "" ) ); @@ -485,7 +487,8 @@ OptimizelyConfig getExpectedConfig() { }} ) ); - }} + }}, + "" ) ); }}, @@ -508,7 +511,9 @@ OptimizelyConfig getExpectedConfig() { "arry" ) ); - }} + }}, + Collections.emptyList(), + Collections.emptyList() ) ); optimizelyFeatureMap.put( @@ -517,7 +522,9 @@ OptimizelyConfig getExpectedConfig() { "4195505407", "boolean_feature", Collections.emptyMap(), - Collections.emptyMap() + Collections.emptyMap(), + Collections.emptyList(), + Collections.emptyList() ) ); @@ -555,7 +562,8 @@ OptimizelyConfig getExpectedConfig() { "test_audience_1", "['and', ['or', '1', '2'], '3']" ) - ) + ), + null ); } } diff --git a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigTest.java b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigTest.java index 13b703799..9ded7f22d 100644 --- a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigTest.java +++ b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigTest.java @@ -32,7 +32,11 @@ public void testOptimizelyConfig() { generateFeatureMap(), "101", "testingSdkKey", - "development" + "development", + null, + null, + null, + null ); assertEquals("101", optimizelyConfig.getRevision()); assertEquals("testingSdkKey", optimizelyConfig.getSdkKey()); @@ -53,12 +57,14 @@ private Map generateExperimentMap() { optimizelyExperimentMap.put("test_exp_1", new OptimizelyExperiment( "33", "test_exp_1", - generateVariationMap() + generateVariationMap(), + "" )); optimizelyExperimentMap.put("test_exp_2", new OptimizelyExperiment( "34", "test_exp_2", - generateVariationMap() + generateVariationMap(), + "" )); return optimizelyExperimentMap; } diff --git a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyExperimentTest.java b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyExperimentTest.java index ec7cebb79..954a90f29 100644 --- a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyExperimentTest.java +++ b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyExperimentTest.java @@ -29,7 +29,8 @@ public void testOptimizelyExperiment() { OptimizelyExperiment optimizelyExperiment = new OptimizelyExperiment( "31", "test_exp", - generateVariationMap() + generateVariationMap(), + "" ); assertEquals("31", optimizelyExperiment.getId()); assertEquals("test_exp", optimizelyExperiment.getKey()); diff --git a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeatureTest.java b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeatureTest.java index 732266a98..a6789311b 100644 --- a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeatureTest.java +++ b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeatureTest.java @@ -17,6 +17,7 @@ import org.junit.Test; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import static com.optimizely.ab.optimizelyconfig.OptimizelyVariationTest.generateVariablesMap; @@ -31,7 +32,9 @@ public void testOptimizelyFeature() { "41", "test_feature", generateExperimentMap(), - generateVariablesMap() + generateVariablesMap(), + Collections.emptyList(), + Collections.emptyList() ); assertEquals("41", optimizelyFeature.getId()); assertEquals("test_feature", optimizelyFeature.getKey()); @@ -50,12 +53,14 @@ static Map generateExperimentMap() { optimizelyExperimentMap.put("test_exp_1", new OptimizelyExperiment ( "32", "test_exp_1", - generateVariationMap() + generateVariationMap(), + "" )); optimizelyExperimentMap.put("test_exp_2", new OptimizelyExperiment ( "33", "test_exp_2", - generateVariationMap() + generateVariationMap(), + "" )); return optimizelyExperimentMap; } From 707a34805d8028d7eddb637bb9b103d6ee05d7b6 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Thu, 22 Jul 2021 08:46:59 -0400 Subject: [PATCH 20/65] Fixed feature constructor in config test. --- .../optimizely/ab/optimizelyconfig/OptimizelyConfigTest.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigTest.java b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigTest.java index 9ded7f22d..58acadd3f 100644 --- a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigTest.java +++ b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigTest.java @@ -17,6 +17,7 @@ import org.junit.Test; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import static com.optimizely.ab.optimizelyconfig.OptimizelyExperimentTest.generateVariationMap; @@ -75,7 +76,9 @@ private Map generateFeatureMap() { "42", "test_feature_1", generateExperimentMap(), - generateVariablesMap() + generateVariablesMap(), + Collections.emptyList(), + Collections.emptyList() )); return optimizelyFeatureMap; } From 1a48ad75bfb32ca208c364a20a6aea921e0fa751 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Thu, 22 Jul 2021 09:07:44 -0400 Subject: [PATCH 21/65] Add scenario 13 to serialize test cases for [and, and] by using an invalid audienceIdCondition --- .../main/java/com/optimizely/ab/config/Experiment.java | 2 +- .../java/com/optimizely/ab/config/ExperimentTest.java | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/core-api/src/main/java/com/optimizely/ab/config/Experiment.java b/core-api/src/main/java/com/optimizely/ab/config/Experiment.java index dd0beda5f..b219ca9c5 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/Experiment.java +++ b/core-api/src/main/java/com/optimizely/ab/config/Experiment.java @@ -182,7 +182,7 @@ public String serializeConditions(Map audiencesMap) { private String getNameFromAudienceId(String audienceId, Map audiencesMap) { String audienceName = audiencesMap.get(audienceId); - return !audienceName.isEmpty() ? "\"" + audienceName + "\"" : "\"" + audienceId + "\""; + return audienceName != null ? "\"" + audienceName + "\"" : "\"" + audienceId + "\""; } private String getOperandOrAudienceId(Condition condition, Map audiencesMap){ diff --git a/core-api/src/test/java/com/optimizely/ab/config/ExperimentTest.java b/core-api/src/test/java/com/optimizely/ab/config/ExperimentTest.java index c6eecfd37..741e0a774 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/ExperimentTest.java +++ b/core-api/src/test/java/com/optimizely/ab/config/ExperimentTest.java @@ -61,6 +61,7 @@ public Map getExpectedScenariosMap() { expectedScenarioStringsMap.put(10, "(\"us\" OR (\"female\" AND \"adult\")) AND (\"fr\" AND (\"male\" OR \"kid\"))"); expectedScenarioStringsMap.put(11, "NOT (\"us\" AND \"female\")"); expectedScenarioStringsMap.put(12, "\"us\" OR \"100000\""); + expectedScenarioStringsMap.put(13, ""); return expectedScenarioStringsMap; } @@ -161,6 +162,13 @@ public List getAudienceConditionsList() { OrCondition scenario12 = new OrCondition(scenario12List); // Scenario 13 - Empty String "" already accounted for in Datafile parsing + AudienceIdCondition invalidAudience = new AudienceIdCondition("5"); + List invalidIdList = new ArrayList<>(); + invalidIdList.add(invalidAudience); + AndCondition andCondition = new AndCondition(invalidIdList); + List andInvalidAudienceId = new ArrayList<>(); + andInvalidAudienceId.add(andCondition); + AndCondition scenario13 = new AndCondition(andInvalidAudienceId); List conditionTestScenarios = new ArrayList<>(); @@ -176,6 +184,7 @@ public List getAudienceConditionsList() { conditionTestScenarios.add(scenario10); conditionTestScenarios.add(scenario11); conditionTestScenarios.add(scenario12); + conditionTestScenarios.add(scenario13); return conditionTestScenarios; } From d0bf1c62d122e8d81babd6353abb4b9cd042c143 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Thu, 22 Jul 2021 09:09:30 -0400 Subject: [PATCH 22/65] Update Scenario13 comment. --- .../src/test/java/com/optimizely/ab/config/ExperimentTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core-api/src/test/java/com/optimizely/ab/config/ExperimentTest.java b/core-api/src/test/java/com/optimizely/ab/config/ExperimentTest.java index 741e0a774..159944dbd 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/ExperimentTest.java +++ b/core-api/src/test/java/com/optimizely/ab/config/ExperimentTest.java @@ -161,7 +161,8 @@ public List getAudienceConditionsList() { OrCondition scenario12 = new OrCondition(scenario12List); - // Scenario 13 - Empty String "" already accounted for in Datafile parsing + // Scenario 13 - ["and", ["and", invalidAudienceIdCondition]] which becomes + // the scenario of ["and", "and"] and results in empty string. AudienceIdCondition invalidAudience = new AudienceIdCondition("5"); List invalidIdList = new ArrayList<>(); invalidIdList.add(invalidAudience); From dddcedf8a0a2ad5ef2796af3738aa3d3227bccb8 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Thu, 22 Jul 2021 09:40:55 -0400 Subject: [PATCH 23/65] Added Null check to account for constructor change and no audiences set. --- .../src/main/java/com/optimizely/ab/config/Experiment.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/config/Experiment.java b/core-api/src/main/java/com/optimizely/ab/config/Experiment.java index b219ca9c5..5288c92fa 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/Experiment.java +++ b/core-api/src/main/java/com/optimizely/ab/config/Experiment.java @@ -181,8 +181,9 @@ public String serializeConditions(Map audiencesMap) { } private String getNameFromAudienceId(String audienceId, Map audiencesMap) { - String audienceName = audiencesMap.get(audienceId); - return audienceName != null ? "\"" + audienceName + "\"" : "\"" + audienceId + "\""; + if (audiencesMap == null ) return "\"" + audienceId + "\""; + String audienceName = "\"" + audiencesMap.get(audienceId) + "\""; + return audiencesMap.get(audienceId) != null ? audienceName : "\"" + audienceId + "\""; } private String getOperandOrAudienceId(Condition condition, Map audiencesMap){ From 4b5077478bfd8b982c7f8ea6a02bc139568f81ee Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Thu, 22 Jul 2021 11:13:18 -0400 Subject: [PATCH 24/65] Fix test for featuresMap. --- .../ab/optimizelyconfig/OptimizelyFeature.java | 13 +++++++------ .../OptimizelyConfigServiceTest.java | 8 ++++---- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java index 1328af22c..23fd4d9ec 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java @@ -17,6 +17,7 @@ import com.optimizely.ab.config.IdKeyMapped; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -35,9 +36,9 @@ public class OptimizelyFeature implements IdKeyMapped { private Map variablesMap; public OptimizelyFeature(String id, - String key, - Map experimentsMap, - Map variablesMap, + String key, + Map experimentsMap, + Map variablesMap, List experimentRules, List deliveryRules) { this.id = id; @@ -77,14 +78,14 @@ public boolean equals(Object obj) { key.equals(optimizelyFeature.getKey()) && experimentsMap.equals(optimizelyFeature.getExperimentsMap()) && variablesMap.equals(optimizelyFeature.getVariablesMap()) && - deliveryRules.equals(optimizelyFeature.getDeliveryRules()) && - experimentRules.equals(optimizelyFeature.getExperimentRules()); + experimentRules.equals(optimizelyFeature.getExperimentRules()) && + deliveryRules.equals(optimizelyFeature.getDeliveryRules()); } @Override public int hashCode() { int result = id.hashCode(); - result = 31 * result + experimentsMap.hashCode() + variablesMap.hashCode(); + result = 31 * result + experimentsMap.hashCode() + variablesMap.hashCode() + experimentRules.hashCode() + deliveryRules.hashCode(); return result; } } diff --git a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java index 07aab156f..da00a9a53 100644 --- a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java +++ b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java @@ -512,8 +512,8 @@ OptimizelyConfig getExpectedConfig() { ) ); }}, - Collections.emptyList(), - Collections.emptyList() + null, + null ) ); optimizelyFeatureMap.put( @@ -523,8 +523,8 @@ OptimizelyConfig getExpectedConfig() { "boolean_feature", Collections.emptyMap(), Collections.emptyMap(), - Collections.emptyList(), - Collections.emptyList() + Collections.emptyList(), + Collections.emptyList() ) ); From 1da63059e314c8069afce25b8f53c90f146e6815 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Thu, 22 Jul 2021 11:20:55 -0400 Subject: [PATCH 25/65] Remove lines for formatting. --- .../src/main/java/com/optimizely/ab/config/Experiment.java | 4 +--- .../com/optimizely/ab/config/parser/JsonConfigParser.java | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/config/Experiment.java b/core-api/src/main/java/com/optimizely/ab/config/Experiment.java index 5288c92fa..02e1aa30f 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/Experiment.java +++ b/core-api/src/main/java/com/optimizely/ab/config/Experiment.java @@ -174,9 +174,7 @@ public boolean isLaunched() { } public String serializeConditions(Map audiencesMap) { - Condition condition = this.audienceConditions; - return condition instanceof EmptyCondition ? "" : this.serialize(condition, audiencesMap); } @@ -241,7 +239,7 @@ public String getNameOrNextCondition(String operand, List conditions, int ctr = 0; if (conditions.isEmpty()) { return ""; - } else if(conditions.size() == 1) { + } else if (conditions.size() == 1) { return serialize(conditions.get(0), audiencesMap); } else { for (Condition con : conditions) { 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 f5a98dae2..c33f30a68 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 @@ -142,7 +142,6 @@ private List parseExperiments(JSONArray experimentJson, String group } Condition conditions = null; - if (experimentObject.has("audienceConditions")) { Object jsonCondition = experimentObject.get("audienceConditions"); conditions = ConditionUtils.parseConditions(AudienceIdCondition.class, jsonCondition); From f09fe7aa1a56c6c6a4e278b3184fc1e2b2b53a5a Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Thu, 22 Jul 2021 12:03:03 -0400 Subject: [PATCH 26/65] Fix testcase for featuresMap. --- .../optimizelyconfig/OptimizelyFeature.java | 6 +- .../OptimizelyConfigServiceTest.java | 68 ++++++++++++++++++- 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java index 23fd4d9ec..d2d2d5ad0 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java @@ -85,7 +85,11 @@ public boolean equals(Object obj) { @Override public int hashCode() { int result = id.hashCode(); - result = 31 * result + experimentsMap.hashCode() + variablesMap.hashCode() + experimentRules.hashCode() + deliveryRules.hashCode(); + result = 31 * result + + experimentsMap.hashCode() + + variablesMap.hashCode() + + experimentRules.hashCode() + + deliveryRules.hashCode(); return result; } } diff --git a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java index da00a9a53..0e17b492d 100644 --- a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java +++ b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java @@ -512,8 +512,72 @@ OptimizelyConfig getExpectedConfig() { ) ); }}, - null, - null + asList( + new OptimizelyExperiment( + "3262035800", + "multivariate_experiment", + new HashMap() {{ + put( + "Feorge", + new OptimizelyVariation( + "3631049532", + "Feorge", + true, + new HashMap() {{ + put( + "first_letter", + new OptimizelyVariable( + "675244127", + "first_letter", + "string", + "F" + ) + ); + put( + "rest_of_name", + new OptimizelyVariable( + "4052219963", + "rest_of_name", + "string", + "eorge" + ) + ); + }} + ) + ); + put( + "Fred", + new OptimizelyVariation( + "1880281238", + "Fred", + true, + new HashMap() {{ + put( + "first_letter", + new OptimizelyVariable( + "675244127", + "first_letter", + "string", + "F" + ) + ); + put( + "rest_of_name", + new OptimizelyVariable( + "4052219963", + "rest_of_name", + "string", + "red" + ) + ); + }} + ) + ); + }}, + "" + ) + ), + Collections.emptyList() ) ); optimizelyFeatureMap.put( From a8c4aaba93ad7095f3a5cf35bf58b28dc60af7ee Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Fri, 23 Jul 2021 09:31:26 -0400 Subject: [PATCH 27/65] Spacing before operator --- core-api/src/main/java/com/optimizely/ab/config/Experiment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-api/src/main/java/com/optimizely/ab/config/Experiment.java b/core-api/src/main/java/com/optimizely/ab/config/Experiment.java index 02e1aa30f..d23359327 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/Experiment.java +++ b/core-api/src/main/java/com/optimizely/ab/config/Experiment.java @@ -223,7 +223,7 @@ public String serialize(Condition condition, Map audiencesMap) { if (notCondition instanceof AudienceIdCondition) { stringBuilder.append(serialize(notCondition, audiencesMap)); } else { - stringBuilder.append("(" + serialize(notCondition, audiencesMap)+ ")"); + stringBuilder.append("(" + serialize(notCondition, audiencesMap) + ")"); } break; default: From 7baef321501f74497863cba0025866f3eaaefd95 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Fri, 23 Jul 2021 10:01:40 -0400 Subject: [PATCH 28/65] Move audiencesMap to global to be reused. --- .../OptimizelyConfigService.java | 42 +++++++++++++------ .../OptimizelyConfigServiceTest.java | 17 ++++++++ 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java index 63f0c42fa..c5a176fcf 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java @@ -34,6 +34,7 @@ public class OptimizelyConfigService { public OptimizelyConfigService(ProjectConfig projectConfig) { this.projectConfig = projectConfig; this.audiences = getAudiencesList(projectConfig.getTypedAudiences(), projectConfig.getAudiences()); + this.audiencesMap = getAudiencesMap(this.audiences); List optimizelyAttributes = new ArrayList<>(); List optimizelyEvents = new ArrayList<>(); @@ -61,18 +62,6 @@ public OptimizelyConfigService(ProjectConfig projectConfig) { } } - audiencesMap = new HashMap<>(); - - // Build audienceMap as [id:name] - if (this.audiences != null) { - for (OptimizelyAudience audience : this.audiences) { - audiencesMap.put( - audience.getId(), - audience.getName() - ); - } - } - optimizelyConfig = new OptimizelyConfig( experimentsMap, getFeaturesMap(experimentsMap), @@ -126,6 +115,18 @@ Map getExperimentsMap() { } Map featureExperimentMap = new HashMap<>(); + audiencesMap = new HashMap<>(); + + // Build audienceMap as [id:name] + if (this.audiences != null) { + for (OptimizelyAudience audience : this.audiences) { + audiencesMap.put( + audience.getId(), + audience.getName() + ); + } + } + for (Experiment experiment : experiments) { OptimizelyExperiment optimizelyExperiment = new OptimizelyExperiment( experiment.getId(), @@ -342,4 +343,21 @@ List getAudiencesList(List typedAudiences, List getAudiencesMap(List optimizelyAudiences) { + Map audiencesMap = new HashMap<>(); + + // Build audienceMap as [id:name] + if (optimizelyAudiences != null) { + for (OptimizelyAudience audience : optimizelyAudiences) { + audiencesMap.put( + audience.getId(), + audience.getName() + ); + } + } + + return audiencesMap; + } } diff --git a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java index 0e17b492d..9123a5832 100644 --- a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java +++ b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java @@ -156,6 +156,23 @@ public void testGetMergedVariablesMap() { assertEquals(expectedOptimizelyVariableMap, optimizelyVariableMap); } + @Test + public void testGetAudiencesMap() { + Map actualAudiencesMap = optimizelyConfigService.getAudiencesMap( + asList( + new OptimizelyAudience( + "123456", + "test_audience_1", + "['and', ['or', '1', '2'], '3']" + ) + ) + ); + + Map expectedAudiencesMap = optimizelyConfigService.getAudiencesMap(expectedConfig.getAudiences()); + + assertEquals(expectedAudiencesMap, actualAudiencesMap); + } + private ProjectConfig generateOptimizelyConfig() { return new DatafileProjectConfig( "2360254204", From 5df424e8d055a134544425b56494cb13f7ac5918 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Thu, 29 Jul 2021 08:18:39 -0400 Subject: [PATCH 29/65] Remove unused audiencesMap from getExperimentsMap method. --- .../ab/optimizelyconfig/OptimizelyConfigService.java | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java index c5a176fcf..c64b66559 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java @@ -115,18 +115,6 @@ Map getExperimentsMap() { } Map featureExperimentMap = new HashMap<>(); - audiencesMap = new HashMap<>(); - - // Build audienceMap as [id:name] - if (this.audiences != null) { - for (OptimizelyAudience audience : this.audiences) { - audiencesMap.put( - audience.getId(), - audience.getName() - ); - } - } - for (Experiment experiment : experiments) { OptimizelyExperiment optimizelyExperiment = new OptimizelyExperiment( experiment.getId(), From 011a6d4e9e4e04ae7437a792340dd97b1cedcbec Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Fri, 6 Aug 2021 08:59:45 -0400 Subject: [PATCH 30/65] Update sdkKey and environmentKey to default to a blank string --- .../com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java index 5e8eb82dc..d0b11d357 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java @@ -50,8 +50,8 @@ public OptimizelyConfig(Map experimentsMap, this.experimentsMap = experimentsMap; this.featuresMap = featuresMap; this.revision = revision; - this.sdkKey = sdkKey; - this.environmentKey = environmentKey; + this.sdkKey = sdkKey.isEmpty() || sdkKey == null ? "" : sdkKey; + this.environmentKey = environmentKey.isEmpty() || environmentKey == null ? "" : environmentKey; this.attributes = attributes; this.events = events; this.audiences = audiences; From 974ea931861d3913bb78a28c8d4600e222dad045 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Fri, 6 Aug 2021 09:42:04 -0400 Subject: [PATCH 31/65] remove redundant null check. --- .../com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java index d0b11d357..769a61d28 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java @@ -50,8 +50,8 @@ public OptimizelyConfig(Map experimentsMap, this.experimentsMap = experimentsMap; this.featuresMap = featuresMap; this.revision = revision; - this.sdkKey = sdkKey.isEmpty() || sdkKey == null ? "" : sdkKey; - this.environmentKey = environmentKey.isEmpty() || environmentKey == null ? "" : environmentKey; + this.sdkKey = sdkKey.isEmpty() ? "" : sdkKey; + this.environmentKey = environmentKey.isEmpty() ? "" : environmentKey; this.attributes = attributes; this.events = events; this.audiences = audiences; From 272bbe11bb8354154e886c1bb5a14fb1d34d8a30 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Fri, 6 Aug 2021 10:22:54 -0400 Subject: [PATCH 32/65] default string for sdkKey and EnvironmentKey testing. --- .../com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java index 769a61d28..c55e6aeb5 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java @@ -50,8 +50,8 @@ public OptimizelyConfig(Map experimentsMap, this.experimentsMap = experimentsMap; this.featuresMap = featuresMap; this.revision = revision; - this.sdkKey = sdkKey.isEmpty() ? "" : sdkKey; - this.environmentKey = environmentKey.isEmpty() ? "" : environmentKey; + this.sdkKey = sdkKey == null ? "" : sdkKey; + this.environmentKey = environmentKey == null ? "" : environmentKey; this.attributes = attributes; this.events = events; this.audiences = audiences; From ec15d257355f3bdf2f8a0b43221f8fa2205480c6 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Fri, 6 Aug 2021 10:38:45 -0400 Subject: [PATCH 33/65] revert to test FSC --- .../com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java index c55e6aeb5..5e8eb82dc 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java @@ -50,8 +50,8 @@ public OptimizelyConfig(Map experimentsMap, this.experimentsMap = experimentsMap; this.featuresMap = featuresMap; this.revision = revision; - this.sdkKey = sdkKey == null ? "" : sdkKey; - this.environmentKey = environmentKey == null ? "" : environmentKey; + this.sdkKey = sdkKey; + this.environmentKey = environmentKey; this.attributes = attributes; this.events = events; this.audiences = audiences; From 5fccddf1fec3eda33d2c49d43e780f264f8588cd Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Fri, 6 Aug 2021 13:59:41 -0400 Subject: [PATCH 34/65] default sdkKey and environmentKey to empty string --- .../com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java index 5e8eb82dc..e440d6652 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java @@ -50,8 +50,8 @@ public OptimizelyConfig(Map experimentsMap, this.experimentsMap = experimentsMap; this.featuresMap = featuresMap; this.revision = revision; - this.sdkKey = sdkKey; - this.environmentKey = environmentKey; + this.sdkKey = sdkKey.isEmpty() ? "" : sdkKey; + this.environmentKey = environmentKey.isEmpty() ? "" : environmentKey; this.attributes = attributes; this.events = events; this.audiences = audiences; From ff0e4adc652a4f90bf5a0d304f8f59dc4697bc80 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Fri, 6 Aug 2021 14:26:21 -0400 Subject: [PATCH 35/65] Add double quotes to to_string method for conditions. --- .../optimizely/ab/config/audience/AndCondition.java | 2 +- .../optimizely/ab/config/audience/NotCondition.java | 2 +- .../optimizely/ab/config/audience/OrCondition.java | 2 +- .../optimizely/ab/config/audience/UserAttribute.java | 12 ++++++------ .../ab/optimizelyconfig/OptimizelyConfigService.java | 3 --- 5 files changed, 9 insertions(+), 12 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/AndCondition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/AndCondition.java index 8b458d059..cd92f6abc 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/AndCondition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/AndCondition.java @@ -70,7 +70,7 @@ public Boolean evaluate(ProjectConfig config, Map attributes) { @Override public String toString() { StringBuilder s = new StringBuilder(); - s.append("[and, "); + s.append("[\"and\", "); for (int i = 0; i < conditions.size(); i++) { s.append(conditions.get(i)); if (i < conditions.size() - 1) diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/NotCondition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/NotCondition.java index b7f45f2ac..b42b29a01 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/NotCondition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/NotCondition.java @@ -51,7 +51,7 @@ public Boolean evaluate(ProjectConfig config, Map attributes) { public String toString() { StringBuilder s = new StringBuilder(); - s.append("[not, "); + s.append("[\"not\", "); s.append(condition); s.append("]"); diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/OrCondition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/OrCondition.java index 70572a9a9..838569033 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/OrCondition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/OrCondition.java @@ -69,7 +69,7 @@ public Boolean evaluate(ProjectConfig config, Map attributes) { public String toString() { StringBuilder s = new StringBuilder(); - s.append("[or, "); + s.append("[\"or\", "); for (int i = 0; i < conditions.size(); i++) { s.append(conditions.get(i)); if (i < conditions.size() - 1) diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java b/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java index 277f2f184..b94d9ee48 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java @@ -124,15 +124,15 @@ public String toString() { if (value == null) { valueStr = "null"; } else if (value instanceof String) { - valueStr = String.format("'%s'", value); + valueStr = String.format("%s", value); } else { valueStr = value.toString(); } - return "{name='" + name + "\'" + - ", type='" + type + "\'" + - ", match='" + match + "\'" + - ", value=" + valueStr + - "}"; + return "{\"name\"=\"" + name + "\"" + + ", \"type\"=\"" + type + "\"" + + ", \"match\"=\"" + match + "\"" + + ", \"value\"=\"" + valueStr + + "\"}"; } @Override diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java index c64b66559..87e40db45 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java @@ -18,11 +18,8 @@ import com.optimizely.ab.annotations.VisibleForTesting; import com.optimizely.ab.config.*; import com.optimizely.ab.config.audience.Audience; -import com.optimizely.ab.config.audience.AudienceIdCondition; -import com.optimizely.ab.config.audience.Condition; import java.util.*; -import java.util.stream.Collectors; public class OptimizelyConfigService { From d382c3dae11b0e9fd07f4c0a849d34aab44eab21 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Fri, 6 Aug 2021 14:47:02 -0400 Subject: [PATCH 36/65] Update check for dummy audience --- .../com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java | 4 ++-- .../ab/optimizelyconfig/OptimizelyConfigService.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java index e440d6652..f86366eff 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java @@ -50,8 +50,8 @@ public OptimizelyConfig(Map experimentsMap, this.experimentsMap = experimentsMap; this.featuresMap = featuresMap; this.revision = revision; - this.sdkKey = sdkKey.isEmpty() ? "" : sdkKey; - this.environmentKey = environmentKey.isEmpty() ? "" : environmentKey; + this.sdkKey = sdkKey == null ? "" : sdkKey; + this.environmentKey = environmentKey == null ? "" : environmentKey; this.attributes = attributes; this.events = events; this.audiences = audiences; diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java index 87e40db45..927f270ee 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java @@ -315,7 +315,7 @@ List getAudiencesList(List typedAudiences, List Date: Fri, 6 Aug 2021 15:12:12 -0400 Subject: [PATCH 37/65] Update Tests --- .../audience/AudienceConditionEvaluationTest.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/core-api/src/test/java/com/optimizely/ab/config/audience/AudienceConditionEvaluationTest.java b/core-api/src/test/java/com/optimizely/ab/config/audience/AudienceConditionEvaluationTest.java index 0a6e41ddc..621f07b03 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/audience/AudienceConditionEvaluationTest.java +++ b/core-api/src/test/java/com/optimizely/ab/config/audience/AudienceConditionEvaluationTest.java @@ -115,7 +115,7 @@ public void unexpectedAttributeType() throws Exception { UserAttribute testInstance = new UserAttribute("browser_type", "custom_attribute", "gt", 20); assertNull(testInstance.evaluate(null, testUserAttributes)); logbackVerifier.expectMessage(Level.WARN, - "Audience condition \"{name='browser_type', type='custom_attribute', match='gt', value=20}\" evaluated to UNKNOWN because a value of type \"java.lang.String\" was passed for user attribute \"browser_type\""); + "Audience condition \"{\"name\"=\"browser_type\", \"type\"=\"custom_attribute\", \"match\"=\"gt\", \"value\"=\"20\"}\" evaluated to UNKNOWN because a value of type \"java.lang.String\" was passed for user attribute \"browser_type\""); } /** @@ -126,7 +126,7 @@ public void unexpectedAttributeTypeNull() throws Exception { UserAttribute testInstance = new UserAttribute("browser_type", "custom_attribute", "gt", 20); assertNull(testInstance.evaluate(null, Collections.singletonMap("browser_type", null))); logbackVerifier.expectMessage(Level.DEBUG, - "Audience condition \"{name='browser_type', type='custom_attribute', match='gt', value=20}\" evaluated to UNKNOWN because a null value was passed for user attribute \"browser_type\""); + "Audience condition \"{\"name\"=\"browser_type\", \"type\"=\"custom_attribute\", \"match\"=\"gt\", \"value\"=\"20\"}\" evaluated to UNKNOWN because a null value was passed for user attribute \"browser_type\""); } @@ -138,7 +138,7 @@ public void missingAttribute() throws Exception { UserAttribute testInstance = new UserAttribute("browser_type", "custom_attribute", "gt", 20); assertNull(testInstance.evaluate(null, Collections.EMPTY_MAP)); logbackVerifier.expectMessage(Level.DEBUG, - "Audience condition \"{name='browser_type', type='custom_attribute', match='gt', value=20}\" evaluated to UNKNOWN because no value was passed for user attribute \"browser_type\""); + "Audience condition \"{\"name\"=\"browser_type\", \"type\"=\"custom_attribute\", \"match\"=\"gt\", \"value\"=\"20\"}\" evaluated to UNKNOWN because no value was passed for user attribute \"browser_type\""); } /** @@ -149,7 +149,7 @@ public void nullAttribute() throws Exception { UserAttribute testInstance = new UserAttribute("browser_type", "custom_attribute", "gt", 20); assertNull(testInstance.evaluate(null, null)); logbackVerifier.expectMessage(Level.DEBUG, - "Audience condition \"{name='browser_type', type='custom_attribute', match='gt', value=20}\" evaluated to UNKNOWN because no value was passed for user attribute \"browser_type\""); + "Audience condition \"{\"name\"=\"browser_type\", \"type\"=\"custom_attribute\", \"match\"=\"gt\", \"value\"=\"20\"}\" evaluated to UNKNOWN because no value was passed for user attribute \"browser_type\""); } /** @@ -160,7 +160,8 @@ public void unknownConditionType() throws Exception { UserAttribute testInstance = new UserAttribute("browser_type", "blah", "exists", "firefox"); assertNull(testInstance.evaluate(null, testUserAttributes)); logbackVerifier.expectMessage(Level.WARN, - "Audience condition \"{name='browser_type', type='blah', match='exists', value='firefox'}\" uses an unknown condition type. You may need to upgrade to a newer release of the Optimizely SDK."); + "Audience condition \"{\"name\"=\"browser_type\", \"type\"=\"blah\", \"match\"=\"exists\", \"value\"=\"firefox\"}\" uses an unknown condition type. You may need to upgrade to a newer release of the Optimizely SDK."); + "Audience condition \"{\"name\"=\"browser_type\", \"type\"=\"blah\", \"match\"=\"exists\", \"value\"=\"firefox\"}\" uses an unknown condition type. You may need to upgrade to a newer release of the Optimizely SDK."); } /** From ae7b43c3499fcff39f9dbac6dcaf1f4d4a3178cd Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Fri, 6 Aug 2021 19:36:52 -0400 Subject: [PATCH 38/65] Test using toJson for audience conditions. --- .../ab/config/audience/AndCondition.java | 16 ++++++++++- .../config/audience/AudienceIdCondition.java | 3 ++ .../ab/config/audience/Condition.java | 2 ++ .../ab/config/audience/EmptyCondition.java | 2 ++ .../ab/config/audience/NotCondition.java | 13 ++++++++- .../ab/config/audience/NullCondition.java | 3 ++ .../ab/config/audience/OrCondition.java | 17 ++++++++++- .../ab/config/audience/UserAttribute.java | 28 +++++++++++++++---- .../OptimizelyConfigService.java | 4 +-- .../AudienceConditionEvaluationTest.java | 11 ++++---- .../OptimizelyConfigServiceTest.java | 4 +-- 11 files changed, 84 insertions(+), 19 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/AndCondition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/AndCondition.java index cd92f6abc..fbe6bb4f8 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/AndCondition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/AndCondition.java @@ -68,9 +68,23 @@ public Boolean evaluate(ProjectConfig config, Map attributes) { } @Override - public String toString() { + public String toJson() { StringBuilder s = new StringBuilder(); s.append("[\"and\", "); + for (int i = 0; i < conditions.size(); i++) { + s.append(conditions.get(i).toJson()); + if (i < conditions.size() - 1) + s.append(", "); + } + s.append("]"); + + return s.toString(); + } + + @Override + public String toString() { + StringBuilder s = new StringBuilder(); + s.append("[and, "); for (int i = 0; i < conditions.size(); i++) { s.append(conditions.get(i)); if (i < conditions.size() - 1) diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/AudienceIdCondition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/AudienceIdCondition.java index c4f052ebb..7d173d3e5 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/AudienceIdCondition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/AudienceIdCondition.java @@ -101,4 +101,7 @@ public int hashCode() { public String toString() { return audienceId; } + + @Override + public String toJson() { return null; } } diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/Condition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/Condition.java index 772d2b03e..6e3872ad5 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/Condition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/Condition.java @@ -28,4 +28,6 @@ public interface Condition { @Nullable Boolean evaluate(ProjectConfig config, Map attributes); + + String toJson(); } diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/EmptyCondition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/EmptyCondition.java index 8f8aedeae..4cc57d6ef 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/EmptyCondition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/EmptyCondition.java @@ -27,4 +27,6 @@ public Boolean evaluate(ProjectConfig config, Map attributes) { return true; } + @Override + public String toJson() { return null; } } diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/NotCondition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/NotCondition.java index b42b29a01..1347be4fb 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/NotCondition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/NotCondition.java @@ -48,10 +48,21 @@ public Boolean evaluate(ProjectConfig config, Map attributes) { } @Override - public String toString() { + public String toJson() { StringBuilder s = new StringBuilder(); s.append("[\"not\", "); + s.append(condition.toJson()); + s.append("]"); + + return s.toString(); + } + + @Override + public String toString() { + StringBuilder s = new StringBuilder(); + + s.append("[not, "); s.append(condition); s.append("]"); diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/NullCondition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/NullCondition.java index fcf5100db..99ee68275 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/NullCondition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/NullCondition.java @@ -26,4 +26,7 @@ public class NullCondition implements Condition { public Boolean evaluate(ProjectConfig config, Map attributes) { return null; } + + @Override + public String toJson() { return null; } } diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/OrCondition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/OrCondition.java index 838569033..38e21cafb 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/OrCondition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/OrCondition.java @@ -66,10 +66,25 @@ public Boolean evaluate(ProjectConfig config, Map attributes) { } @Override - public String toString() { + public String toJson() { StringBuilder s = new StringBuilder(); s.append("[\"or\", "); + for (int i = 0; i < conditions.size(); i++) { + s.append(conditions.get(i).toJson()); + if (i < conditions.size() - 1) + s.append(", "); + } + s.append("]"); + + return s.toString(); + } + + @Override + public String toString() { + StringBuilder s = new StringBuilder(); + + s.append("[or, "); for (int i = 0; i < conditions.size(); i++) { s.append(conditions.get(i)); if (i < conditions.size() - 1) diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java b/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java index b94d9ee48..44680c9f7 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java @@ -118,21 +118,37 @@ public Boolean evaluate(ProjectConfig config, Map attributes) { return null; } + public String toJson() { + final String valueStr; + if (value == null) { + valueStr = "null"; + } else if (value instanceof String) { + valueStr = String.format("\"%s\"", value); + } else { + valueStr = "\"" + value.toString() + "\""; + } + return "{\"name\":\"" + name + "\"" + + ", \"type\":\"" + type + "\"" + + ", \"match\":\"" + match + "\"" + + ", \"value\":" + valueStr + + "}"; + } + @Override public String toString() { final String valueStr; if (value == null) { valueStr = "null"; } else if (value instanceof String) { - valueStr = String.format("%s", value); + valueStr = String.format("'%s'", value); } else { valueStr = value.toString(); } - return "{\"name\"=\"" + name + "\"" + - ", \"type\"=\"" + type + "\"" + - ", \"match\"=\"" + match + "\"" + - ", \"value\"=\"" + valueStr + - "\"}"; + return "{name='" + name + "\'" + + ", type='" + type + "\'" + + ", match='" + match + "\'" + + ", value=" + valueStr + + "}"; } @Override diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java index 927f270ee..06c6e1466 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java @@ -306,7 +306,7 @@ List getAudiencesList(List typedAudiences, List getAudiencesList(List typedAudiences, List Date: Fri, 6 Aug 2021 19:53:04 -0400 Subject: [PATCH 39/65] Remove quotes around value in userAttribute. --- .../java/com/optimizely/ab/config/audience/UserAttribute.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java b/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java index 44680c9f7..1ac502af0 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java @@ -118,12 +118,13 @@ public Boolean evaluate(ProjectConfig config, Map attributes) { return null; } + @Override public String toJson() { final String valueStr; if (value == null) { valueStr = "null"; } else if (value instanceof String) { - valueStr = String.format("\"%s\"", value); + valueStr = String.format("%s", value); } else { valueStr = "\"" + value.toString() + "\""; } From 9830de18a8345bbbc9c5fac8156f27aadf0c909c Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Mon, 9 Aug 2021 08:13:03 -0400 Subject: [PATCH 40/65] Null check on attributes in UserAttributes before creating conditions string. --- .../optimizely/ab/config/audience/UserAttribute.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java b/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java index 1ac502af0..1c71fdf77 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java @@ -128,11 +128,13 @@ public String toJson() { } else { valueStr = "\"" + value.toString() + "\""; } - return "{\"name\":\"" + name + "\"" + - ", \"type\":\"" + type + "\"" + - ", \"match\":\"" + match + "\"" + - ", \"value\":" + valueStr + - "}"; + StringBuilder attributes = new StringBuilder(); + if (name != null) attributes.append("{\"name\":\"" + name + "\""); + if (type != null) attributes.append(", \"type\":\"" + type + "\""); + if (match != null) attributes.append(", \"match\":\"" + match + "\""); + attributes.append(", \"value\":" + valueStr + "}"); + + return attributes.toString(); } @Override From c9d9dca5dfe16ed3e0298c9f56861b55df4c8e92 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Mon, 9 Aug 2021 08:14:59 -0400 Subject: [PATCH 41/65] remove quotes around valueStr for attributes. --- .../java/com/optimizely/ab/config/audience/UserAttribute.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java b/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java index 1c71fdf77..dddb1b52a 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java @@ -126,7 +126,7 @@ public String toJson() { } else if (value instanceof String) { valueStr = String.format("%s", value); } else { - valueStr = "\"" + value.toString() + "\""; + valueStr = value.toString(); } StringBuilder attributes = new StringBuilder(); if (name != null) attributes.append("{\"name\":\"" + name + "\""); From 7613fdef8e7b5ea712ab6d480cc6f4812344f03d Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Mon, 9 Aug 2021 08:36:51 -0400 Subject: [PATCH 42/65] Add in quotes and feature Id to Delivery Rules. More to add still for Delivery Rules to properly retrieve variables for variations. --- .../ab/config/audience/UserAttribute.java | 20 ++++++++----------- .../OptimizelyConfigService.java | 4 ++-- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java b/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java index dddb1b52a..e6e816ce3 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java @@ -118,8 +118,7 @@ public Boolean evaluate(ProjectConfig config, Map attributes) { return null; } - @Override - public String toJson() { + public String getValueStr() { final String valueStr; if (value == null) { valueStr = "null"; @@ -128,29 +127,26 @@ public String toJson() { } else { valueStr = value.toString(); } + return valueStr; + } + + @Override + public String toJson() { StringBuilder attributes = new StringBuilder(); if (name != null) attributes.append("{\"name\":\"" + name + "\""); if (type != null) attributes.append(", \"type\":\"" + type + "\""); if (match != null) attributes.append(", \"match\":\"" + match + "\""); - attributes.append(", \"value\":" + valueStr + "}"); + attributes.append(", \"value\":\"" + getValueStr() + "\"}"); return attributes.toString(); } @Override public String toString() { - final String valueStr; - if (value == null) { - valueStr = "null"; - } else if (value instanceof String) { - valueStr = String.format("'%s'", value); - } else { - valueStr = value.toString(); - } return "{name='" + name + "\'" + ", type='" + type + "\'" + ", match='" + match + "\'" + - ", value=" + valueStr + + ", value=" + getValueStr() + "}"; } diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java index 06c6e1466..a062cc30a 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java @@ -216,7 +216,7 @@ Map getFeaturesMap(Map List experimentRules = new ArrayList(experimentsMapForFeature.values()); List deliveryRules = - this.getDeliveryRules(featureFlag.getRolloutId()); + this.getDeliveryRules(featureFlag.getRolloutId(), featureFlag.getId()); OptimizelyFeature optimizelyFeature = new OptimizelyFeature( featureFlag.getId(), @@ -232,7 +232,7 @@ Map getFeaturesMap(Map return optimizelyFeatureKeyMap; } - List getDeliveryRules(String rolloutId) { + List getDeliveryRules(String rolloutId, String featureId) { List deliveryRules = new ArrayList(); From 5bbe711e56c870f8f89472ac040131a18cf33bd2 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Mon, 9 Aug 2021 08:49:39 -0400 Subject: [PATCH 43/65] Remove featureId from delivery rules. --- .../ab/optimizelyconfig/OptimizelyConfigService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java index a062cc30a..06c6e1466 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java @@ -216,7 +216,7 @@ Map getFeaturesMap(Map List experimentRules = new ArrayList(experimentsMapForFeature.values()); List deliveryRules = - this.getDeliveryRules(featureFlag.getRolloutId(), featureFlag.getId()); + this.getDeliveryRules(featureFlag.getRolloutId()); OptimizelyFeature optimizelyFeature = new OptimizelyFeature( featureFlag.getId(), @@ -232,7 +232,7 @@ Map getFeaturesMap(Map return optimizelyFeatureKeyMap; } - List getDeliveryRules(String rolloutId, String featureId) { + List getDeliveryRules(String rolloutId) { List deliveryRules = new ArrayList(); From a7766d01d20c2dbae5e023428832200462550c76 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Mon, 9 Aug 2021 09:05:47 -0400 Subject: [PATCH 44/65] test --- .../ab/config/audience/UserAttribute.java | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java b/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java index e6e816ce3..7788a62db 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java @@ -132,21 +132,37 @@ public String getValueStr() { @Override public String toJson() { + final String valueStr; + if (value == null) { + valueStr = "null"; + } else if (value instanceof String) { + valueStr = String.format("%s", value); + } else { + valueStr = value.toString(); + } StringBuilder attributes = new StringBuilder(); if (name != null) attributes.append("{\"name\":\"" + name + "\""); if (type != null) attributes.append(", \"type\":\"" + type + "\""); if (match != null) attributes.append(", \"match\":\"" + match + "\""); - attributes.append(", \"value\":\"" + getValueStr() + "\"}"); + attributes.append(", \"value\":\"" + valueStr + "\"}"); return attributes.toString(); } @Override public String toString() { + final String valueStr; + if (value == null) { + valueStr = "null"; + } else if (value instanceof String) { + valueStr = String.format("'%s'", value); + } else { + valueStr = value.toString(); + } return "{name='" + name + "\'" + ", type='" + type + "\'" + ", match='" + match + "\'" + - ", value=" + getValueStr() + + ", value=" + valueStr + "}"; } From 8e737dd007ae6370b9017310faac8e0f53ee6798 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Mon, 9 Aug 2021 09:37:36 -0400 Subject: [PATCH 45/65] Add test cases for toJson method --- .../optimizely/ab/config/audience/UserAttribute.java | 10 +--------- .../audience/AudienceConditionEvaluationTest.java | 11 +++++++++++ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java b/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java index 7788a62db..b8cfbaf0a 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java @@ -132,19 +132,11 @@ public String getValueStr() { @Override public String toJson() { - final String valueStr; - if (value == null) { - valueStr = "null"; - } else if (value instanceof String) { - valueStr = String.format("%s", value); - } else { - valueStr = value.toString(); - } StringBuilder attributes = new StringBuilder(); if (name != null) attributes.append("{\"name\":\"" + name + "\""); if (type != null) attributes.append(", \"type\":\"" + type + "\""); if (match != null) attributes.append(", \"match\":\"" + match + "\""); - attributes.append(", \"value\":\"" + valueStr + "\"}"); + attributes.append(", \"value\":\"" + getValueStr() + "\"}"); return attributes.toString(); } diff --git a/core-api/src/test/java/com/optimizely/ab/config/audience/AudienceConditionEvaluationTest.java b/core-api/src/test/java/com/optimizely/ab/config/audience/AudienceConditionEvaluationTest.java index 0a6e41ddc..80d4ef9d9 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/audience/AudienceConditionEvaluationTest.java +++ b/core-api/src/test/java/com/optimizely/ab/config/audience/AudienceConditionEvaluationTest.java @@ -56,6 +56,17 @@ public void initialize() { testTypedUserAttributes.put("null_val", null); } + /** + * Verify that UserAttribute.toJson returns a json represented string of conditions. + */ + @Test + public void userAttributeConditionsToJson() throws Exception { + UserAttribute testInstance = new UserAttribute("browser_type", "custom_attribute", "true", "safari"); + String expectedConditionJsonString = "{\"name\":\"browser_type\", \"type\":\"custom_attribute\", \"match\":\"true\", \"value\":\"safari\"}"; + assertEquals(testInstance.toJson(), expectedConditionJsonString); + } + + /** * Verify that UserAttribute.evaluate returns true on exact-matching visitor attribute data. */ From fe32f57a72bb5378750aec686a2abcfdf628de9f Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Mon, 9 Aug 2021 10:12:35 -0400 Subject: [PATCH 46/65] Initial commit for variationsMap fix. --- .../ab/config/audience/UserAttribute.java | 10 +-- .../OptimizelyConfigService.java | 73 ++++++++++--------- .../OptimizelyConfigServiceTest.java | 4 +- 3 files changed, 43 insertions(+), 44 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java b/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java index b8cfbaf0a..b2b90f37a 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java @@ -143,18 +143,10 @@ public String toJson() { @Override public String toString() { - final String valueStr; - if (value == null) { - valueStr = "null"; - } else if (value instanceof String) { - valueStr = String.format("'%s'", value); - } else { - valueStr = value.toString(); - } return "{name='" + name + "\'" + ", type='" + type + "\'" + ", match='" + match + "\'" + - ", value=" + valueStr + + ", value=" + ((value instanceof String) ? ("'" + getValueStr() + "'") : getValueStr()) + "}"; } diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java index 06c6e1466..ed61fff5b 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java @@ -27,6 +27,7 @@ public class OptimizelyConfigService { private OptimizelyConfig optimizelyConfig; private List audiences; private Map audiencesMap; + private Map> featureIdToVariablesMap = new HashMap<>(); public OptimizelyConfigService(ProjectConfig projectConfig) { this.projectConfig = projectConfig; @@ -94,6 +95,7 @@ Map> generateFeatureKeyToVariablesMap() { Map> featureVariableIdMap = new HashMap<>(); for (FeatureFlag featureFlag : featureFlags) { featureVariableIdMap.put(featureFlag.getKey(), featureFlag.getVariables()); + featureIdToVariablesMap.put(featureFlag.getId(), featureFlag.getVariables()); } return featureVariableIdMap; } @@ -116,7 +118,7 @@ Map getExperimentsMap() { OptimizelyExperiment optimizelyExperiment = new OptimizelyExperiment( experiment.getId(), experiment.getKey(), - getVariationsMap(experiment.getVariations(), experiment.getId()), + getVariationsMap(experiment.getVariations(), experiment.getId(), null), experiment.serializeConditions(this.audiencesMap) ); @@ -126,7 +128,7 @@ Map getExperimentsMap() { } @VisibleForTesting - Map getVariationsMap(List variations, String experimentId) { + Map getVariationsMap(List variations, String experimentId, String featureId) { if (variations == null) { return Collections.emptyMap(); } @@ -137,7 +139,7 @@ Map getVariationsMap(List variations, St variation.getId(), variation.getKey(), isFeatureExperiment ? variation.getFeatureEnabled() : null, - getMergedVariablesMap(variation, experimentId) + getMergedVariablesMap(variation, experimentId, featureId) )); } return variationKeyMap; @@ -150,36 +152,41 @@ Map getVariationsMap(List variations, St * 3. If Variation does not contain a variable, then all `id`, `key`, `type` and defaultValue as `value` is used from feature varaible and added to variation. */ @VisibleForTesting - Map getMergedVariablesMap(Variation variation, String experimentId) { + Map getMergedVariablesMap(Variation variation, String experimentId, String featureId) { String featureKey = this.getExperimentFeatureKey(experimentId); - if (featureKey != null) { - // Map containing variables list for every feature key used for merging variation and feature variables. - Map> featureKeyToVariablesMap = generateFeatureKeyToVariablesMap(); - - // Generate temp map of all the available variable values from variation. - Map tempVariableIdMap = getFeatureVariableUsageInstanceMap(variation.getFeatureVariableUsageInstances()); - - // Iterate over all the variables available in associated feature. - // Use value from variation variable if variable is available in variation and feature is enabled, otherwise use defaultValue from feature variable. - List featureVariables = featureKeyToVariablesMap.get(featureKey); - if (featureVariables == null) { - return Collections.emptyMap(); - } + Map> featureKeyToVariablesMap = generateFeatureKeyToVariablesMap(); + if (featureKey == null && featureId == null) { + return Collections.emptyMap(); + } - Map featureVariableKeyMap = new HashMap<>(); - for (FeatureVariable featureVariable : featureVariables) { - featureVariableKeyMap.put(featureVariable.getKey(), new OptimizelyVariable( - featureVariable.getId(), - featureVariable.getKey(), - featureVariable.getType(), - variation.getFeatureEnabled() && tempVariableIdMap.get(featureVariable.getId()) != null - ? tempVariableIdMap.get(featureVariable.getId()).getValue() - : featureVariable.getDefaultValue() - )); - } - return featureVariableKeyMap; + // Generate temp map of all the available variable values from variation. + Map tempVariableIdMap = getFeatureVariableUsageInstanceMap(variation.getFeatureVariableUsageInstances()); + + // Iterate over all the variables available in associated feature. + // Use value from variation variable if variable is available in variation and feature is enabled, otherwise use defaultValue from feature variable. + List featureVariables; + + if (featureId != null) { + featureVariables = featureIdToVariablesMap.get(featureId); + } else { + featureVariables = featureKeyToVariablesMap.get(featureKey); } - return Collections.emptyMap(); + if (featureVariables == null) { + return Collections.emptyMap(); + } + + Map featureVariableKeyMap = new HashMap<>(); + for (FeatureVariable featureVariable : featureVariables) { + featureVariableKeyMap.put(featureVariable.getKey(), new OptimizelyVariable( + featureVariable.getId(), + featureVariable.getKey(), + featureVariable.getType(), + variation.getFeatureEnabled() && tempVariableIdMap.get(featureVariable.getId()) != null + ? tempVariableIdMap.get(featureVariable.getId()).getValue() + : featureVariable.getDefaultValue() + )); + } + return featureVariableKeyMap; } @VisibleForTesting @@ -216,7 +223,7 @@ Map getFeaturesMap(Map List experimentRules = new ArrayList(experimentsMapForFeature.values()); List deliveryRules = - this.getDeliveryRules(featureFlag.getRolloutId()); + this.getDeliveryRules(featureFlag.getRolloutId(), featureFlag.getId()); OptimizelyFeature optimizelyFeature = new OptimizelyFeature( featureFlag.getId(), @@ -232,7 +239,7 @@ Map getFeaturesMap(Map return optimizelyFeatureKeyMap; } - List getDeliveryRules(String rolloutId) { + List getDeliveryRules(String rolloutId, String featureId) { List deliveryRules = new ArrayList(); @@ -244,7 +251,7 @@ List getDeliveryRules(String rolloutId) { OptimizelyExperiment optimizelyExperiment = new OptimizelyExperiment( experiment.getId(), experiment.getKey(), - this.getVariationsMap(experiment.getVariations(), experiment.getId()), + this.getVariationsMap(experiment.getVariations(), experiment.getId(), featureId), experiment.serializeConditions(this.audiencesMap) ); diff --git a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java index 22705e659..e52436b33 100644 --- a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java +++ b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java @@ -124,7 +124,7 @@ public void testGetFeatureVariableUsageInstanceMap() { @Test public void testGetVariationsMap() { Map optimizelyVariationMap = - optimizelyConfigService.getVariationsMap(projectConfig.getExperiments().get(1).getVariations(), "3262035800"); + optimizelyConfigService.getVariationsMap(projectConfig.getExperiments().get(1).getVariations(), "3262035800", null); assertEquals(expectedConfig.getExperimentsMap().get("multivariate_experiment").getVariationsMap().size(), optimizelyVariationMap.size()); assertEquals(expectedConfig.getExperimentsMap().get("multivariate_experiment").getVariationsMap(), optimizelyVariationMap); } @@ -149,7 +149,7 @@ public void testGenerateFeatureKeyToVariablesMap() { @Test public void testGetMergedVariablesMap() { Variation variation = projectConfig.getExperiments().get(1).getVariations().get(1); - Map optimizelyVariableMap = optimizelyConfigService.getMergedVariablesMap(variation, "3262035800"); + Map optimizelyVariableMap = optimizelyConfigService.getMergedVariablesMap(variation, "3262035800", null); Map expectedOptimizelyVariableMap = expectedConfig.getExperimentsMap().get("multivariate_experiment").getVariationsMap().get("Feorge").getVariablesMap(); assertEquals(expectedOptimizelyVariableMap.size(), optimizelyVariableMap.size()); From c7a9fcb7b045026f0c0c2097ee8a692cadc6d66f Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Mon, 9 Aug 2021 10:38:06 -0400 Subject: [PATCH 47/65] Test for feature_enabled not displaying --- .../optimizely/ab/optimizelyconfig/OptimizelyConfigService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java index ed61fff5b..0dc836dcb 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java @@ -138,7 +138,7 @@ Map getVariationsMap(List variations, St variationKeyMap.put(variation.getKey(), new OptimizelyVariation( variation.getId(), variation.getKey(), - isFeatureExperiment ? variation.getFeatureEnabled() : null, + variation.getFeatureEnabled(), getMergedVariablesMap(variation, experimentId, featureId) )); } From 52220b7987c54dd88233257296407da0a48d1090 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Mon, 9 Aug 2021 10:56:29 -0400 Subject: [PATCH 48/65] Remove unused isFeatureExperiment. --- .../optimizely/ab/optimizelyconfig/OptimizelyConfigService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java index 0dc836dcb..329666df4 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java @@ -132,7 +132,7 @@ Map getVariationsMap(List variations, St if (variations == null) { return Collections.emptyMap(); } - Boolean isFeatureExperiment = this.getExperimentFeatureKey(experimentId) != null; + Map variationKeyMap = new HashMap<>(); for (Variation variation : variations) { variationKeyMap.put(variation.getKey(), new OptimizelyVariation( From 3146e9d98056d3c2a5352d019bf6106e323cb849 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Mon, 9 Aug 2021 11:27:17 -0400 Subject: [PATCH 49/65] Update condition in toJson for userAttribute to check instance for quotes or not. --- .../java/com/optimizely/ab/config/audience/UserAttribute.java | 2 +- .../optimizely/ab/optimizelyconfig/OptimizelyConfigService.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java b/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java index b2b90f37a..814f9df10 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java @@ -136,7 +136,7 @@ public String toJson() { if (name != null) attributes.append("{\"name\":\"" + name + "\""); if (type != null) attributes.append(", \"type\":\"" + type + "\""); if (match != null) attributes.append(", \"match\":\"" + match + "\""); - attributes.append(", \"value\":\"" + getValueStr() + "\"}"); + attributes.append(", \"value\":" + ((value instanceof String) ? ("\"" + getValueStr() + "\"") : getValueStr()) + "}"); return attributes.toString(); } diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java index 329666df4..c6b20a7ba 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java @@ -132,7 +132,7 @@ Map getVariationsMap(List variations, St if (variations == null) { return Collections.emptyMap(); } - + Map variationKeyMap = new HashMap<>(); for (Variation variation : variations) { variationKeyMap.put(variation.getKey(), new OptimizelyVariation( From fc7fab5645e10ef1b51092231b50b215076f3074 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Mon, 9 Aug 2021 11:52:39 -0400 Subject: [PATCH 50/65] Test for featureEnabled defaulting to Null instead of false. --- .../main/java/com/optimizely/ab/config/Variation.java | 4 ++-- .../optimizely/ab/config/parser/JsonConfigParser.java | 9 +++++++-- 2 files changed, 9 insertions(+), 4 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 0bb1765c2..83d0223ac 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 @@ -48,7 +48,7 @@ public Variation(String id, String key) { public Variation(String id, String key, List featureVariableUsageInstances) { - this(id, key, false, featureVariableUsageInstances); + this(id, key, null, featureVariableUsageInstances); } @JsonCreator @@ -61,7 +61,7 @@ public Variation(@JsonProperty("id") String id, if (featureEnabled != null) this.featureEnabled = featureEnabled; else - this.featureEnabled = false; + this.featureEnabled = null; if (featureVariableUsageInstances == null) { this.featureVariableUsageInstances = Collections.emptyList(); } else { 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 c33f30a68..6103dd91e 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 @@ -206,7 +206,7 @@ private List parseVariations(JSONArray variationJson) { JSONObject variationObject = (JSONObject) obj; String id = variationObject.getString("id"); String key = variationObject.getString("key"); - Boolean featureEnabled = false; + Boolean featureEnabled = null; if (variationObject.has("featureEnabled") && !variationObject.isNull("featureEnabled")) { featureEnabled = variationObject.getBoolean("featureEnabled"); @@ -218,7 +218,12 @@ private List parseVariations(JSONArray variationJson) { parseFeatureVariableInstances(variationObject.getJSONArray("variables")); } - variations.add(new Variation(id, key, featureEnabled, featureVariableUsageInstances)); + variations.add(new Variation( + id, + key, + featureEnabled, + featureVariableUsageInstances + )); } return variations; From 4e9c2fa9cac09ccbadefb83d2761b7ca74341372 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Mon, 9 Aug 2021 12:30:14 -0400 Subject: [PATCH 51/65] Revert changes for featureEnabled to null. --- .../main/java/com/optimizely/ab/config/Variation.java | 4 ++-- .../optimizely/ab/config/parser/JsonConfigParser.java | 9 ++------- 2 files changed, 4 insertions(+), 9 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 83d0223ac..0bb1765c2 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 @@ -48,7 +48,7 @@ public Variation(String id, String key) { public Variation(String id, String key, List featureVariableUsageInstances) { - this(id, key, null, featureVariableUsageInstances); + this(id, key, false, featureVariableUsageInstances); } @JsonCreator @@ -61,7 +61,7 @@ public Variation(@JsonProperty("id") String id, if (featureEnabled != null) this.featureEnabled = featureEnabled; else - this.featureEnabled = null; + this.featureEnabled = false; if (featureVariableUsageInstances == null) { this.featureVariableUsageInstances = Collections.emptyList(); } else { 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 6103dd91e..c33f30a68 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 @@ -206,7 +206,7 @@ private List parseVariations(JSONArray variationJson) { JSONObject variationObject = (JSONObject) obj; String id = variationObject.getString("id"); String key = variationObject.getString("key"); - Boolean featureEnabled = null; + Boolean featureEnabled = false; if (variationObject.has("featureEnabled") && !variationObject.isNull("featureEnabled")) { featureEnabled = variationObject.getBoolean("featureEnabled"); @@ -218,12 +218,7 @@ private List parseVariations(JSONArray variationJson) { parseFeatureVariableInstances(variationObject.getJSONArray("variables")); } - variations.add(new Variation( - id, - key, - featureEnabled, - featureVariableUsageInstances - )); + variations.add(new Variation(id, key, featureEnabled, featureVariableUsageInstances)); } return variations; From 035cca986e0f358a8433473b7e00af5c8b0d8292 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Mon, 9 Aug 2021 13:49:55 -0400 Subject: [PATCH 52/65] Update experiments map to use experimentID rather than Key as experiment rules were incorrect due to multiple experiments with same key. --- .../ab/optimizelyconfig/OptimizelyConfigService.java | 5 ++--- .../OptimizelyConfigServiceTest.java | 12 ++++++------ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java index c6b20a7ba..f25c71eed 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java @@ -122,7 +122,7 @@ Map getExperimentsMap() { experiment.serializeConditions(this.audiencesMap) ); - featureExperimentMap.put(experiment.getKey(), optimizelyExperiment); + featureExperimentMap.put(experiment.getId(), optimizelyExperiment); } return featureExperimentMap; } @@ -271,8 +271,7 @@ Map getExperimentsMapForFeature(List exper Map optimizelyExperimentKeyMap = new HashMap<>(); for (String experimentId : experimentIds) { - String experimentKey = projectConfig.getExperimentIdMapping().get(experimentId).getKey(); - optimizelyExperimentKeyMap.put(experimentKey, allExperimentsMap.get(experimentKey)); + optimizelyExperimentKeyMap.put(experimentId, allExperimentsMap.get(experimentId)); } return optimizelyExperimentKeyMap; diff --git a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java index e52436b33..163801584 100644 --- a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java +++ b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java @@ -125,8 +125,8 @@ public void testGetFeatureVariableUsageInstanceMap() { public void testGetVariationsMap() { Map optimizelyVariationMap = optimizelyConfigService.getVariationsMap(projectConfig.getExperiments().get(1).getVariations(), "3262035800", null); - assertEquals(expectedConfig.getExperimentsMap().get("multivariate_experiment").getVariationsMap().size(), optimizelyVariationMap.size()); - assertEquals(expectedConfig.getExperimentsMap().get("multivariate_experiment").getVariationsMap(), optimizelyVariationMap); + assertEquals(expectedConfig.getExperimentsMap().get("3262035800").getVariationsMap().size(), optimizelyVariationMap.size()); + assertEquals(expectedConfig.getExperimentsMap().get("3262035800").getVariationsMap(), optimizelyVariationMap); } @Test @@ -151,7 +151,7 @@ public void testGetMergedVariablesMap() { Variation variation = projectConfig.getExperiments().get(1).getVariations().get(1); Map optimizelyVariableMap = optimizelyConfigService.getMergedVariablesMap(variation, "3262035800", null); Map expectedOptimizelyVariableMap = - expectedConfig.getExperimentsMap().get("multivariate_experiment").getVariationsMap().get("Feorge").getVariablesMap(); + expectedConfig.getExperimentsMap().get("3262035800").getVariationsMap().get("Feorge").getVariablesMap(); assertEquals(expectedOptimizelyVariableMap.size(), optimizelyVariableMap.size()); assertEquals(expectedOptimizelyVariableMap, optimizelyVariableMap); } @@ -341,7 +341,7 @@ private ProjectConfig generateOptimizelyConfig() { OptimizelyConfig getExpectedConfig() { Map optimizelyExperimentMap = new HashMap<>(); optimizelyExperimentMap.put( - "multivariate_experiment", + "3262035800", new OptimizelyExperiment( "3262035800", "multivariate_experiment", @@ -407,7 +407,7 @@ OptimizelyConfig getExpectedConfig() { ) ); optimizelyExperimentMap.put( - "basic_experiment", + "1323241596", new OptimizelyExperiment( "1323241596", "basic_experiment", @@ -443,7 +443,7 @@ OptimizelyConfig getExpectedConfig() { "multi_variate_feature", new HashMap() {{ put( - "multivariate_experiment", + "3262035800", new OptimizelyExperiment( "3262035800", "multivariate_experiment", From c2efd871efa7ea1312ddd93a1f8eca03478d4802 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Mon, 9 Aug 2021 14:34:37 -0400 Subject: [PATCH 53/65] Update Experiment rules to use Experiment ID. --- .../OptimizelyConfigService.java | 21 ++++++++++++++++--- .../OptimizelyConfigServiceTest.java | 12 +++++------ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java index f25c71eed..aa176b8b5 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java @@ -28,6 +28,7 @@ public class OptimizelyConfigService { private List audiences; private Map audiencesMap; private Map> featureIdToVariablesMap = new HashMap<>(); + private Map experimentMapByExperimentId = new HashMap<>(); public OptimizelyConfigService(ProjectConfig projectConfig) { this.projectConfig = projectConfig; @@ -122,7 +123,8 @@ Map getExperimentsMap() { experiment.serializeConditions(this.audiencesMap) ); - featureExperimentMap.put(experiment.getId(), optimizelyExperiment); + featureExperimentMap.put(experiment.getKey(), optimizelyExperiment); + experimentMapByExperimentId.put(experiment.getId(), optimizelyExperiment); } return featureExperimentMap; } @@ -221,7 +223,7 @@ Map getFeaturesMap(Map getExperimentsMapForFeature(featureFlag.getExperimentIds(), allExperimentsMap); List experimentRules = - new ArrayList(experimentsMapForFeature.values()); + new ArrayList(getExperimentRulesExperiments(featureFlag.getExperimentIds()).values()); List deliveryRules = this.getDeliveryRules(featureFlag.getRolloutId(), featureFlag.getId()); @@ -271,12 +273,25 @@ Map getExperimentsMapForFeature(List exper Map optimizelyExperimentKeyMap = new HashMap<>(); for (String experimentId : experimentIds) { - optimizelyExperimentKeyMap.put(experimentId, allExperimentsMap.get(experimentId)); + String experimentKey = projectConfig.getExperimentIdMapping().get(experimentId).getKey(); + optimizelyExperimentKeyMap.put(experimentKey, allExperimentsMap.get(experimentKey)); } return optimizelyExperimentKeyMap; } + @VisibleForTesting + Map getExperimentRulesExperiments(List experimentIds) { + if (experimentIds == null) { + return Collections.emptyMap(); + } + Map optimizelyExperimentIdMap = new HashMap<>(); + for (String experimentId : experimentIds) { + optimizelyExperimentIdMap.put(experimentId, experimentMapByExperimentId.get(experimentId)); + } + return optimizelyExperimentIdMap; + } + @VisibleForTesting Map getFeatureVariablesMap(List featureVariables) { if (featureVariables == null) { diff --git a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java index 163801584..e52436b33 100644 --- a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java +++ b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java @@ -125,8 +125,8 @@ public void testGetFeatureVariableUsageInstanceMap() { public void testGetVariationsMap() { Map optimizelyVariationMap = optimizelyConfigService.getVariationsMap(projectConfig.getExperiments().get(1).getVariations(), "3262035800", null); - assertEquals(expectedConfig.getExperimentsMap().get("3262035800").getVariationsMap().size(), optimizelyVariationMap.size()); - assertEquals(expectedConfig.getExperimentsMap().get("3262035800").getVariationsMap(), optimizelyVariationMap); + assertEquals(expectedConfig.getExperimentsMap().get("multivariate_experiment").getVariationsMap().size(), optimizelyVariationMap.size()); + assertEquals(expectedConfig.getExperimentsMap().get("multivariate_experiment").getVariationsMap(), optimizelyVariationMap); } @Test @@ -151,7 +151,7 @@ public void testGetMergedVariablesMap() { Variation variation = projectConfig.getExperiments().get(1).getVariations().get(1); Map optimizelyVariableMap = optimizelyConfigService.getMergedVariablesMap(variation, "3262035800", null); Map expectedOptimizelyVariableMap = - expectedConfig.getExperimentsMap().get("3262035800").getVariationsMap().get("Feorge").getVariablesMap(); + expectedConfig.getExperimentsMap().get("multivariate_experiment").getVariationsMap().get("Feorge").getVariablesMap(); assertEquals(expectedOptimizelyVariableMap.size(), optimizelyVariableMap.size()); assertEquals(expectedOptimizelyVariableMap, optimizelyVariableMap); } @@ -341,7 +341,7 @@ private ProjectConfig generateOptimizelyConfig() { OptimizelyConfig getExpectedConfig() { Map optimizelyExperimentMap = new HashMap<>(); optimizelyExperimentMap.put( - "3262035800", + "multivariate_experiment", new OptimizelyExperiment( "3262035800", "multivariate_experiment", @@ -407,7 +407,7 @@ OptimizelyConfig getExpectedConfig() { ) ); optimizelyExperimentMap.put( - "1323241596", + "basic_experiment", new OptimizelyExperiment( "1323241596", "basic_experiment", @@ -443,7 +443,7 @@ OptimizelyConfig getExpectedConfig() { "multi_variate_feature", new HashMap() {{ put( - "3262035800", + "multivariate_experiment", new OptimizelyExperiment( "3262035800", "multivariate_experiment", From 39943ed739ea3c3f4a29f0fd82fe3685486dff0c Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Mon, 9 Aug 2021 15:26:48 -0400 Subject: [PATCH 54/65] Test ordering experimentRules. --- .../optimizely/ab/optimizelyconfig/OptimizelyConfigService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java index aa176b8b5..43c91f7ee 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java @@ -224,6 +224,7 @@ Map getFeaturesMap(Map List experimentRules = new ArrayList(getExperimentRulesExperiments(featureFlag.getExperimentIds()).values()); + Collections.reverse(experimentRules); List deliveryRules = this.getDeliveryRules(featureFlag.getRolloutId(), featureFlag.getId()); From 399333559f36b2f88af826b0cf6c46e8395d931d Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Mon, 9 Aug 2021 15:51:25 -0400 Subject: [PATCH 55/65] Revert delivery rules. --- .../ab/optimizelyconfig/OptimizelyConfigService.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java index 43c91f7ee..770a204a1 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java @@ -223,8 +223,7 @@ Map getFeaturesMap(Map getExperimentsMapForFeature(featureFlag.getExperimentIds(), allExperimentsMap); List experimentRules = - new ArrayList(getExperimentRulesExperiments(featureFlag.getExperimentIds()).values()); - Collections.reverse(experimentRules); + new ArrayList(experimentsMapForFeature.values()); List deliveryRules = this.getDeliveryRules(featureFlag.getRolloutId(), featureFlag.getId()); From c439a2fc3811161bdbf2b48262d1c34ad86f7ac8 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Mon, 9 Aug 2021 16:01:54 -0400 Subject: [PATCH 56/65] Change lookup of feature experiments Map to use experimentsMapByExperimentId. --- .../optimizely/ab/optimizelyconfig/OptimizelyConfigService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java index 770a204a1..3d4da6894 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java @@ -273,7 +273,7 @@ Map getExperimentsMapForFeature(List exper Map optimizelyExperimentKeyMap = new HashMap<>(); for (String experimentId : experimentIds) { - String experimentKey = projectConfig.getExperimentIdMapping().get(experimentId).getKey(); + String experimentKey = experimentMapByExperimentId.get(experimentId).getKey(); optimizelyExperimentKeyMap.put(experimentKey, allExperimentsMap.get(experimentKey)); } From 48dda4ff4ef324378dc9874f75e2e90e3418c9ca Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Mon, 9 Aug 2021 16:27:22 -0400 Subject: [PATCH 57/65] Updated getExperimentsMapForFeature to use new id mapping. --- .../OptimizelyConfigService.java | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java index 3d4da6894..f87de55f0 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java @@ -273,25 +273,13 @@ Map getExperimentsMapForFeature(List exper Map optimizelyExperimentKeyMap = new HashMap<>(); for (String experimentId : experimentIds) { - String experimentKey = experimentMapByExperimentId.get(experimentId).getKey(); - optimizelyExperimentKeyMap.put(experimentKey, allExperimentsMap.get(experimentKey)); + Experiment experiment = projectConfig.getExperimentIdMapping().get(experimentId); + optimizelyExperimentKeyMap.put(experiment.getKey(), experimentMapByExperimentId.get(experiment.getId())); } return optimizelyExperimentKeyMap; } - @VisibleForTesting - Map getExperimentRulesExperiments(List experimentIds) { - if (experimentIds == null) { - return Collections.emptyMap(); - } - Map optimizelyExperimentIdMap = new HashMap<>(); - for (String experimentId : experimentIds) { - optimizelyExperimentIdMap.put(experimentId, experimentMapByExperimentId.get(experimentId)); - } - return optimizelyExperimentIdMap; - } - @VisibleForTesting Map getFeatureVariablesMap(List featureVariables) { if (featureVariables == null) { From efd138379840a94d640af3bf513de94320d523f0 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Wed, 11 Aug 2021 14:24:18 -0400 Subject: [PATCH 58/65] trigger CI --- .../optimizely/ab/optimizelyconfig/OptimizelyConfigService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java index f87de55f0..15035472c 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java @@ -110,6 +110,7 @@ String getExperimentFeatureKey(String experimentId) { @VisibleForTesting Map getExperimentsMap() { List experiments = projectConfig.getExperiments(); + if (experiments == null) { return Collections.emptyMap(); } From 20683c7600cd7cb6805ec131a81952b88f7ae045 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Thu, 12 Aug 2021 08:57:55 -0400 Subject: [PATCH 59/65] Updated change requests. and deprecated warning for OptimizelyFeature.experimentsMap. --- .../com/optimizely/ab/config/Experiment.java | 48 +++++++++---------- .../ab/config/audience/AndCondition.java | 17 ++++--- .../config/audience/AudienceIdCondition.java | 5 ++ .../ab/config/audience/Condition.java | 2 + .../ab/config/audience/EmptyCondition.java | 5 ++ .../ab/config/audience/NotCondition.java | 16 ++++--- .../ab/config/audience/NullCondition.java | 5 ++ .../ab/config/audience/OrCondition.java | 18 +++---- .../ab/config/audience/UserAttribute.java | 5 ++ .../OptimizelyConfigService.java | 2 +- .../optimizelyconfig/OptimizelyFeature.java | 6 +++ 11 files changed, 83 insertions(+), 46 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/config/Experiment.java b/core-api/src/main/java/com/optimizely/ab/config/Experiment.java index d23359327..11530735c 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/Experiment.java +++ b/core-api/src/main/java/com/optimizely/ab/config/Experiment.java @@ -23,7 +23,6 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; @@ -43,6 +42,10 @@ public class Experiment implements IdKeyMapped { private final String layerId; private final String groupId; + private final String AND = "AND"; + private final String OR = "OR"; + private final String NOT = "NOT"; + private final List audienceIds; private final Condition audienceConditions; private final List variations; @@ -179,45 +182,42 @@ public String serializeConditions(Map audiencesMap) { } private String getNameFromAudienceId(String audienceId, Map audiencesMap) { - if (audiencesMap == null ) return "\"" + audienceId + "\""; - String audienceName = "\"" + audiencesMap.get(audienceId) + "\""; - return audiencesMap.get(audienceId) != null ? audienceName : "\"" + audienceId + "\""; + StringBuilder audienceName = new StringBuilder(); + if (audiencesMap != null && audiencesMap.get(audienceId) != null) { + audienceName.append("\"" + audiencesMap.get(audienceId) + "\""); + } else { + audienceName.append("\"" + audienceId + "\""); + } + return audienceName.toString(); } - private String getOperandOrAudienceId(Condition condition, Map audiencesMap){ - String operand = ""; + private String getOperandOrAudienceId(Condition condition, Map audiencesMap) { if (condition != null) { - if (condition instanceof AndCondition) { - operand = "AND"; - } else if (condition instanceof NotCondition) { - operand = "NOT"; - } else if (condition instanceof OrCondition) { - operand = "OR"; - } else if (condition instanceof AudienceIdCondition) { - String audienceName = this.getNameFromAudienceId(((AudienceIdCondition) condition).getAudienceId(), - audiencesMap); - operand = audienceName; + if (condition instanceof AudienceIdCondition) { + return this.getNameFromAudienceId(condition.getOperandOrId(), audiencesMap); + } else { + return condition.getOperandOrId(); } + } else { + return ""; } - return operand; } public String serialize(Condition condition, Map audiencesMap) { StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append(""); List conditions; String operand = this.getOperandOrAudienceId(condition, audiencesMap); switch (operand){ - case ("AND"): + case (AND): conditions = ((AndCondition) condition).getConditions(); stringBuilder.append(this.getNameOrNextCondition(operand, conditions, audiencesMap)); break; - case ("OR"): + case (OR): conditions = ((OrCondition) condition).getConditions(); stringBuilder.append(this.getNameOrNextCondition(operand, conditions, audiencesMap)); break; - case ("NOT"): + case (NOT): stringBuilder.append(operand + " "); Condition notCondition = ((NotCondition) condition).getCondition(); if (notCondition instanceof AudienceIdCondition) { @@ -236,15 +236,15 @@ public String serialize(Condition condition, Map audiencesMap) { public String getNameOrNextCondition(String operand, List conditions, Map audiencesMap) { StringBuilder stringBuilder = new StringBuilder(); - int ctr = 0; + int index = 0; if (conditions.isEmpty()) { return ""; } else if (conditions.size() == 1) { return serialize(conditions.get(0), audiencesMap); } else { for (Condition con : conditions) { - ctr++; - if (ctr + 1 <= conditions.size()) { + index++; + if (index + 1 <= conditions.size()) { if (con instanceof AudienceIdCondition) { String audienceName = this.getNameFromAudienceId(((AudienceIdCondition) con).getAudienceId(), audiencesMap); diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/AndCondition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/AndCondition.java index fbe6bb4f8..dca3cefc8 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/AndCondition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/AndCondition.java @@ -23,6 +23,7 @@ import javax.annotation.concurrent.Immutable; import java.util.List; import java.util.Map; +import java.util.StringJoiner; /** * Represents an 'And' conditions condition operation. @@ -30,6 +31,7 @@ public class AndCondition implements Condition { private final List conditions; + private static final String OPERAND = "AND"; public AndCondition(@Nonnull List conditions) { this.conditions = conditions; @@ -67,17 +69,18 @@ public Boolean evaluate(ProjectConfig config, Map attributes) { return true; // otherwise, return true } + @Override + public String getOperandOrId() { + return OPERAND; + } + @Override public String toJson() { - StringBuilder s = new StringBuilder(); - s.append("[\"and\", "); + StringJoiner s = new StringJoiner(", ", "[", "]"); + s.add("\"and\""); for (int i = 0; i < conditions.size(); i++) { - s.append(conditions.get(i).toJson()); - if (i < conditions.size() - 1) - s.append(", "); + s.add(conditions.get(i).toJson()); } - s.append("]"); - return s.toString(); } diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/AudienceIdCondition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/AudienceIdCondition.java index 7d173d3e5..e07757016 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/AudienceIdCondition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/AudienceIdCondition.java @@ -64,6 +64,11 @@ public String getAudienceId() { return audienceId; } + @Override + public String getOperandOrId() { + return audienceId; + } + @Nullable @Override public Boolean evaluate(ProjectConfig config, Map attributes) { diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/Condition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/Condition.java index 6e3872ad5..11b7165b9 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/Condition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/Condition.java @@ -30,4 +30,6 @@ public interface Condition { Boolean evaluate(ProjectConfig config, Map attributes); String toJson(); + + String getOperandOrId(); } diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/EmptyCondition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/EmptyCondition.java index 4cc57d6ef..9bb355a13 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/EmptyCondition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/EmptyCondition.java @@ -29,4 +29,9 @@ public Boolean evaluate(ProjectConfig config, Map attributes) { @Override public String toJson() { return null; } + + @Override + public String getOperandOrId() { + return null; + } } diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/NotCondition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/NotCondition.java index 1347be4fb..8c17b4ef2 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/NotCondition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/NotCondition.java @@ -23,6 +23,7 @@ import javax.annotation.Nonnull; import java.util.Map; +import java.util.StringJoiner; /** * Represents a 'Not' conditions condition operation. @@ -31,6 +32,7 @@ public class NotCondition implements Condition { private final Condition condition; + private static final String OPERAND = "NOT"; public NotCondition(@Nonnull Condition condition) { this.condition = condition; @@ -48,13 +50,15 @@ public Boolean evaluate(ProjectConfig config, Map attributes) { } @Override - public String toJson() { - StringBuilder s = new StringBuilder(); - - s.append("[\"not\", "); - s.append(condition.toJson()); - s.append("]"); + public String getOperandOrId() { + return OPERAND; + } + @Override + public String toJson() { + StringJoiner s = new StringJoiner(", ","[","]"); + s.add("\"not\""); + s.add(condition.toJson()); return s.toString(); } diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/NullCondition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/NullCondition.java index 99ee68275..10633aed9 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/NullCondition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/NullCondition.java @@ -29,4 +29,9 @@ public Boolean evaluate(ProjectConfig config, Map attributes) { @Override public String toJson() { return null; } + + @Override + public String getOperandOrId() { + return null; + } } diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/OrCondition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/OrCondition.java index 38e21cafb..71c8c9e76 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/OrCondition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/OrCondition.java @@ -23,6 +23,7 @@ import javax.annotation.concurrent.Immutable; import java.util.List; import java.util.Map; +import java.util.StringJoiner; /** * Represents an 'Or' conditions condition operation. @@ -30,6 +31,7 @@ @Immutable public class OrCondition implements Condition { private final List conditions; + private static final String OPERAND = "OR"; public OrCondition(@Nonnull List conditions) { this.conditions = conditions; @@ -66,17 +68,17 @@ public Boolean evaluate(ProjectConfig config, Map attributes) { } @Override - public String toJson() { - StringBuilder s = new StringBuilder(); + public String getOperandOrId() { + return OPERAND; + } - s.append("[\"or\", "); + @Override + public String toJson() { + StringJoiner s = new StringJoiner(", ", "[", "]"); + s.add("\"or\""); for (int i = 0; i < conditions.size(); i++) { - s.append(conditions.get(i).toJson()); - if (i < conditions.size() - 1) - s.append(", "); + s.add(conditions.get(i).toJson()); } - s.append("]"); - return s.toString(); } diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java b/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java index 814f9df10..ed029f89c 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java @@ -118,6 +118,11 @@ public Boolean evaluate(ProjectConfig config, Map attributes) { return null; } + @Override + public String getOperandOrId() { + return null; + } + public String getValueStr() { final String valueStr; if (value == null) { diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java index 15035472c..60aef9ce3 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigService.java @@ -110,7 +110,7 @@ String getExperimentFeatureKey(String experimentId) { @VisibleForTesting Map getExperimentsMap() { List experiments = projectConfig.getExperiments(); - + if (experiments == null) { return Collections.emptyMap(); } diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java index d2d2d5ad0..08b36cf3b 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java @@ -32,6 +32,12 @@ public class OptimizelyFeature implements IdKeyMapped { private List deliveryRules; private List experimentRules; + /** + * @Deprecated The experimentsMap has been deprecated.
+ * {Will be removed un the next version}
+ * Use experimentRules and deliveryRules instead. + * */ + @Deprecated() private Map experimentsMap; private Map variablesMap; From d5d53838f83ab29879605261d21ea4209cbd9fa4 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Thu, 12 Aug 2021 09:20:07 -0400 Subject: [PATCH 60/65] Spelling correction. --- .../com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java index 08b36cf3b..18d4be2a1 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java @@ -34,7 +34,7 @@ public class OptimizelyFeature implements IdKeyMapped { /** * @Deprecated The experimentsMap has been deprecated.
- * {Will be removed un the next version}
+ * {Will be removed in the upcoming version}
* Use experimentRules and deliveryRules instead. * */ @Deprecated() From 41e8e2bb910ccfdb1e05b20e051f7552afa4e8f2 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Thu, 12 Aug 2021 12:52:09 -0400 Subject: [PATCH 61/65] Add warning comment about OptimizelyConfig experimentsMap. --- .../optimizely/ab/optimizelyconfig/OptimizelyConfig.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java index f86366eff..4d9e5ce88 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java @@ -27,7 +27,7 @@ */ @JsonInclude(JsonInclude.Include.NON_NULL) public class OptimizelyConfig { - + private Map experimentsMap; private Map featuresMap; private List attributes; @@ -47,7 +47,13 @@ public OptimizelyConfig(Map experimentsMap, List events, List audiences, String datafile) { + + // This experimentsMap is for experiments of legacy projects only. + // For flag projects, experiment keys are not guaranteed to be unique + // across multiple flags, so this map may not include all experiments + // when keys conflict. this.experimentsMap = experimentsMap; + this.featuresMap = featuresMap; this.revision = revision; this.sdkKey = sdkKey == null ? "" : sdkKey; From a87793049a7cd552c2b4289159e12e9770e1f562 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Thu, 12 Aug 2021 12:54:08 -0400 Subject: [PATCH 62/65] Remove deprecated line saying when will be removed. --- .../com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java | 2 +- .../com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java index 4d9e5ce88..7fa890b66 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfig.java @@ -53,7 +53,7 @@ public OptimizelyConfig(Map experimentsMap, // across multiple flags, so this map may not include all experiments // when keys conflict. this.experimentsMap = experimentsMap; - + this.featuresMap = featuresMap; this.revision = revision; this.sdkKey = sdkKey == null ? "" : sdkKey; diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java index 18d4be2a1..1294c9e1f 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java @@ -34,7 +34,6 @@ public class OptimizelyFeature implements IdKeyMapped { /** * @Deprecated The experimentsMap has been deprecated.
- * {Will be removed in the upcoming version}
* Use experimentRules and deliveryRules instead. * */ @Deprecated() From f79f199583c538b51cd53be767ae58532f6fa4bf Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Thu, 12 Aug 2021 14:46:51 -0400 Subject: [PATCH 63/65] Update deprecated to follow suite of Android for compatibility. --- .../optimizely/ab/optimizelyconfig/OptimizelyFeature.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java index 1294c9e1f..240ea9475 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java @@ -32,11 +32,10 @@ public class OptimizelyFeature implements IdKeyMapped { private List deliveryRules; private List experimentRules; + @Deprecated /** - * @Deprecated The experimentsMap has been deprecated.
- * Use experimentRules and deliveryRules instead. - * */ - @Deprecated() + * @deprecated use {@link #experimentRules} and {@link #deliveryRules} instead + */ private Map experimentsMap; private Map variablesMap; From 04d1fb95b4be79f07a92b3385257876f033c7a79 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Thu, 12 Aug 2021 15:13:26 -0400 Subject: [PATCH 64/65] Update tests case to use proper order of assert. --- .../src/test/java/com/optimizely/ab/config/ExperimentTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-api/src/test/java/com/optimizely/ab/config/ExperimentTest.java b/core-api/src/test/java/com/optimizely/ab/config/ExperimentTest.java index 159944dbd..334e76067 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/ExperimentTest.java +++ b/core-api/src/test/java/com/optimizely/ab/config/ExperimentTest.java @@ -41,7 +41,7 @@ public void testStringifyConditionScenarios() { Experiment experiment = makeMockExperimentWithStatus(Experiment.ExperimentStatus.RUNNING, audienceConditionsScenarios.get(i)); String audiences = experiment.serializeConditions(audiencesMap); - assertEquals(audiences, expectedScenarioStringsMap.get(i+1)); + assertEquals(expectedScenarioStringsMap.get(i+1), audiences); } } From 89722b7975f4d70967f97446b06ee183a05361cd Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Thu, 12 Aug 2021 16:34:57 -0400 Subject: [PATCH 65/65] Update deprecated format. --- .../com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java index 240ea9475..1f8359e62 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelyconfig/OptimizelyFeature.java @@ -32,10 +32,10 @@ public class OptimizelyFeature implements IdKeyMapped { private List deliveryRules; private List experimentRules; - @Deprecated /** * @deprecated use {@link #experimentRules} and {@link #deliveryRules} instead */ + @Deprecated private Map experimentsMap; private Map variablesMap;