1
1
/****************************************************************************
2
- * Copyright 2017-2018 , Optimizely, Inc. and contributors *
2
+ * Copyright 2017-2019 , Optimizely, Inc. and contributors *
3
3
* *
4
4
* Licensed under the Apache License, Version 2.0 (the "License"); *
5
5
* you may not use this file except in compliance with the License. *
@@ -55,9 +55,10 @@ public class DecisionService {
55
55
56
56
/**
57
57
* Initialize a decision service for the Optimizely client.
58
- * @param bucketer Base bucketer to allocate new users to an experiment.
59
- * @param errorHandler The error handler of the Optimizely client.
60
- * @param projectConfig Optimizely Project Config representing the datafile.
58
+ *
59
+ * @param bucketer Base bucketer to allocate new users to an experiment.
60
+ * @param errorHandler The error handler of the Optimizely client.
61
+ * @param projectConfig Optimizely Project Config representing the datafile.
61
62
* @param userProfileService UserProfileService implementation for storing user info.
62
63
*/
63
64
public DecisionService (@ Nonnull Bucketer bucketer ,
@@ -73,14 +74,15 @@ public DecisionService(@Nonnull Bucketer bucketer,
73
74
/**
74
75
* Get a {@link Variation} of an {@link Experiment} for a user to be allocated into.
75
76
*
76
- * @param experiment The Experiment the user will be bucketed into.
77
- * @param userId The userId of the user.
77
+ * @param experiment The Experiment the user will be bucketed into.
78
+ * @param userId The userId of the user.
78
79
* @param filteredAttributes The user's attributes. This should be filtered to just attributes in the Datafile.
79
80
* @return The {@link Variation} the user is allocated into.
80
81
*/
81
- public @ Nullable Variation getVariation (@ Nonnull Experiment experiment ,
82
- @ Nonnull String userId ,
83
- @ Nonnull Map <String , ?> filteredAttributes ) {
82
+ @ Nullable
83
+ public Variation getVariation (@ Nonnull Experiment experiment ,
84
+ @ Nonnull String userId ,
85
+ @ Nonnull Map <String , ?> filteredAttributes ) {
84
86
85
87
if (!ExperimentUtils .isExperimentActive (experiment )) {
86
88
return null ;
@@ -100,7 +102,7 @@ public DecisionService(@Nonnull Bucketer bucketer,
100
102
101
103
// fetch the user profile map from the user profile service
102
104
UserProfile userProfile = null ;
103
-
105
+
104
106
if (userProfileService != null ) {
105
107
try {
106
108
Map <String , Object > userProfileMap = userProfileService .lookup (userId );
@@ -149,21 +151,23 @@ public DecisionService(@Nonnull Bucketer bucketer,
149
151
150
152
/**
151
153
* Get the variation the user is bucketed into for the FeatureFlag
152
- * @param featureFlag The feature flag the user wants to access.
153
- * @param userId User Identifier
154
+ *
155
+ * @param featureFlag The feature flag the user wants to access.
156
+ * @param userId User Identifier
154
157
* @param filteredAttributes A map of filtered attributes.
155
158
* @return {@link FeatureDecision}
156
159
*/
157
- public @ Nonnull FeatureDecision getVariationForFeature (@ Nonnull FeatureFlag featureFlag ,
158
- @ Nonnull String userId ,
159
- @ Nonnull Map <String , ?> filteredAttributes ) {
160
+ @ Nonnull
161
+ public FeatureDecision getVariationForFeature (@ Nonnull FeatureFlag featureFlag ,
162
+ @ Nonnull String userId ,
163
+ @ Nonnull Map <String , ?> filteredAttributes ) {
160
164
if (!featureFlag .getExperimentIds ().isEmpty ()) {
161
165
for (String experimentId : featureFlag .getExperimentIds ()) {
162
166
Experiment experiment = projectConfig .getExperimentIdMapping ().get (experimentId );
163
167
Variation variation = this .getVariation (experiment , userId , filteredAttributes );
164
168
if (variation != null ) {
165
169
return new FeatureDecision (experiment , variation ,
166
- FeatureDecision .DecisionSource .EXPERIMENT );
170
+ FeatureDecision .DecisionSource .EXPERIMENT );
167
171
}
168
172
}
169
173
} else {
@@ -173,10 +177,10 @@ public DecisionService(@Nonnull Bucketer bucketer,
173
177
FeatureDecision featureDecision = getVariationForFeatureInRollout (featureFlag , userId , filteredAttributes );
174
178
if (featureDecision .variation == null ) {
175
179
logger .info ("The user \" {}\" was not bucketed into a rollout for feature flag \" {}\" ." ,
176
- userId , featureFlag .getKey ());
180
+ userId , featureFlag .getKey ());
177
181
} else {
178
182
logger .info ("The user \" {}\" was bucketed into a rollout for feature flag \" {}\" ." ,
179
- userId , featureFlag .getKey ());
183
+ userId , featureFlag .getKey ());
180
184
}
181
185
return featureDecision ;
182
186
}
@@ -185,14 +189,16 @@ public DecisionService(@Nonnull Bucketer bucketer,
185
189
* Try to bucket the user into a rollout rule.
186
190
* Evaluate the user for rules in priority order by seeing if the user satisfies the audience.
187
191
* Fall back onto the everyone else rule if the user is ever excluded from a rule due to traffic allocation.
188
- * @param featureFlag The feature flag the user wants to access.
189
- * @param userId User Identifier
192
+ *
193
+ * @param featureFlag The feature flag the user wants to access.
194
+ * @param userId User Identifier
190
195
* @param filteredAttributes A map of filtered attributes.
191
196
* @return {@link FeatureDecision}
192
197
*/
193
- @ Nonnull FeatureDecision getVariationForFeatureInRollout (@ Nonnull FeatureFlag featureFlag ,
194
- @ Nonnull String userId ,
195
- @ Nonnull Map <String , ?> filteredAttributes ) {
198
+ @ Nonnull
199
+ FeatureDecision getVariationForFeatureInRollout (@ Nonnull FeatureFlag featureFlag ,
200
+ @ Nonnull String userId ,
201
+ @ Nonnull Map <String , ?> filteredAttributes ) {
196
202
// use rollout to get variation for feature
197
203
if (featureFlag .getRolloutId ().isEmpty ()) {
198
204
logger .info ("The feature flag \" {}\" is not used in a rollout." , featureFlag .getKey ());
@@ -201,7 +207,7 @@ public DecisionService(@Nonnull Bucketer bucketer,
201
207
Rollout rollout = projectConfig .getRolloutIdMapping ().get (featureFlag .getRolloutId ());
202
208
if (rollout == null ) {
203
209
logger .error ("The rollout with id \" {}\" was not found in the datafile for feature flag \" {}\" ." ,
204
- featureFlag .getRolloutId (), featureFlag .getKey ());
210
+ featureFlag .getRolloutId (), featureFlag .getKey ());
205
211
return new FeatureDecision (null , null , null );
206
212
}
207
213
@@ -218,11 +224,10 @@ public DecisionService(@Nonnull Bucketer bucketer,
218
224
break ;
219
225
}
220
226
return new FeatureDecision (rolloutRule , variation ,
221
- FeatureDecision .DecisionSource .ROLLOUT );
222
- }
223
- else {
227
+ FeatureDecision .DecisionSource .ROLLOUT );
228
+ } else {
224
229
logger .debug ("User \" {}\" did not meet the conditions to be in rollout rule for audience \" {}\" ." ,
225
- userId , audience .getName ());
230
+ userId , audience .getName ());
226
231
}
227
232
}
228
233
@@ -232,20 +237,22 @@ public DecisionService(@Nonnull Bucketer bucketer,
232
237
variation = bucketer .bucket (finalRule , bucketingId );
233
238
if (variation != null ) {
234
239
return new FeatureDecision (finalRule , variation ,
235
- FeatureDecision .DecisionSource .ROLLOUT );
240
+ FeatureDecision .DecisionSource .ROLLOUT );
236
241
}
237
242
}
238
243
return new FeatureDecision (null , null , null );
239
244
}
240
245
241
246
/**
242
247
* Get the variation the user has been whitelisted into.
248
+ *
243
249
* @param experiment {@link Experiment} in which user is to be bucketed.
244
- * @param userId User Identifier
250
+ * @param userId User Identifier
245
251
* @return null if the user is not whitelisted into any variation
246
- * {@link Variation} the user is bucketed into if the user has a specified whitelisted variation.
252
+ * {@link Variation} the user is bucketed into if the user has a specified whitelisted variation.
247
253
*/
248
- @ Nullable Variation getWhitelistedVariation (@ Nonnull Experiment experiment , @ Nonnull String userId ) {
254
+ @ Nullable
255
+ Variation getWhitelistedVariation (@ Nonnull Experiment experiment , @ Nonnull String userId ) {
249
256
// if a user has a forced variation mapping, return the respective variation
250
257
Map <String , String > userIdToVariationKeyMap = experiment .getUserIdToVariationKeyMap ();
251
258
if (userIdToVariationKeyMap .containsKey (userId )) {
@@ -255,7 +262,7 @@ public DecisionService(@Nonnull Bucketer bucketer,
255
262
logger .info ("User \" {}\" is forced in variation \" {}\" ." , userId , forcedVariationKey );
256
263
} else {
257
264
logger .error ("Variation \" {}\" is not in the datafile. Not activating user \" {}\" ." ,
258
- forcedVariationKey , userId );
265
+ forcedVariationKey , userId );
259
266
}
260
267
return forcedVariation ;
261
268
}
@@ -264,13 +271,15 @@ public DecisionService(@Nonnull Bucketer bucketer,
264
271
265
272
/**
266
273
* Get the {@link Variation} that has been stored for the user in the {@link UserProfileService} implementation.
267
- * @param experiment {@link Experiment} in which the user was bucketed.
274
+ *
275
+ * @param experiment {@link Experiment} in which the user was bucketed.
268
276
* @param userProfile {@link UserProfile} of the user.
269
277
* @return null if the {@link UserProfileService} implementation is null or the user was not previously bucketed.
270
- * else return the {@link Variation} the user was previously bucketed into.
278
+ * else return the {@link Variation} the user was previously bucketed into.
271
279
*/
272
- @ Nullable Variation getStoredVariation (@ Nonnull Experiment experiment ,
273
- @ Nonnull UserProfile userProfile ) {
280
+ @ Nullable
281
+ Variation getStoredVariation (@ Nonnull Experiment experiment ,
282
+ @ Nonnull UserProfile userProfile ) {
274
283
// ---------- Check User Profile for Sticky Bucketing ----------
275
284
// If a user profile instance is present then check it for a saved variation
276
285
String experimentId = experiment .getId ();
@@ -279,35 +288,35 @@ public DecisionService(@Nonnull Bucketer bucketer,
279
288
if (decision != null ) {
280
289
String variationId = decision .variationId ;
281
290
Variation savedVariation = projectConfig
282
- .getExperimentIdMapping ()
283
- .get (experimentId )
284
- .getVariationIdToVariationMap ()
285
- .get (variationId );
291
+ .getExperimentIdMapping ()
292
+ .get (experimentId )
293
+ .getVariationIdToVariationMap ()
294
+ .get (variationId );
286
295
if (savedVariation != null ) {
287
296
logger .info ("Returning previously activated variation \" {}\" of experiment \" {}\" " +
288
- "for user \" {}\" from user profile." ,
289
- savedVariation .getKey (), experimentKey , userProfile .userId );
297
+ "for user \" {}\" from user profile." ,
298
+ savedVariation .getKey (), experimentKey , userProfile .userId );
290
299
// A variation is stored for this combined bucket id
291
300
return savedVariation ;
292
301
} else {
293
302
logger .info ("User \" {}\" was previously bucketed into variation with ID \" {}\" for experiment \" {}\" , " +
294
- "but no matching variation was found for that user. We will re-bucket the user." ,
295
- userProfile .userId , variationId , experimentKey );
303
+ "but no matching variation was found for that user. We will re-bucket the user." ,
304
+ userProfile .userId , variationId , experimentKey );
296
305
return null ;
297
306
}
298
307
} else {
299
308
logger .info ("No previously activated variation of experiment \" {}\" " +
300
- "for user \" {}\" found in user profile." ,
301
- experimentKey , userProfile .userId );
309
+ "for user \" {}\" found in user profile." ,
310
+ experimentKey , userProfile .userId );
302
311
return null ;
303
312
}
304
313
}
305
314
306
315
/**
307
316
* Save a {@link Variation} of an {@link Experiment} for a user in the {@link UserProfileService}.
308
317
*
309
- * @param experiment The experiment the user was buck
310
- * @param variation The Variation to save.
318
+ * @param experiment The experiment the user was buck
319
+ * @param variation The Variation to save.
311
320
* @param userProfile A {@link UserProfile} instance of the user information.
312
321
*/
313
322
void saveVariation (@ Nonnull Experiment experiment ,
@@ -332,18 +341,19 @@ void saveVariation(@Nonnull Experiment experiment,
332
341
variationId , experimentId , userProfile .userId );
333
342
} catch (Exception exception ) {
334
343
logger .warn ("Failed to save variation \" {}\" of experiment \" {}\" for user \" {}\" ." ,
335
- variationId , experimentId , userProfile .userId );
344
+ variationId , experimentId , userProfile .userId );
336
345
errorHandler .handleError (new OptimizelyRuntimeException (exception ));
337
346
}
338
347
}
339
348
}
340
349
341
350
/**
342
351
* Get the bucketingId of a user if a bucketingId exists in attributes, or else default to userId.
343
- * @param userId The userId of the user.
352
+ *
353
+ * @param userId The userId of the user.
344
354
* @param filteredAttributes The user's attributes. This should be filtered to just attributes in the Datafile.
345
355
* @return bucketingId if it is a String type in attributes.
346
- * else return userId
356
+ * else return userId
347
357
*/
348
358
String getBucketingId (@ Nonnull String userId ,
349
359
@ Nonnull Map <String , ?> filteredAttributes ) {
@@ -352,10 +362,9 @@ String getBucketingId(@Nonnull String userId,
352
362
if (String .class .isInstance (filteredAttributes .get (ControlAttribute .BUCKETING_ATTRIBUTE .toString ()))) {
353
363
bucketingId = (String ) filteredAttributes .get (ControlAttribute .BUCKETING_ATTRIBUTE .toString ());
354
364
logger .debug ("BucketingId is valid: \" {}\" " , bucketingId );
355
- }
356
- else {
365
+ } else {
357
366
logger .warn ("BucketingID attribute is not a string. Defaulted to userId" );
358
- }
367
+ }
359
368
}
360
369
return bucketingId ;
361
370
}
0 commit comments