1
1
package com .eppo .sdk ;
2
2
3
3
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 ;
6
23
7
24
import lombok .extern .slf4j .Slf4j ;
8
25
9
- import com .eppo .sdk .exception .*;
10
26
import org .ehcache .Cache ;
11
27
12
28
import java .util .List ;
@@ -35,55 +51,67 @@ private EppoClient(ConfigurationStore configurationStore, Timer poller, EppoClie
35
51
* This function is used to get assignment Value
36
52
*
37
53
* @param subjectKey
38
- * @param experimentKey
54
+ * @param flagKey
39
55
* @param subjectAttributes
40
56
* @return
41
57
*/
42
58
public Optional <String > getAssignment (
43
59
String subjectKey ,
44
- String experimentKey ,
60
+ String flagKey ,
45
61
SubjectAttributes subjectAttributes
46
62
) {
47
63
// Validate Input Values
48
64
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" );
50
66
51
67
// Fetch Experiment Configuration
52
- ExperimentConfiguration configuration = this .configurationStore .getExperimentConfiguration (experimentKey );
68
+ ExperimentConfiguration configuration = this .configurationStore .getExperimentConfiguration (flagKey );
53
69
if (configuration == null ) {
54
- log .warn ("No configuration found for experiment key: " + experimentKey );
70
+ log .warn ("[Eppo SDK] No configuration found for key: " + flagKey );
55
71
return Optional .empty ();
56
72
}
57
73
58
74
// 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 ();
62
84
}
63
85
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" );
69
97
return Optional .empty ();
70
98
}
71
99
72
100
// Get assigned variation
73
- Variation assignedVariation = this .getAssignedVariation (subjectKey , experimentKey , configuration );
101
+ Variation assignedVariation = this .getAssignedVariation (subjectKey , flagKey , configuration . getSubjectShards (), allocation . getVariations () );
74
102
75
103
try {
76
104
this .eppoClientConfig .getAssignmentLogger ()
77
105
.logAssignment (new AssignmentLogData (
78
- experimentKey ,
79
- assignedVariation .name ,
106
+ flagKey ,
107
+ assignedVariation .getValue (). stringValue () ,
80
108
subjectKey ,
81
109
subjectAttributes
82
110
));
83
111
} catch (Exception e ){
84
112
// Ignore Exception
85
113
}
86
- return Optional .of (assignedVariation .name );
114
+ return Optional .of (assignedVariation .getValue (). stringValue () );
87
115
}
88
116
89
117
/**
@@ -108,11 +136,9 @@ public Optional<String> getAssignment(String subjectKey, String experimentKey) {
108
136
private boolean isInExperimentSample (
109
137
String subjectKey ,
110
138
String experimentKey ,
111
- ExperimentConfiguration experimentConfiguration
139
+ int subjectShards ,
140
+ float percentageExposure
112
141
) {
113
- int subjectShards = experimentConfiguration .subjectShards ;
114
- float percentageExposure = experimentConfiguration .percentExposure ;
115
-
116
142
int shard = Shard .getShard ("exposure-" + subjectKey + "-" + experimentKey , subjectShards );
117
143
return shard <= percentageExposure * subjectShards ;
118
144
}
@@ -128,13 +154,13 @@ private boolean isInExperimentSample(
128
154
private Variation getAssignedVariation (
129
155
String subjectKey ,
130
156
String experimentKey ,
131
- ExperimentConfiguration experimentConfiguration
157
+ int subjectShards ,
158
+ List <Variation > variations
132
159
) {
133
- int subjectShards = experimentConfiguration .subjectShards ;
134
160
int shard = Shard .getShard ("assignment-" + subjectKey + "-" + experimentKey , subjectShards );
135
161
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 () ))
138
164
.findFirst ();
139
165
140
166
return variation .get ();
@@ -147,30 +173,12 @@ private Variation getAssignedVariation(
147
173
* @param experimentConfiguration
148
174
* @return
149
175
*/
150
- private String getSubjectVariationOverride (
176
+ private EppoValue getSubjectVariationOverride (
151
177
String subjectKey ,
152
178
ExperimentConfiguration experimentConfiguration
153
179
) {
154
180
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 ());
174
182
}
175
183
176
184
/***
0 commit comments