Skip to content

Commit 05f922d

Browse files
[FSSDK-11520] parse holdout section and update project config (#572)
1 parent 746e815 commit 05f922d

26 files changed

+2664
-188
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,5 @@ classes
2525
.vagrant
2626
.DS_Store
2727
.venv
28+
29+
.vscode/mcp.json

build.gradle

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ plugins {
22
id 'com.github.kt3k.coveralls' version '2.12.2'
33
id 'jacoco'
44
id 'me.champeau.gradle.jmh' version '0.5.3'
5-
id 'nebula.optional-base' version '3.2.0'
5+
id 'nebula.optional-base' version '3.1.0'
66
id 'com.github.hierynomus.license' version '0.16.1'
77
id 'com.github.spotbugs' version "6.0.14"
88
id 'maven-publish'
@@ -116,6 +116,7 @@ configure(publishedProjects) {
116116
configurations.all {
117117
resolutionStrategy {
118118
force "junit:junit:${junitVersion}"
119+
force 'com.netflix.nebula:nebula-gradle-interop:2.2.2'
119120
}
120121
}
121122

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ public class DatafileProjectConfig implements ProjectConfig {
9595
// other mappings
9696
private final Map<String, Experiment> variationIdToExperimentMapping;
9797

98+
private final HoldoutConfig holdoutConfig;
99+
98100
private String datafile;
99101

100102
// v2 constructor
@@ -124,6 +126,7 @@ public DatafileProjectConfig(String accountId, String projectId, String version,
124126
eventType,
125127
experiments,
126128
null,
129+
null,
127130
groups,
128131
null,
129132
null
@@ -145,6 +148,7 @@ public DatafileProjectConfig(String accountId,
145148
List<Audience> typedAudiences,
146149
List<EventType> events,
147150
List<Experiment> experiments,
151+
List<Holdout> holdouts,
148152
List<FeatureFlag> featureFlags,
149153
List<Group> groups,
150154
List<Rollout> rollouts,
@@ -187,6 +191,12 @@ public DatafileProjectConfig(String accountId,
187191
allExperiments.addAll(aggregateGroupExperiments(groups));
188192
this.experiments = Collections.unmodifiableList(allExperiments);
189193

194+
if (holdouts == null) {
195+
this.holdoutConfig = new HoldoutConfig();
196+
} else {
197+
this.holdoutConfig = new HoldoutConfig(holdouts);
198+
}
199+
190200
String publicKeyForODP = "";
191201
String hostForODP = "";
192202
if (integrations == null) {
@@ -434,6 +444,21 @@ public List<Experiment> getExperiments() {
434444
return experiments;
435445
}
436446

447+
@Override
448+
public List<Holdout> getHoldouts() {
449+
return holdoutConfig.getAllHoldouts();
450+
}
451+
452+
@Override
453+
public List<Holdout> getHoldoutForFlag(@Nonnull String id) {
454+
return holdoutConfig.getHoldoutForFlag(id);
455+
}
456+
457+
@Override
458+
public Holdout getHoldout(@Nonnull String id) {
459+
return holdoutConfig.getHoldout(id);
460+
}
461+
437462
@Override
438463
public Set<String> getAllSegments() {
439464
return this.allSegments;

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

Lines changed: 1 addition & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,14 @@
3434
*/
3535
@Immutable
3636
@JsonIgnoreProperties(ignoreUnknown = true)
37-
public class Experiment implements IdKeyMapped {
37+
public class Experiment implements ExperimentCore {
3838

3939
private final String id;
4040
private final String key;
4141
private final String status;
4242
private final String layerId;
4343
private final String groupId;
4444

45-
private final String AND = "AND";
46-
private final String OR = "OR";
47-
private final String NOT = "NOT";
48-
4945
private final List<String> audienceIds;
5046
private final Condition<AudienceIdCondition> audienceConditions;
5147
private final List<Variation> variations;
@@ -176,98 +172,6 @@ public boolean isLaunched() {
176172
return status.equals(ExperimentStatus.LAUNCHED.toString());
177173
}
178174

179-
public String serializeConditions(Map<String, String> audiencesMap) {
180-
Condition condition = this.audienceConditions;
181-
return condition instanceof EmptyCondition ? "" : this.serialize(condition, audiencesMap);
182-
}
183-
184-
private String getNameFromAudienceId(String audienceId, Map<String, String> audiencesMap) {
185-
StringBuilder audienceName = new StringBuilder();
186-
if (audiencesMap != null && audiencesMap.get(audienceId) != null) {
187-
audienceName.append("\"" + audiencesMap.get(audienceId) + "\"");
188-
} else {
189-
audienceName.append("\"" + audienceId + "\"");
190-
}
191-
return audienceName.toString();
192-
}
193-
194-
private String getOperandOrAudienceId(Condition condition, Map<String, String> audiencesMap) {
195-
if (condition != null) {
196-
if (condition instanceof AudienceIdCondition) {
197-
return this.getNameFromAudienceId(condition.getOperandOrId(), audiencesMap);
198-
} else {
199-
return condition.getOperandOrId();
200-
}
201-
} else {
202-
return "";
203-
}
204-
}
205-
206-
public String serialize(Condition condition, Map<String, String> audiencesMap) {
207-
StringBuilder stringBuilder = new StringBuilder();
208-
List<Condition> conditions;
209-
210-
String operand = this.getOperandOrAudienceId(condition, audiencesMap);
211-
switch (operand){
212-
case (AND):
213-
conditions = ((AndCondition<?>) condition).getConditions();
214-
stringBuilder.append(this.getNameOrNextCondition(operand, conditions, audiencesMap));
215-
break;
216-
case (OR):
217-
conditions = ((OrCondition<?>) condition).getConditions();
218-
stringBuilder.append(this.getNameOrNextCondition(operand, conditions, audiencesMap));
219-
break;
220-
case (NOT):
221-
stringBuilder.append(operand + " ");
222-
Condition notCondition = ((NotCondition<?>) condition).getCondition();
223-
if (notCondition instanceof AudienceIdCondition) {
224-
stringBuilder.append(serialize(notCondition, audiencesMap));
225-
} else {
226-
stringBuilder.append("(" + serialize(notCondition, audiencesMap) + ")");
227-
}
228-
break;
229-
default:
230-
stringBuilder.append(operand);
231-
break;
232-
}
233-
234-
return stringBuilder.toString();
235-
}
236-
237-
public String getNameOrNextCondition(String operand, List<Condition> conditions, Map<String, String> audiencesMap) {
238-
StringBuilder stringBuilder = new StringBuilder();
239-
int index = 0;
240-
if (conditions.isEmpty()) {
241-
return "";
242-
} else if (conditions.size() == 1) {
243-
return serialize(conditions.get(0), audiencesMap);
244-
} else {
245-
for (Condition con : conditions) {
246-
index++;
247-
if (index + 1 <= conditions.size()) {
248-
if (con instanceof AudienceIdCondition) {
249-
String audienceName = this.getNameFromAudienceId(((AudienceIdCondition<?>) con).getAudienceId(),
250-
audiencesMap);
251-
stringBuilder.append( audienceName + " ");
252-
} else {
253-
stringBuilder.append("(" + serialize(con, audiencesMap) + ") ");
254-
}
255-
stringBuilder.append(operand);
256-
stringBuilder.append(" ");
257-
} else {
258-
if (con instanceof AudienceIdCondition) {
259-
String audienceName = this.getNameFromAudienceId(((AudienceIdCondition<?>) con).getAudienceId(),
260-
audiencesMap);
261-
stringBuilder.append(audienceName);
262-
} else {
263-
stringBuilder.append("(" + serialize(con, audiencesMap) + ")");
264-
}
265-
}
266-
}
267-
}
268-
return stringBuilder.toString();
269-
}
270-
271175
@Override
272176
public String toString() {
273177
return "Experiment{" +
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/**
2+
*
3+
* Copyright 2016-2019, 2021, Optimizely and contributors
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package com.optimizely.ab.config;
18+
19+
import com.optimizely.ab.config.audience.AndCondition;
20+
import com.optimizely.ab.config.audience.AudienceIdCondition;
21+
import com.optimizely.ab.config.audience.Condition;
22+
import com.optimizely.ab.config.audience.EmptyCondition;
23+
import com.optimizely.ab.config.audience.NotCondition;
24+
import com.optimizely.ab.config.audience.OrCondition;
25+
26+
import java.util.List;
27+
import java.util.Map;
28+
29+
public interface ExperimentCore extends IdKeyMapped {
30+
String AND = "AND";
31+
String OR = "OR";
32+
String NOT = "NOT";
33+
34+
String getLayerId();
35+
String getGroupId();
36+
List<String> getAudienceIds();
37+
Condition<AudienceIdCondition> getAudienceConditions();
38+
List<Variation> getVariations();
39+
List<TrafficAllocation> getTrafficAllocation();
40+
Map<String, Variation> getVariationKeyToVariationMap();
41+
Map<String, Variation> getVariationIdToVariationMap();
42+
43+
default String serializeConditions(Map<String, String> audiencesMap) {
44+
Condition condition = this.getAudienceConditions();
45+
return condition instanceof EmptyCondition ? "" : this.serialize(condition, audiencesMap);
46+
}
47+
48+
default String getNameFromAudienceId(String audienceId, Map<String, String> audiencesMap) {
49+
StringBuilder audienceName = new StringBuilder();
50+
if (audiencesMap != null && audiencesMap.get(audienceId) != null) {
51+
audienceName.append("\"" + audiencesMap.get(audienceId) + "\"");
52+
} else {
53+
audienceName.append("\"" + audienceId + "\"");
54+
}
55+
return audienceName.toString();
56+
}
57+
58+
default String getOperandOrAudienceId(Condition condition, Map<String, String> audiencesMap) {
59+
if (condition != null) {
60+
if (condition instanceof AudienceIdCondition) {
61+
return this.getNameFromAudienceId(condition.getOperandOrId(), audiencesMap);
62+
} else {
63+
return condition.getOperandOrId();
64+
}
65+
} else {
66+
return "";
67+
}
68+
}
69+
70+
default String serialize(Condition condition, Map<String, String> audiencesMap) {
71+
StringBuilder stringBuilder = new StringBuilder();
72+
List<Condition> conditions;
73+
74+
String operand = this.getOperandOrAudienceId(condition, audiencesMap);
75+
switch (operand){
76+
case (AND):
77+
conditions = ((AndCondition<?>) condition).getConditions();
78+
stringBuilder.append(this.getNameOrNextCondition(operand, conditions, audiencesMap));
79+
break;
80+
case (OR):
81+
conditions = ((OrCondition<?>) condition).getConditions();
82+
stringBuilder.append(this.getNameOrNextCondition(operand, conditions, audiencesMap));
83+
break;
84+
case (NOT):
85+
stringBuilder.append(operand + " ");
86+
Condition notCondition = ((NotCondition<?>) condition).getCondition();
87+
if (notCondition instanceof AudienceIdCondition) {
88+
stringBuilder.append(serialize(notCondition, audiencesMap));
89+
} else {
90+
stringBuilder.append("(" + serialize(notCondition, audiencesMap) + ")");
91+
}
92+
break;
93+
default:
94+
stringBuilder.append(operand);
95+
break;
96+
}
97+
98+
return stringBuilder.toString();
99+
}
100+
101+
default String getNameOrNextCondition(String operand, List<Condition> conditions, Map<String, String> audiencesMap) {
102+
StringBuilder stringBuilder = new StringBuilder();
103+
int index = 0;
104+
if (conditions.isEmpty()) {
105+
return "";
106+
} else if (conditions.size() == 1) {
107+
return serialize(conditions.get(0), audiencesMap);
108+
} else {
109+
for (Condition con : conditions) {
110+
index++;
111+
if (index + 1 <= conditions.size()) {
112+
if (con instanceof AudienceIdCondition) {
113+
String audienceName = this.getNameFromAudienceId(((AudienceIdCondition<?>) con).getAudienceId(),
114+
audiencesMap);
115+
stringBuilder.append( audienceName + " ");
116+
} else {
117+
stringBuilder.append("(" + serialize(con, audiencesMap) + ") ");
118+
}
119+
stringBuilder.append(operand);
120+
stringBuilder.append(" ");
121+
} else {
122+
if (con instanceof AudienceIdCondition) {
123+
String audienceName = this.getNameFromAudienceId(((AudienceIdCondition<?>) con).getAudienceId(),
124+
audiencesMap);
125+
stringBuilder.append(audienceName);
126+
} else {
127+
stringBuilder.append("(" + serialize(con, audiencesMap) + ")");
128+
}
129+
}
130+
}
131+
}
132+
return stringBuilder.toString();
133+
}
134+
}

0 commit comments

Comments
 (0)