Skip to content

Commit cae9cdd

Browse files
Merge pull request #7 from optimizely/mnoman/forced-decision
feat(forced-decision): Forced decision API's implemented for user context Android only
2 parents 2219a62 + 17cfb51 commit cae9cdd

File tree

5 files changed

+384
-134
lines changed

5 files changed

+384
-134
lines changed

android/src/main/java/com/optimizely/optimizely_flutter_sdk/OptimizelyDecisionResponse.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public class OptimizelyDecisionResponse {
2929

3030
private final Map variables;
3131

32-
private final String ruleKey;
32+
private final String ruleKey;
3333

3434
private final String flagKey;
3535

android/src/main/java/com/optimizely/optimizely_flutter_sdk/OptimizelyFlutterClient.java

Lines changed: 259 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import io.flutter.plugin.common.MethodChannel.Result;
2121

2222
import com.optimizely.ab.OptimizelyUserContext;
23+
import com.optimizely.ab.OptimizelyDecisionContext;
24+
import com.optimizely.ab.OptimizelyForcedDecision;
2325
import com.optimizely.ab.android.sdk.OptimizelyClient;
2426

2527
import java.util.HashMap;
@@ -30,10 +32,17 @@
3032

3133
import com.fasterxml.jackson.databind.ObjectMapper;
3234
import com.optimizely.ab.android.sdk.OptimizelyManager;
35+
import com.optimizely.ab.event.LogEvent;
36+
import com.optimizely.ab.notification.DecisionNotification;
37+
import com.optimizely.ab.notification.TrackNotification;
38+
import com.optimizely.ab.notification.UpdateConfigNotification;
3339
import com.optimizely.ab.optimizelyconfig.OptimizelyConfig;
3440
import com.optimizely.ab.optimizelydecision.OptimizelyDecideOption;
3541
import com.optimizely.ab.optimizelydecision.OptimizelyDecision;
42+
import com.optimizely.optimizely_flutter_sdk.helper_classes.ArgumentsParser;
43+
3644
import static com.optimizely.optimizely_flutter_sdk.helper_classes.Constants.*;
45+
import static com.optimizely.optimizely_flutter_sdk.helper_classes.Utils.convertKeysCamelCaseToSnakeCase;
3746

3847
import java.util.Collections;
3948
import java.util.LinkedHashMap;
@@ -49,7 +58,12 @@ public class OptimizelyFlutterClient {
4958
protected static final Map<Integer, Integer> notificationIdsTracker = new HashMap<>();
5059

5160

52-
protected void initializeOptimizely(@NonNull String sdkKey, @NonNull Result result) {
61+
protected void initializeOptimizely(@NonNull ArgumentsParser argumentsParser, @NonNull Result result) {
62+
String sdkKey = argumentsParser.getSdkKey();
63+
if (sdkKey == null) {
64+
result.success(createResponse(false, ErrorMessage.INVALID_PARAMS));
65+
return;
66+
}
5367
// Delete old user context
5468
userContextsTracker.remove(sdkKey);
5569
// Creating new instance
@@ -68,12 +82,21 @@ protected void initializeOptimizely(@NonNull String sdkKey, @NonNull Result resu
6882
});
6983
}
7084

71-
protected void createUserContext(String sdkKey, String userId, Map<String, Object> attributes, @NonNull Result result) {
85+
protected void createUserContext(ArgumentsParser argumentsParser, @NonNull Result result) {
86+
String sdkKey = argumentsParser.getSdkKey();
87+
if (sdkKey == null) {
88+
result.success(createResponse(false, ErrorMessage.INVALID_PARAMS));
89+
return;
90+
}
91+
7292
OptimizelyClient optimizelyClient = getOptimizelyClient(sdkKey);
73-
if (optimizelyClient == null) {
93+
if (optimizelyClient == null) {
7494
result.success(createResponse(false, ErrorMessage.OPTIMIZELY_CLIENT_NOT_FOUND));
7595
return;
7696
}
97+
98+
String userId = argumentsParser.getUserID();
99+
Map<String, Object> attributes = argumentsParser.getAttributes();
77100
if (userId == null) {
78101
result.success(createResponse(false, ErrorMessage.INVALID_PARAMS));
79102
return;
@@ -96,12 +119,20 @@ protected void createUserContext(String sdkKey, String userId, Map<String, Objec
96119
}
97120
}
98121

99-
protected void decide(String sdkKey, List<String> decideKeys, List<OptimizelyDecideOption> decideOptions, @NonNull Result result) {
122+
protected void decide(ArgumentsParser argumentsParser, @NonNull Result result) {
123+
String sdkKey = argumentsParser.getSdkKey();
124+
if (sdkKey == null) {
125+
result.success(createResponse(false, ErrorMessage.INVALID_PARAMS));
126+
return;
127+
}
128+
100129
OptimizelyUserContext userContext = getUserContext(sdkKey);
101130
if (userContext == null) {
102131
result.success(createResponse(false, ErrorMessage.USER_CONTEXT_NOT_FOUND));
103132
return;
104133
}
134+
List<String> decideKeys = argumentsParser.getDecideKeys();
135+
List<OptimizelyDecideOption> decideOptions = argumentsParser.getDecideOptions();
105136

106137
Map<String, OptimizelyDecision> optimizelyDecisionsMap;
107138

@@ -123,8 +154,119 @@ protected void decide(String sdkKey, List<String> decideKeys, List<OptimizelyDec
123154
result.success(createResponse(true, s, ""));
124155
}
125156

126-
protected void trackEvent(String sdkKey, String eventKey, Map<String, Object> eventTags, @NonNull Result result) {
157+
protected void setForcedDecision(ArgumentsParser argumentsParser, @NonNull Result result) {
158+
String sdkKey = argumentsParser.getSdkKey();
159+
if (sdkKey == null) {
160+
result.success(createResponse(false, ErrorMessage.INVALID_PARAMS));
161+
return;
162+
}
163+
OptimizelyUserContext userContext = getUserContext(sdkKey);
164+
if (userContext == null) {
165+
result.success(createResponse(false, ErrorMessage.USER_CONTEXT_NOT_FOUND));
166+
return;
167+
}
168+
String flagKey = argumentsParser.getFlagKey();
169+
String ruleKey = argumentsParser.getRuleKey();
170+
String variationKey = argumentsParser.getVariationKey();
171+
172+
if (flagKey == null || variationKey == null) {
173+
result.success(createResponse(false, ErrorMessage.INVALID_PARAMS));
174+
return;
175+
}
176+
177+
OptimizelyDecisionContext optimizelyDecisionContext = new OptimizelyDecisionContext(flagKey, ruleKey);
178+
OptimizelyForcedDecision optimizelyForcedDecision = new OptimizelyForcedDecision(variationKey);
179+
if (userContext.setForcedDecision(optimizelyDecisionContext, optimizelyForcedDecision)) {
180+
result.success(createResponse(true, SuccessMessage.FORCED_DECISION_SET));
181+
}
182+
183+
result.success(createResponse(false, ""));
184+
}
185+
186+
protected void getForcedDecision(ArgumentsParser argumentsParser, @NonNull Result result) {
187+
String sdkKey = argumentsParser.getSdkKey();
188+
if (sdkKey == null) {
189+
result.success(createResponse(false, ErrorMessage.INVALID_PARAMS));
190+
return;
191+
}
127192
OptimizelyUserContext userContext = getUserContext(sdkKey);
193+
if (userContext == null) {
194+
result.success(createResponse(false, ErrorMessage.USER_CONTEXT_NOT_FOUND));
195+
return;
196+
}
197+
String flagKey = argumentsParser.getFlagKey();
198+
String ruleKey = argumentsParser.getRuleKey();
199+
if (flagKey == null) {
200+
result.success(createResponse(false, ErrorMessage.INVALID_PARAMS));
201+
return;
202+
}
203+
204+
OptimizelyDecisionContext optimizelyDecisionContext = new OptimizelyDecisionContext(flagKey, ruleKey);
205+
OptimizelyForcedDecision forcedDecision = userContext.getForcedDecision(optimizelyDecisionContext);
206+
if (forcedDecision != null) {
207+
result.success(createResponse(true, forcedDecision.getVariationKey(), ""));
208+
}
209+
210+
result.success(createResponse(false, ""));
211+
}
212+
213+
protected void removeForcedDecision(ArgumentsParser argumentsParser, @NonNull Result result) {
214+
String sdkKey = argumentsParser.getSdkKey();
215+
if (sdkKey == null) {
216+
result.success(createResponse(false, ErrorMessage.INVALID_PARAMS));
217+
return;
218+
}
219+
OptimizelyUserContext userContext = getUserContext(sdkKey);
220+
if (userContext == null) {
221+
result.success(createResponse(false, ErrorMessage.USER_CONTEXT_NOT_FOUND));
222+
return;
223+
}
224+
225+
String flagKey = argumentsParser.getFlagKey();
226+
String ruleKey = argumentsParser.getRuleKey();
227+
if (flagKey == null) {
228+
result.success(createResponse(false, ErrorMessage.INVALID_PARAMS));
229+
return;
230+
}
231+
232+
OptimizelyDecisionContext optimizelyDecisionContext = new OptimizelyDecisionContext(flagKey, ruleKey);
233+
if (userContext.removeForcedDecision(optimizelyDecisionContext)) {
234+
result.success(createResponse(true, SuccessMessage.REMOVED_FORCED_DECISION));
235+
}
236+
237+
result.success(createResponse(false, ""));
238+
}
239+
240+
protected void removeAllForcedDecisions(ArgumentsParser argumentsParser, @NonNull Result result) {
241+
String sdkKey = argumentsParser.getSdkKey();
242+
if (sdkKey == null) {
243+
result.success(createResponse(false, ErrorMessage.INVALID_PARAMS));
244+
return;
245+
}
246+
OptimizelyUserContext userContext = getUserContext(sdkKey);
247+
if (userContext == null) {
248+
result.success(createResponse(false, ErrorMessage.USER_CONTEXT_NOT_FOUND));
249+
return;
250+
}
251+
252+
if (userContext.removeAllForcedDecisions()) {
253+
result.success(createResponse(true, SuccessMessage.REMOVED_ALL_FORCED_DECISION));
254+
}
255+
256+
result.success(createResponse(false, ""));
257+
}
258+
259+
protected void trackEvent(ArgumentsParser argumentsParser, @NonNull Result result) {
260+
String sdkKey = argumentsParser.getSdkKey();
261+
if (sdkKey == null) {
262+
result.success(createResponse(false, ErrorMessage.INVALID_PARAMS));
263+
return;
264+
}
265+
OptimizelyUserContext userContext = getUserContext(sdkKey);
266+
267+
String eventKey = argumentsParser.getEventKey();
268+
Map<String, Object> eventTags = argumentsParser.getEventTags();
269+
128270
if (userContext == null) {
129271
result.success(createResponse(false, ErrorMessage.USER_CONTEXT_NOT_FOUND));
130272
return;
@@ -144,8 +286,15 @@ protected void trackEvent(String sdkKey, String eventKey, Map<String, Object> ev
144286
}
145287
}
146288

147-
protected void setAttribute(String sdkKey, Map<String, Object> attributes, @NonNull Result result) {
289+
protected void setAttribute(ArgumentsParser argumentsParser, @NonNull Result result) {
290+
String sdkKey = argumentsParser.getSdkKey();
291+
if (sdkKey == null) {
292+
result.success(createResponse(false, ErrorMessage.INVALID_PARAMS));
293+
return;
294+
}
148295
OptimizelyUserContext userContext = getUserContext(sdkKey);
296+
297+
Map<String, Object> attributes = argumentsParser.getAttributes();
149298
if (userContext == null) {
150299
result.success(createResponse(false, ErrorMessage.USER_CONTEXT_NOT_FOUND));
151300
return;
@@ -161,8 +310,18 @@ protected void setAttribute(String sdkKey, Map<String, Object> attributes, @NonN
161310
result.success(createResponse(true, userContext.getAttributes(), SuccessMessage.ATTRIBUTES_ADDED));
162311
}
163312

164-
protected void removeNotificationListener(String sdkKey, Integer id, String type, @NonNull Result result) {
313+
protected void removeNotificationListener(ArgumentsParser argumentsParser, @NonNull Result result) {
314+
String sdkKey = argumentsParser.getSdkKey();
315+
if (sdkKey == null) {
316+
result.success(createResponse(false, ErrorMessage.INVALID_PARAMS));
317+
return;
318+
}
319+
165320
OptimizelyClient optimizelyClient = getOptimizelyClient(sdkKey);
321+
322+
Integer id = argumentsParser.getNotificaitonID();
323+
String type = argumentsParser.getNotificationType();
324+
166325
if (optimizelyClient == null) {
167326
result.success(createResponse(false, ErrorMessage.OPTIMIZELY_CLIENT_NOT_FOUND));
168327
return;
@@ -176,7 +335,12 @@ protected void removeNotificationListener(String sdkKey, Integer id, String type
176335
result.success(createResponse(true, SuccessMessage.LISTENER_REMOVED));
177336
}
178337

179-
protected void getOptimizelyConfig(String sdkKey, @NonNull Result result) {
338+
protected void getOptimizelyConfig(ArgumentsParser argumentsParser, @NonNull Result result) {
339+
String sdkKey = argumentsParser.getSdkKey();
340+
if (sdkKey == null) {
341+
result.success(createResponse(false, ErrorMessage.INVALID_PARAMS));
342+
return;
343+
}
180344
OptimizelyClient optimizelyClient = getOptimizelyClient(sdkKey);
181345
if (optimizelyClient == null) {
182346
result.success(createResponse(false, ErrorMessage.OPTIMIZELY_CLIENT_NOT_FOUND));
@@ -193,9 +357,9 @@ protected void getOptimizelyConfig(String sdkKey, @NonNull Result result) {
193357

194358
public Map<String, ?> createResponse(Boolean success, Object result, String reason) {
195359
Map<String, Object> response = new HashMap<>();
196-
response.put("success", success);
197-
response.put("result", result);
198-
response.put("reason", reason);
360+
response.put(ResponseKey.SUCCESS, success);
361+
response.put(ResponseKey.RESULT, result);
362+
response.put(ResponseKey.REASON, reason);
199363

200364
return response;
201365
}
@@ -211,4 +375,88 @@ public OptimizelyClient getOptimizelyClient(String SDKKey) {
211375
public OptimizelyUserContext getUserContext(String SDKKey) {
212376
return userContextsTracker.get(SDKKey);
213377
}
378+
379+
protected void addNotificationListener(ArgumentsParser argumentsParser, @NonNull Result result) {
380+
String sdkKey = argumentsParser.getSdkKey();
381+
if (sdkKey == null) {
382+
result.success(createResponse(false, ErrorMessage.INVALID_PARAMS));
383+
return;
384+
}
385+
Integer id = argumentsParser.getNotificaitonID();
386+
String type = argumentsParser.getNotificationType();
387+
388+
OptimizelyClient optimizelyClient = getOptimizelyClient(sdkKey);
389+
if (optimizelyClient == null) {
390+
result.success(createResponse(false, ErrorMessage.OPTIMIZELY_CLIENT_NOT_FOUND));
391+
return;
392+
}
393+
394+
if (id == null || type == null) {
395+
result.success(createResponse(false, ErrorMessage.INVALID_PARAMS));
396+
return;
397+
}
398+
switch (type) {
399+
case NotificationType.DECISION: {
400+
int notificationId = optimizelyClient.getNotificationCenter().addNotificationHandler(DecisionNotification.class, decisionNotification -> {
401+
Map<String, Object> notificationMap = new HashMap<>();
402+
notificationMap.put(DecisionListenerKeys.TYPE, decisionNotification.getType());
403+
notificationMap.put(DecisionListenerKeys.USER_ID, decisionNotification.getUserId());
404+
notificationMap.put(DecisionListenerKeys.ATTRIBUTES, decisionNotification.getAttributes());
405+
notificationMap.put(DecisionListenerKeys.DECISION_INFO, convertKeysCamelCaseToSnakeCase(decisionNotification.getDecisionInfo()));
406+
invokeNotification(id, NotificationType.DECISION, notificationMap);
407+
});
408+
notificationIdsTracker.put(id, notificationId);
409+
result.success(createResponse(true, SuccessMessage.LISTENER_ADDED));
410+
break;
411+
}
412+
case NotificationType.TRACK: {
413+
int notificationId = optimizelyClient.getNotificationCenter().addNotificationHandler(TrackNotification.class, trackNotification -> {
414+
Map<String, Object> notificationMap = new HashMap<>();
415+
notificationMap.put(TrackListenerKeys.EVENT_KEY, trackNotification.getEventKey());
416+
notificationMap.put(TrackListenerKeys.USER_ID, trackNotification.getUserId());
417+
notificationMap.put(TrackListenerKeys.ATTRIBUTES, trackNotification.getAttributes());
418+
notificationMap.put(TrackListenerKeys.EVENT_TAGS, trackNotification.getEventTags());
419+
invokeNotification(id, NotificationType.TRACK, notificationMap);
420+
});
421+
notificationIdsTracker.put(id, notificationId);
422+
result.success(createResponse(true, SuccessMessage.LISTENER_ADDED));
423+
break;
424+
}
425+
case NotificationType.LOG_EVENT: {
426+
int notificationId = optimizelyClient.getNotificationCenter().addNotificationHandler(LogEvent.class, logEvent -> {
427+
ObjectMapper mapper = new ObjectMapper();
428+
Map<String, Object> eventParams = mapper.readValue(logEvent.getBody(), Map.class);
429+
Map<String, Object> listenerMap = new HashMap<>();
430+
listenerMap.put(LogEventListenerKeys.URL, logEvent.getEndpointUrl());
431+
listenerMap.put(LogEventListenerKeys.HTTP_VERB, logEvent.getRequestMethod());
432+
listenerMap.put(LogEventListenerKeys.PARAMS, eventParams);
433+
invokeNotification(id, NotificationType.LOG_EVENT, listenerMap);
434+
});
435+
notificationIdsTracker.put(id, notificationId);
436+
result.success(createResponse(true, SuccessMessage.LISTENER_ADDED));
437+
break;
438+
}
439+
case NotificationType.CONFIG_UPDATE: {
440+
int notificationId = optimizelyClient.getNotificationCenter().addNotificationHandler(UpdateConfigNotification.class, configUpdate -> {
441+
Map<String, Object> listenerMap = new HashMap<>();
442+
listenerMap.put("Config-update", Collections.emptyMap());
443+
invokeNotification(id, NotificationType.CONFIG_UPDATE, listenerMap);
444+
});
445+
notificationIdsTracker.put(id, notificationId);
446+
result.success(createResponse(true, SuccessMessage.LISTENER_ADDED));
447+
break;
448+
}
449+
default:
450+
result.success(createResponse(false, ErrorMessage.INVALID_PARAMS));
451+
}
452+
}
453+
454+
private void invokeNotification(int id, String notificationType, Map notificationMap) {
455+
Map<String, Object> listenerResponse = new HashMap<>();
456+
listenerResponse.put(RequestParameterKey.NOTIFICATION_ID, id);
457+
listenerResponse.put(RequestParameterKey.NOTIFICATION_TYPE, notificationType);
458+
listenerResponse.put(RequestParameterKey.NOTIFICATION_PAYLOAD, notificationMap);
459+
Map<String, Object> listenerUnmodifiable = Collections.unmodifiableMap(listenerResponse);
460+
OptimizelyFlutterSdkPlugin.channel.invokeMethod("callbackListener", listenerUnmodifiable);
461+
}
214462
}

0 commit comments

Comments
 (0)