Skip to content

Commit 65ba456

Browse files
Merge pull request #15 from optimizely/mnoman/closeApi
feat(close): Close API implemented for optimizelyClient
2 parents 4f978b6 + 1e1feeb commit 65ba456

15 files changed

+568
-92
lines changed

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

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,25 @@ protected void removeAllForcedDecisions(ArgumentsParser argumentsParser, @NonNul
262262
result.success(createResponse(false, ""));
263263
}
264264

265+
protected void close(ArgumentsParser argumentsParser, @NonNull Result result) {
266+
String sdkKey = argumentsParser.getSdkKey();
267+
if (sdkKey == null) {
268+
result.success(createResponse(false, ErrorMessage.INVALID_PARAMS));
269+
return;
270+
}
271+
OptimizelyClient optimizelyClient = getOptimizelyClient(sdkKey);
272+
if (optimizelyClient == null) {
273+
result.success(createResponse(false, ErrorMessage.OPTIMIZELY_CLIENT_NOT_FOUND));
274+
return;
275+
}
276+
optimizelyClient.close();
277+
278+
optimizelyManagerTracker.remove(sdkKey);
279+
userContextsTracker.remove(sdkKey);
280+
281+
result.success(createResponse(true, SuccessMessage.OPTIMIZELY_CLIENT_CLOSED));
282+
}
283+
265284
protected void trackEvent(ArgumentsParser argumentsParser, @NonNull Result result) {
266285
String sdkKey = argumentsParser.getSdkKey();
267286
if (sdkKey == null) {
@@ -467,7 +486,8 @@ private void invokeNotification(int id, String notificationType, Map notificatio
467486
Map<String, Object> listenerUnmodifiable = Collections.unmodifiableMap(listenerResponse);
468487
// Get a handler that can be used to post to the main thread
469488
Handler mainHandler = new Handler(context.getMainLooper());
470-
Runnable myRunnable = () -> OptimizelyFlutterSdkPlugin.channel.invokeMethod("callbackListener", listenerUnmodifiable);
489+
490+
Runnable myRunnable = () -> OptimizelyFlutterSdkPlugin.channel.invokeMethod(notificationType+"CallbackListener", listenerUnmodifiable);
471491
mainHandler.post(myRunnable);
472492
}
473493
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
9090
removeAllForcedDecisions(argumentsParser, result);
9191
break;
9292
}
93+
case APIs.CLOSE: {
94+
close(argumentsParser, result);
95+
break;
96+
}
9397
default:
9498
result.notImplemented();
9599
}

android/src/main/java/com/optimizely/optimizely_flutter_sdk/helper_classes/Constants.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
public class Constants {
1919
public static class APIs {
20+
public static final String CLOSE = "close";
2021
public static final String INITIALIZE = "initialize";
2122
public static final String GET_OPTIMIZELY_CONFIG = "getOptimizelyConfig";
2223
public static final String CREATE_USER_CONTEXT = "createUserContext";
@@ -64,6 +65,7 @@ public static class ErrorMessage {
6465
public static class SuccessMessage {
6566
public static final String INSTANCE_CREATED = "Optimizely instance created successfully.";
6667
public static final String OPTIMIZELY_CONFIG_FOUND = "Optimizely config found.";
68+
public static final String OPTIMIZELY_CLIENT_CLOSED = "Optimizely client closed successfully.";
6769
public static final String USER_CONTEXT_CREATED = "User context created successfully.";
6870
public static final String LISTENER_REMOVED = "Listener removed successfully.";
6971
public static final String DECIDE_CALLED = "Decide called successfully.";
@@ -77,16 +79,16 @@ public static class SuccessMessage {
7779

7880
public static class DecisionListenerKeys {
7981
public static final String TYPE = "type";
80-
public static final String USER_ID = "user_id";
82+
public static final String USER_ID = "userID";
8183
public static final String ATTRIBUTES = "attributes";
82-
public static final String DECISION_INFO = "decision_info";
84+
public static final String DECISION_INFO = "decisionInfo";
8385
}
8486

8587
public static class TrackListenerKeys {
86-
public static final String EVENT_KEY = "event_key";
87-
public static final String USER_ID = "user_id";
88+
public static final String EVENT_KEY = "eventKey";
89+
public static final String USER_ID = "userID";
8890
public static final String ATTRIBUTES = "attributes";
89-
public static final String EVENT_TAGS = "event_tags";
91+
public static final String EVENT_TAGS = "eventTags";
9092
}
9193

9294
public static class LogEventListenerKeys {

example/lib/main.dart

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import 'package:flutter/material.dart';
22
import 'dart:async';
33
import 'dart:convert';
44
import 'dart:math';
5-
65
import 'package:optimizely_flutter_sdk/optimizely_flutter_sdk.dart';
6+
import 'package:optimizely_flutter_sdk/src/data_objects/track_listener_response.dart';
77

88
void main() {
99
runApp(const MyApp());
@@ -50,6 +50,9 @@ class _MyAppState extends State<MyApp> {
5050
// To add decide listener
5151
var cancelDecideListener =
5252
await flutterSDK.addDecisionNotificationListener((notification) {
53+
print("Parsed decision event ....................");
54+
print(notification.type);
55+
print(notification.userID);
5356
print(notification);
5457
print("decide notification received");
5558
});
@@ -103,10 +106,21 @@ class _MyAppState extends State<MyApp> {
103106
// To add track listener
104107
var cancelTrackListener =
105108
await flutterSDK.addTrackNotificationListener((notification) {
106-
print(notification);
109+
print("Parsed track event ....................");
110+
print(notification.attributes);
111+
print(notification.eventKey);
107112
print("track notification received");
108113
});
109114

115+
// To add logEvent listener
116+
var cancelLogEventListener =
117+
await flutterSDK.addLogEventNotificationListener((notification) {
118+
print("Parsed log event ....................");
119+
print(notification.url);
120+
print(notification.params);
121+
print("log event notification received");
122+
});
123+
110124
// Track call
111125
response = await userContext.trackEvent("myevent", {
112126
"age": 20,

ios/Classes/HelperClasses/Constants.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ struct API {
2727
static let getForcedDecision = "getForcedDecision"
2828
static let removeForcedDecision = "removeForcedDecision"
2929
static let removeAllForcedDecisions = "removeAllForcedDecisions"
30+
static let close = "close"
3031
static let addNotificationListener = "addNotificationListener"
3132
static let removeNotificationListener = "removeNotificationListener"
3233
}
@@ -83,6 +84,7 @@ struct ErrorMessage {
8384
struct SuccessMessage {
8485
static let instanceCreated = "Optimizely instance created successfully."
8586
static let optimizelyConfigFound = "Optimizely config found."
87+
static let optimizelyClientClosed = "Optimizely client closed successfully."
8688
static let userContextCreated = "User context created successfully."
8789
static let attributesAdded = "Attributes added successfully."
8890
static let listenerAdded = "Listener added successfully."

ios/Classes/HelperClasses/Utils.swift

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public class Utils: NSObject {
6565
"url" : url,
6666
"params" : logEvent as Any
6767
]
68-
SwiftOptimizelyFlutterSdkPlugin.channel.invokeMethod("callbackListener", arguments: [RequestParameterKey.notificationId: id, RequestParameterKey.notificationType: NotificationType.logEvent, RequestParameterKey.notificationPayload: listenerDict])
68+
SwiftOptimizelyFlutterSdkPlugin.channel.invokeMethod("\(NotificationType.logEvent)CallbackListener", arguments: [RequestParameterKey.notificationId: id, RequestParameterKey.notificationType: NotificationType.logEvent, RequestParameterKey.notificationPayload: listenerDict])
6969
}
7070

7171
return listener
@@ -79,7 +79,7 @@ public class Utils: NSObject {
7979
if let datafileMap = try? JSONSerialization.jsonObject(with: datafile, options: []) as? [String: Any] {
8080
listenerDict["datafile"] = datafileMap
8181
}
82-
SwiftOptimizelyFlutterSdkPlugin.channel.invokeMethod("callbackListener", arguments: [RequestParameterKey.notificationId: id, RequestParameterKey.notificationType: NotificationType.projectConfigUpdate, RequestParameterKey.notificationPayload: listenerDict])
82+
SwiftOptimizelyFlutterSdkPlugin.channel.invokeMethod("\(NotificationType.projectConfigUpdate)CallbackListener", arguments: [RequestParameterKey.notificationId: id, RequestParameterKey.notificationType: NotificationType.projectConfigUpdate, RequestParameterKey.notificationPayload: listenerDict])
8383
}
8484

8585
return listener
@@ -89,12 +89,12 @@ public class Utils: NSObject {
8989
static func getDecisionCallback(id: Int) -> DecisionListener {
9090
let listener : DecisionListener = {(type, userId, attributes, decisionInfo) in
9191
let listenerDict : [String : Any] = [
92-
"type" : type,
93-
"user_id" : userId,
94-
"attributes" : attributes as Any,
95-
"decision_info": decisionInfo
92+
"type" : type,
93+
"userID" : userId,
94+
"attributes" : attributes as Any,
95+
"decisionInfo": decisionInfo
9696
]
97-
SwiftOptimizelyFlutterSdkPlugin.channel.invokeMethod("callbackListener", arguments: [RequestParameterKey.notificationId: id, RequestParameterKey.notificationType: NotificationType.decision, RequestParameterKey.notificationPayload: listenerDict])
97+
SwiftOptimizelyFlutterSdkPlugin.channel.invokeMethod("\(NotificationType.decision)CallbackListener", arguments: [RequestParameterKey.notificationId: id, RequestParameterKey.notificationType: NotificationType.decision, RequestParameterKey.notificationPayload: listenerDict])
9898
}
9999
return listener
100100
}
@@ -103,13 +103,13 @@ public class Utils: NSObject {
103103
static func getTrackCallback(id: Int) -> TrackListener {
104104
let listener : TrackListener = {(eventKey, userId, attributes, eventTags, event) in
105105
let listenerDict : [String : Any] = [
106-
"attributes": attributes as Any,
107-
"event_key" : eventKey,
108-
"event_tags" : eventTags as Any,
109-
"user_id" : userId,
106+
"attributes" : attributes as Any,
107+
"eventKey" : eventKey,
108+
"eventTags" : eventTags as Any,
109+
"userID" : userId,
110110
// "event": event as Any, This is causing codec related exceptions on flutter side, need to debug
111111
]
112-
SwiftOptimizelyFlutterSdkPlugin.channel.invokeMethod("callbackListener", arguments: [RequestParameterKey.notificationId: id, RequestParameterKey.notificationType: NotificationType.track, RequestParameterKey.notificationPayload: listenerDict])
112+
SwiftOptimizelyFlutterSdkPlugin.channel.invokeMethod("\(NotificationType.track)CallbackListener", arguments: [RequestParameterKey.notificationId: id, RequestParameterKey.notificationType: NotificationType.track, RequestParameterKey.notificationPayload: listenerDict])
113113
}
114114
return listener
115115
}

ios/Classes/SwiftOptimizelyFlutterSdkPlugin.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ public class SwiftOptimizelyFlutterSdkPlugin: NSObject, FlutterPlugin {
5454
case API.getForcedDecision: getForcedDecision(call, result: result)
5555
case API.removeForcedDecision: removeForcedDecision(call, result: result)
5656
case API.removeAllForcedDecisions: removeAllForcedDecisions(call, result: result)
57+
case API.close: close(call, result: result)
5758
default: result(FlutterMethodNotImplemented)
5859
}
5960
}
@@ -319,6 +320,25 @@ public class SwiftOptimizelyFlutterSdkPlugin: NSObject, FlutterPlugin {
319320
result(self.createResponse(success: false))
320321
}
321322

323+
/// Closes optimizely client after Flushing/batching all events
324+
func close(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
325+
326+
guard let parameters = call.arguments as? Dictionary<String, Any?>, let sdkKey = parameters[RequestParameterKey.sdkKey] as? String else {
327+
result(createResponse(success: false, reason: ErrorMessage.invalidParameters))
328+
return
329+
}
330+
331+
guard let optimizelyClient = getOptimizelyClient(arguments: call.arguments) else {
332+
result(self.createResponse(success: false, reason: ErrorMessage.optlyClientNotFound))
333+
return
334+
}
335+
336+
optimizelyClient.close()
337+
optimizelyClientsTracker.removeValue(forKey: sdkKey)
338+
userContextsTracker.removeValue(forKey: sdkKey)
339+
result(self.createResponse(success: true, reason: SuccessMessage.optimizelyClientClosed))
340+
}
341+
322342
/// Returns saved optimizely client
323343
func getOptimizelyClient(arguments: Any?) -> OptimizelyClient? {
324344
guard let parameters = arguments as? Dictionary<String, Any?>, let sdkKey = parameters[RequestParameterKey.sdkKey] as? String else {

lib/optimizely_flutter_sdk.dart

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ export 'package:optimizely_flutter_sdk/src/user_context/optimizely_user_context.
3030
show OptimizelyDecideOption;
3131
export 'package:optimizely_flutter_sdk/src/data_objects/decide_response.dart'
3232
show Decision;
33+
export 'package:optimizely_flutter_sdk/src/data_objects/track_listener_response.dart'
34+
show TrackListenerResponse;
35+
export 'package:optimizely_flutter_sdk/src/data_objects/decision_listener_response.dart'
36+
show DecisionListenerResponse;
37+
export 'package:optimizely_flutter_sdk/src/data_objects/logevent_listener_response.dart'
38+
show LogEventListenerResponse;
3339

3440
/// The main client class for the Optimizely Flutter SDK.
3541
///
@@ -59,31 +65,56 @@ class OptimizelyFlutterSdk {
5965
_sdkKey, userId, attributes);
6066
}
6167

68+
/// Checks if eventHandler are Closeable and calls close on them.
69+
Future<BaseResponse> close() async {
70+
return await OptimizelyClientWrapper.close(_sdkKey);
71+
}
72+
6273
Future<CancelListening> addDecisionNotificationListener(
63-
MultiUseCallback callback) async {
64-
return await _addNotificationListener(callback, ListenerType.decision);
74+
DecisionNotificationCallback callback) async {
75+
return await _addDecisionNotificationListener(callback);
6576
}
6677

6778
Future<CancelListening> addTrackNotificationListener(
68-
MultiUseCallback callback) async {
69-
return await _addNotificationListener(callback, ListenerType.track);
79+
TrackNotificationCallback callback) async {
80+
return await _addTrackNotificationListener(callback);
7081
}
7182

7283
Future<CancelListening> addUpdateConfigNotificationListener(
7384
MultiUseCallback callback) async {
74-
return await _addNotificationListener(
75-
callback, ListenerType.projectConfigUpdate);
85+
return await _addConfigUpdateNotificationListener(callback);
7686
}
7787

7888
Future<CancelListening> addLogEventNotificationListener(
79-
MultiUseCallback callback) async {
80-
return await _addNotificationListener(callback, ListenerType.logEvent);
89+
LogEventNotificationCallback callback) async {
90+
return await _addLogEventNotificationListener(callback);
91+
}
92+
93+
/// Allows user to listen to supported Decision notifications.
94+
Future<CancelListening> _addDecisionNotificationListener(
95+
DecisionNotificationCallback callback) async {
96+
return await OptimizelyClientWrapper.addDecisionNotificationListener(
97+
_sdkKey, callback);
98+
}
99+
100+
/// Allows user to listen to supported Track notifications.
101+
Future<CancelListening> _addTrackNotificationListener(
102+
TrackNotificationCallback callback) async {
103+
return await OptimizelyClientWrapper.addTrackNotificationListener(
104+
_sdkKey, callback);
81105
}
82106

83-
/// Allows user to listen to supported notifications.
84-
Future<CancelListening> _addNotificationListener(
85-
MultiUseCallback callback, ListenerType listenerType) async {
86-
return await OptimizelyClientWrapper.addNotificationListener(
87-
_sdkKey, callback, listenerType);
107+
/// Allows user to listen to supported LogEvent notifications.
108+
Future<CancelListening> _addLogEventNotificationListener(
109+
LogEventNotificationCallback callback) async {
110+
return await OptimizelyClientWrapper.addLogEventNotificationListener(
111+
_sdkKey, callback);
112+
}
113+
114+
/// Allows user to listen to supported Project Config Update notifications.
115+
Future<CancelListening> _addConfigUpdateNotificationListener(
116+
MultiUseCallback callback) async {
117+
return await OptimizelyClientWrapper.addConfigUpdateNotificationListener(
118+
_sdkKey, callback);
88119
}
89120
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/// **************************************************************************
2+
/// Copyright 2022, Optimizely, Inc. and contributors *
3+
/// *
4+
/// Licensed under the Apache License, Version 2.0 (the "License"); *
5+
/// you may not use this file except in compliance with the License. *
6+
/// You may obtain a copy of the License at *
7+
/// *
8+
/// http://www.apache.org/licenses/LICENSE-2.0 *
9+
/// *
10+
/// Unless required by applicable law or agreed to in writing, software *
11+
/// distributed under the License is distributed on an "AS IS" BASIS, *
12+
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
13+
/// See the License for the specific language governing permissions and *
14+
/// limitations under the License. *
15+
///**************************************************************************/
16+
17+
import 'package:optimizely_flutter_sdk/src/utils/constants.dart';
18+
19+
class DecisionListenerResponse {
20+
String type = '';
21+
String userID = '';
22+
Map<String, dynamic> attributes = {};
23+
Map<String, dynamic> decisionInfo = {};
24+
25+
DecisionListenerResponse(Map<String, dynamic> json) {
26+
if (json[Constants.type] is String) {
27+
type = json[Constants.type];
28+
}
29+
if (json[Constants.userID] is String) {
30+
userID = json[Constants.userID];
31+
}
32+
33+
if (json[Constants.attributes] is Map<dynamic, dynamic>) {
34+
attributes = Map<String, dynamic>.from(json[Constants.attributes]);
35+
}
36+
37+
if (json[Constants.decisionInfo] is Map<dynamic, dynamic>) {
38+
decisionInfo = Map<String, dynamic>.from(json[Constants.decisionInfo]);
39+
}
40+
}
41+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/// **************************************************************************
2+
/// Copyright 2022, Optimizely, Inc. and contributors *
3+
/// *
4+
/// Licensed under the Apache License, Version 2.0 (the "License"); *
5+
/// you may not use this file except in compliance with the License. *
6+
/// You may obtain a copy of the License at *
7+
/// *
8+
/// http://www.apache.org/licenses/LICENSE-2.0 *
9+
/// *
10+
/// Unless required by applicable law or agreed to in writing, software *
11+
/// distributed under the License is distributed on an "AS IS" BASIS, *
12+
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
13+
/// See the License for the specific language governing permissions and *
14+
/// limitations under the License. *
15+
///**************************************************************************/
16+
17+
import 'package:optimizely_flutter_sdk/src/utils/constants.dart';
18+
19+
class LogEventListenerResponse {
20+
String url = '';
21+
Map<String, dynamic> params = {};
22+
23+
LogEventListenerResponse(Map<String, dynamic> json) {
24+
if (json[Constants.url] is String) {
25+
url = json[Constants.url];
26+
}
27+
28+
if (json[Constants.params] is Map<dynamic, dynamic>) {
29+
params = Map<String, dynamic>.from(json[Constants.params]);
30+
}
31+
}
32+
}

0 commit comments

Comments
 (0)