Skip to content

Commit ca0083b

Browse files
authored
Migrate Java SDK to use V2 Randomization Endpoint (#8)
* Migrate SDK to RAC version 2 * bump version of pom * don't log subject attributes * remove matchesAnyRule function
1 parent 18690f1 commit ca0083b

16 files changed

+124
-117
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
target/
33
out/
44
src/test/resources/assignment-v2
5-
src/test/resources/rac-experiments.json
5+
src/test/resources/rac-experiments-v2.json

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ testDataDir := src/test/resources/
3434
test-data:
3535
rm -rf $(testDataDir)
3636
mkdir -p $(testDataDir)
37-
gsutil cp gs://sdk-test-data/rac-experiments.json $(testDataDir)
37+
gsutil cp gs://sdk-test-data/rac-experiments-v2.json $(testDataDir)
3838
gsutil cp -r gs://sdk-test-data/assignment-v2 $(testDataDir)
3939

4040
.PHONY: test

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>cloud.eppo</groupId>
88
<artifactId>eppo-server-sdk</artifactId>
9-
<version>1.0.2</version>
9+
<version>1.1.0</version>
1010

1111
<name>${project.groupId}:${project.artifactId}</name>
1212
<description>Eppo Server-Side SDK for Java</description>

src/main/java/com/eppo/sdk/EppoClient.java

Lines changed: 56 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,28 @@
11
package com.eppo.sdk;
22

33
import com.eppo.sdk.constants.Constants;
4-
import com.eppo.sdk.dto.*;
5-
import com.eppo.sdk.helpers.*;
4+
import com.eppo.sdk.dto.Allocation;
5+
import com.eppo.sdk.dto.AssignmentLogData;
6+
import com.eppo.sdk.dto.EppoClientConfig;
7+
import com.eppo.sdk.dto.EppoValue;
8+
import com.eppo.sdk.dto.ExperimentConfiguration;
9+
import com.eppo.sdk.dto.Rule;
10+
import com.eppo.sdk.dto.SubjectAttributes;
11+
import com.eppo.sdk.dto.Variation;
12+
import com.eppo.sdk.exception.EppoClientIsNotInitializedException;
13+
import com.eppo.sdk.exception.InvalidInputException;
14+
import com.eppo.sdk.helpers.AppDetails;
15+
import com.eppo.sdk.helpers.CacheHelper;
16+
import com.eppo.sdk.helpers.ConfigurationStore;
17+
import com.eppo.sdk.helpers.EppoHttpClient;
18+
import com.eppo.sdk.helpers.ExperimentConfigurationRequestor;
19+
import com.eppo.sdk.helpers.FetchConfigurationsTask;
20+
import com.eppo.sdk.helpers.InputValidator;
21+
import com.eppo.sdk.helpers.RuleValidator;
22+
import com.eppo.sdk.helpers.Shard;
623

724
import lombok.extern.slf4j.Slf4j;
825

9-
import com.eppo.sdk.exception.*;
1026
import org.ehcache.Cache;
1127

1228
import java.util.List;
@@ -35,55 +51,67 @@ private EppoClient(ConfigurationStore configurationStore, Timer poller, EppoClie
3551
* This function is used to get assignment Value
3652
*
3753
* @param subjectKey
38-
* @param experimentKey
54+
* @param flagKey
3955
* @param subjectAttributes
4056
* @return
4157
*/
4258
public Optional<String> getAssignment(
4359
String subjectKey,
44-
String experimentKey,
60+
String flagKey,
4561
SubjectAttributes subjectAttributes
4662
) {
4763
// Validate Input Values
4864
InputValidator.validateNotBlank(subjectKey, "Invalid argument: subjectKey cannot be blank");
49-
InputValidator.validateNotBlank(experimentKey, "Invalid argument: experimentKey cannot be blank");
65+
InputValidator.validateNotBlank(flagKey, "Invalid argument: flagKey cannot be blank");
5066

5167
// Fetch Experiment Configuration
52-
ExperimentConfiguration configuration = this.configurationStore.getExperimentConfiguration(experimentKey);
68+
ExperimentConfiguration configuration = this.configurationStore.getExperimentConfiguration(flagKey);
5369
if (configuration == null) {
54-
log.warn("No configuration found for experiment key: " + experimentKey);
70+
log.warn("[Eppo SDK] No configuration found for key: " + flagKey);
5571
return Optional.empty();
5672
}
5773

5874
// Check if subject has override variations
59-
String subjectVariationOverride = this.getSubjectVariationOverride(subjectKey, configuration);
60-
if (subjectVariationOverride != null) {
61-
return Optional.of(subjectVariationOverride);
75+
EppoValue subjectVariationOverride = this.getSubjectVariationOverride(subjectKey, configuration);
76+
if (!subjectVariationOverride.isNull()) {
77+
return Optional.of(subjectVariationOverride.stringValue());
78+
}
79+
80+
// Check if disabled
81+
if (!configuration.isEnabled()) {
82+
log.info("[Eppo SDK] No assigned variation because the experiment or feature flag {} is disabled", flagKey);
83+
return Optional.empty();
6284
}
6385

64-
// If disabled or not in Experiment Sampler or Rules not satisfied return empty string
65-
if (!configuration.enabled ||
66-
!this.isInExperimentSample(subjectKey, experimentKey, configuration) ||
67-
!this.subjectAttributesSatisfyRules(subjectAttributes, configuration.rules)
68-
) {
86+
// Find matched rule
87+
Optional<Rule> rule = RuleValidator.findMatchingRule(subjectAttributes, configuration.getRules());
88+
if (rule.isEmpty()) {
89+
log.info("[Eppo SDK] No assigned variation. The subject attributes did not match any targeting rules");
90+
return Optional.empty();
91+
}
92+
93+
// Check if in experiment sample
94+
Allocation allocation = configuration.getAllocation(rule.get().getAllocationKey());
95+
if (!this.isInExperimentSample(subjectKey, flagKey, configuration.getSubjectShards(), allocation.getPercentExposure())) {
96+
log.info("[Eppo SDK] No assigned variation. The subject is not part of the sample population");
6997
return Optional.empty();
7098
}
7199

72100
// Get assigned variation
73-
Variation assignedVariation = this.getAssignedVariation(subjectKey, experimentKey, configuration);
101+
Variation assignedVariation = this.getAssignedVariation(subjectKey, flagKey, configuration.getSubjectShards(), allocation.getVariations());
74102

75103
try {
76104
this.eppoClientConfig.getAssignmentLogger()
77105
.logAssignment(new AssignmentLogData(
78-
experimentKey,
79-
assignedVariation.name,
106+
flagKey,
107+
assignedVariation.getValue().stringValue(),
80108
subjectKey,
81109
subjectAttributes
82110
));
83111
} catch (Exception e){
84112
// Ignore Exception
85113
}
86-
return Optional.of(assignedVariation.name);
114+
return Optional.of(assignedVariation.getValue().stringValue());
87115
}
88116

89117
/**
@@ -108,11 +136,9 @@ public Optional<String> getAssignment(String subjectKey, String experimentKey) {
108136
private boolean isInExperimentSample(
109137
String subjectKey,
110138
String experimentKey,
111-
ExperimentConfiguration experimentConfiguration
139+
int subjectShards,
140+
float percentageExposure
112141
) {
113-
int subjectShards = experimentConfiguration.subjectShards;
114-
float percentageExposure = experimentConfiguration.percentExposure;
115-
116142
int shard = Shard.getShard("exposure-" + subjectKey + "-" + experimentKey, subjectShards);
117143
return shard <= percentageExposure * subjectShards;
118144
}
@@ -128,13 +154,13 @@ private boolean isInExperimentSample(
128154
private Variation getAssignedVariation(
129155
String subjectKey,
130156
String experimentKey,
131-
ExperimentConfiguration experimentConfiguration
157+
int subjectShards,
158+
List<Variation> variations
132159
) {
133-
int subjectShards = experimentConfiguration.subjectShards;
134160
int shard = Shard.getShard("assignment-" + subjectKey + "-" + experimentKey, subjectShards);
135161

136-
Optional<Variation> variation = experimentConfiguration.variations.stream()
137-
.filter(config -> Shard.isShardInRange(shard, config.shardRange))
162+
Optional<Variation> variation = variations.stream()
163+
.filter(config -> Shard.isShardInRange(shard, config.getShardRange()))
138164
.findFirst();
139165

140166
return variation.get();
@@ -147,30 +173,12 @@ private Variation getAssignedVariation(
147173
* @param experimentConfiguration
148174
* @return
149175
*/
150-
private String getSubjectVariationOverride(
176+
private EppoValue getSubjectVariationOverride(
151177
String subjectKey,
152178
ExperimentConfiguration experimentConfiguration
153179
) {
154180
String hexedSubjectKey = Shard.getHex(subjectKey);
155-
return experimentConfiguration.overrides.getOrDefault(hexedSubjectKey, null);
156-
}
157-
158-
/**
159-
* This function is used to test if subject attributes are satisfying rules or not
160-
*
161-
* @param subjectAttributes
162-
* @param rules
163-
* @return
164-
* @throws Exception
165-
*/
166-
private boolean subjectAttributesSatisfyRules(
167-
SubjectAttributes subjectAttributes,
168-
List<Rule> rules
169-
) {
170-
if (rules.size() == 0) {
171-
return true;
172-
}
173-
return RuleValidator.matchesAnyRule(subjectAttributes, rules);
181+
return experimentConfiguration.getOverrides().getOrDefault(hexedSubjectKey, new EppoValue());
174182
}
175183

176184
/***

src/main/java/com/eppo/sdk/constants/Constants.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public class Constants {
3131
/**
3232
* RAC settings
3333
*/
34-
public static final String RAC_ENDPOINT = "/randomized_assignment/config";
34+
public static final String RAC_ENDPOINT = "/randomized_assignment/v2/config";
3535

3636

3737
/**
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.eppo.sdk.dto;
2+
3+
import java.util.List;
4+
5+
import lombok.Data;
6+
7+
@Data
8+
public class Allocation {
9+
private float percentExposure;
10+
private List<Variation> variations;
11+
}

src/main/java/com/eppo/sdk/dto/ExperimentConfiguration.java

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,21 @@
33
import java.util.List;
44
import java.util.Map;
55

6+
import lombok.Data;
7+
68
/**
79
* Experiment Configuration Class
810
*/
11+
@Data
912
public class ExperimentConfiguration {
10-
public String name;
11-
public boolean enabled;
12-
public int subjectShards;
13-
public float percentExposure;
14-
public List<Variation> variations;
15-
public Map<String, String> overrides;
16-
public List<Rule> rules;
13+
private String name;
14+
private boolean enabled;
15+
private int subjectShards;
16+
private Map<String, EppoValue> overrides;
17+
private Map<String, Allocation> allocations;
18+
private List<Rule> rules;
1719

18-
@Override
19-
public String toString() {
20-
return "[Name: " + name + " | Enabled: " + enabled + " | SubjectShards: " +
21-
subjectShards + " | PercentExposure: " + percentExposure + " | Variations: " +
22-
variations.toString() + " | Overrides: " + overrides.toString() + " | Rules: " + rules.toString() + "]";
20+
public Allocation getAllocation(String allocationKey) {
21+
return getAllocations().get(allocationKey);
2322
}
2423
}

src/main/java/com/eppo/sdk/dto/ExperimentConfigurationResponse.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,12 @@
22

33
import java.util.Map;
44

5+
import lombok.Data;
6+
57
/**
68
* Experiment Configuration Response Class
79
*/
10+
@Data
811
public class ExperimentConfigurationResponse {
9-
public Map<String, ExperimentConfiguration> experiments;
10-
11-
@Override
12-
public String toString() {
13-
return "[Experiments: " + experiments.toString() + "]";
14-
}
12+
private Map<String, ExperimentConfiguration> flags;
1513
}

src/main/java/com/eppo/sdk/dto/Rule.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@
22

33
import java.util.List;
44

5+
import lombok.Data;
6+
57
/**
68
* Rule Class
79
*/
10+
@Data
811
public class Rule {
9-
public List<Condition> conditions;
10-
11-
@Override
12-
public String toString() {
13-
return "[Conditions: " + conditions + "]";
14-
}
12+
private String allocationKey;
13+
private List<Condition> conditions;
1514
}
Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
package com.eppo.sdk.dto;
22

3+
import lombok.Data;
4+
35
/**
46
* Experiment's Variation Class
57
*/
8+
@Data
69
public class Variation {
7-
public String name;
8-
public ShardRange shardRange;
9-
10-
@Override
11-
public String toString() {
12-
return "[Name: " + name + "| ShareRange: " + shardRange.toString() + "]";
13-
}
10+
private EppoValue value;
11+
private ShardRange shardRange;
1412
}

0 commit comments

Comments
 (0)