Skip to content

Commit 94e89f1

Browse files
raju-opticlaude
andcommitted
[FSSDK-12275] Skip unsupported experiment type during flag decision
- Add type field to Experiment class with SUPPORTED_TYPES constant - Update all constructors and parsers (Jackson, Gson, JSON, JSONSimple) to parse type - Update DecisionService.getVariationFromExperiment to skip unsupported types - Experiments with null type (not set in datafile) are still evaluated - Add backward-compatible constructor for existing test code Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent f496660 commit 94e89f1

File tree

6 files changed

+58
-9
lines changed

6 files changed

+58
-9
lines changed

core-api/src/main/java/com/optimizely/ab/bucketing/DecisionService.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,17 @@ DecisionResponse<FeatureDecision> getVariationFromExperiment(@Nonnull ProjectCon
399399
for (String experimentId : featureFlag.getExperimentIds()) {
400400
Experiment experiment = projectConfig.getExperimentIdMapping().get(experimentId);
401401

402+
// Skip experiments with unsupported types.
403+
// If the experiment type is null (not set in datafile), we still evaluate it.
404+
// If the experiment type is set but not in the supported list, we skip it.
405+
if (experiment != null && experiment.getType() != null && !Experiment.SUPPORTED_TYPES.contains(experiment.getType())) {
406+
String skipMessage = reasons.addInfo(
407+
"Skipping experiment \"%s\" with unsupported type \"%s\" for feature \"%s\".",
408+
experiment.getKey(), experiment.getType(), featureFlag.getKey());
409+
logger.debug(skipMessage);
410+
continue;
411+
}
412+
402413
DecisionResponse<Variation> decisionVariation =
403414
getVariationFromExperimentRule(projectConfig, featureFlag.getKey(), experiment, user, options, userProfileTracker, decisionPath);
404415
reasons.merge(decisionVariation.getReasons());

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

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,12 @@
2323
import javax.annotation.Nonnull;
2424
import javax.annotation.Nullable;
2525
import javax.annotation.concurrent.Immutable;
26+
import java.util.Arrays;
2627
import java.util.Collections;
28+
import java.util.HashSet;
2729
import java.util.List;
2830
import java.util.Map;
31+
import java.util.Set;
2932

3033
/**
3134
* Represents the Optimizely Experiment configuration.
@@ -36,8 +39,18 @@
3639
@JsonIgnoreProperties(ignoreUnknown = true)
3740
public class Experiment implements ExperimentCore {
3841

42+
/**
43+
* The set of experiment types supported by this SDK.
44+
* Experiments with a type not in this set will be skipped during flag decisions.
45+
* If an experiment has no type (null), it is still evaluated.
46+
*/
47+
public static final Set<String> SUPPORTED_TYPES = Collections.unmodifiableSet(
48+
new HashSet<>(Arrays.asList("a/b", "mab", "cmab", "feature_rollouts"))
49+
);
50+
3951
private final String id;
4052
private final String key;
53+
private final String type;
4154
private final String status;
4255
private final String layerId;
4356
private final String groupId;
@@ -72,30 +85,40 @@ public String toString() {
7285

7386
@VisibleForTesting
7487
public Experiment(String id, String key, String layerId) {
75-
this(id, key, null, layerId, Collections.emptyList(), null, Collections.emptyList(), Collections.emptyMap(), Collections.emptyList(), "", null);
88+
this(id, key, null, null, layerId, Collections.emptyList(), null, Collections.emptyList(), Collections.emptyMap(), Collections.emptyList(), "", null);
7689
}
7790

7891
@VisibleForTesting
7992
public Experiment(String id, String key, String status, String layerId,
8093
List<String> audienceIds, Condition audienceConditions,
8194
List<Variation> variations, Map<String, String> userIdToVariationKeyMap,
8295
List<TrafficAllocation> trafficAllocation, String groupId) {
83-
this(id, key, status, layerId, audienceIds, audienceConditions, variations,
84-
userIdToVariationKeyMap, trafficAllocation, groupId, null); // Default cmab=null
96+
this(id, key, null, status, layerId, audienceIds, audienceConditions, variations,
97+
userIdToVariationKeyMap, trafficAllocation, groupId, null); // Default type=null, cmab=null
8598
}
8699

87100
@VisibleForTesting
88101
public Experiment(String id, String key, String status, String layerId,
89102
List<String> audienceIds, Condition audienceConditions,
90103
List<Variation> variations, Map<String, String> userIdToVariationKeyMap,
91104
List<TrafficAllocation> trafficAllocation) {
92-
this(id, key, status, layerId, audienceIds, audienceConditions, variations,
93-
userIdToVariationKeyMap, trafficAllocation, "", null); // Default groupId="" and cmab=null
105+
this(id, key, null, status, layerId, audienceIds, audienceConditions, variations,
106+
userIdToVariationKeyMap, trafficAllocation, "", null); // Default type=null, groupId="" and cmab=null
107+
}
108+
109+
@VisibleForTesting
110+
public Experiment(String id, String key, String status, String layerId,
111+
List<String> audienceIds, Condition audienceConditions,
112+
List<Variation> variations, Map<String, String> userIdToVariationKeyMap,
113+
List<TrafficAllocation> trafficAllocation, Cmab cmab) {
114+
this(id, key, null, status, layerId, audienceIds, audienceConditions, variations,
115+
userIdToVariationKeyMap, trafficAllocation, "", cmab); // Default type=null, groupId=""
94116
}
95117

96118
@JsonCreator
97119
public Experiment(@JsonProperty("id") String id,
98120
@JsonProperty("key") String key,
121+
@JsonProperty("type") String type,
99122
@JsonProperty("status") String status,
100123
@JsonProperty("layerId") String layerId,
101124
@JsonProperty("audienceIds") List<String> audienceIds,
@@ -104,11 +127,12 @@ public Experiment(@JsonProperty("id") String id,
104127
@JsonProperty("forcedVariations") Map<String, String> userIdToVariationKeyMap,
105128
@JsonProperty("trafficAllocation") List<TrafficAllocation> trafficAllocation,
106129
@JsonProperty("cmab") Cmab cmab) {
107-
this(id, key, status, layerId, audienceIds, audienceConditions, variations, userIdToVariationKeyMap, trafficAllocation, "", cmab);
130+
this(id, key, type, status, layerId, audienceIds, audienceConditions, variations, userIdToVariationKeyMap, trafficAllocation, "", cmab);
108131
}
109132

110133
public Experiment(@Nonnull String id,
111134
@Nonnull String key,
135+
@Nullable String type,
112136
@Nullable String status,
113137
@Nullable String layerId,
114138
@Nonnull List<String> audienceIds,
@@ -120,6 +144,7 @@ public Experiment(@Nonnull String id,
120144
@Nullable Cmab cmab) {
121145
this.id = id;
122146
this.key = key;
147+
this.type = type;
123148
this.status = status == null ? ExperimentStatus.NOT_STARTED.toString() : status;
124149
this.layerId = layerId;
125150
this.audienceIds = Collections.unmodifiableList(audienceIds);
@@ -141,6 +166,11 @@ public String getKey() {
141166
return key;
142167
}
143168

169+
@Nullable
170+
public String getType() {
171+
return type;
172+
}
173+
144174
public String getStatus() {
145175
return status;
146176
}
@@ -203,6 +233,7 @@ public String toString() {
203233
return "Experiment{" +
204234
"id='" + id + '\'' +
205235
", key='" + key + '\'' +
236+
", type='" + type + '\'' +
206237
", groupId='" + groupId + '\'' +
207238
", status='" + status + '\'' +
208239
", audienceIds=" + audienceIds +

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public Group(@JsonProperty("id") String id,
5555
experiment = new Experiment(
5656
experiment.getId(),
5757
experiment.getKey(),
58+
experiment.getType(),
5859
experiment.getStatus(),
5960
experiment.getLayerId(),
6061
experiment.getAudienceIds(),

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ static Cmab parseCmab(JsonObject cmabJson, JsonDeserializationContext context) {
137137
static Experiment parseExperiment(JsonObject experimentJson, String groupId, JsonDeserializationContext context) {
138138
String id = experimentJson.get("id").getAsString();
139139
String key = experimentJson.get("key").getAsString();
140+
String type = experimentJson.has("type") && !experimentJson.get("type").isJsonNull()
141+
? experimentJson.get("type").getAsString() : null;
140142
JsonElement experimentStatusJson = experimentJson.get("status");
141143
String status = experimentStatusJson.isJsonNull() ?
142144
ExperimentStatus.NOT_STARTED.toString() : experimentStatusJson.getAsString();
@@ -168,7 +170,7 @@ static Experiment parseExperiment(JsonObject experimentJson, String groupId, Jso
168170
}
169171
}
170172

171-
return new Experiment(id, key, status, layerId, audienceIds, conditions, variations, userIdToVariationKeyMap,
173+
return new Experiment(id, key, type, status, layerId, audienceIds, conditions, variations, userIdToVariationKeyMap,
172174
trafficAllocations, groupId, cmab);
173175
}
174176

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@ private List<Experiment> parseExperiments(JSONArray experimentJson, String group
148148
JSONObject experimentObject = (JSONObject) obj;
149149
String id = experimentObject.getString("id");
150150
String key = experimentObject.getString("key");
151+
String type = experimentObject.has("type") && !experimentObject.isNull("type")
152+
? experimentObject.getString("type") : null;
151153
String status = experimentObject.isNull("status") ?
152154
ExperimentStatus.NOT_STARTED.toString() : experimentObject.getString("status");
153155
String layerId = experimentObject.has("layerId") ? experimentObject.getString("layerId") : null;
@@ -179,7 +181,7 @@ private List<Experiment> parseExperiments(JSONArray experimentJson, String group
179181
cmab = parseCmab(cmabObject);
180182
}
181183

182-
experiments.add(new Experiment(id, key, status, layerId, audienceIds, conditions, variations, userIdToVariationKeyMap,
184+
experiments.add(new Experiment(id, key, type, status, layerId, audienceIds, conditions, variations, userIdToVariationKeyMap,
183185
trafficAllocations, groupId, cmab));
184186
}
185187

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ private List<Experiment> parseExperiments(JSONArray experimentJson, String group
150150
JSONObject experimentObject = (JSONObject) obj;
151151
String id = (String) experimentObject.get("id");
152152
String key = (String) experimentObject.get("key");
153+
Object typeObj = experimentObject.get("type");
154+
String type = typeObj != null ? (String) typeObj : null;
153155
Object statusJson = experimentObject.get("status");
154156
String status = statusJson == null ? ExperimentStatus.NOT_STARTED.toString() :
155157
(String) experimentObject.get("status");
@@ -189,7 +191,7 @@ private List<Experiment> parseExperiments(JSONArray experimentJson, String group
189191
}
190192
}
191193

192-
experiments.add(new Experiment(id, key, status, layerId, audienceIds, conditions, variations,
194+
experiments.add(new Experiment(id, key, type, status, layerId, audienceIds, conditions, variations,
193195
userIdToVariationKeyMap, trafficAllocations, groupId, cmab));
194196
}
195197

0 commit comments

Comments
 (0)