From 09cda0b2bd8cfc00b3c9287110d02701d24fb464 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Wed, 31 Aug 2022 11:47:05 -0300 Subject: [PATCH 01/17] Extract MethodCall from domain classes --- splitio/lib/channel/method_channel_manager.dart | 2 +- .../lib/events/split_method_call_handler.dart | 17 ++++++++--------- .../impressions_method_call_handler.dart | 7 +++---- splitio/lib/method_call_handler.dart | 4 +--- .../impressions_method_call_handler_test.dart | 9 ++++++--- splitio/test/method_channel_manager_test.dart | 4 ++-- splitio/test/split_event_listener_test.dart | 3 ++- 7 files changed, 23 insertions(+), 23 deletions(-) diff --git a/splitio/lib/channel/method_channel_manager.dart b/splitio/lib/channel/method_channel_manager.dart index 89ec677..34eab30 100644 --- a/splitio/lib/channel/method_channel_manager.dart +++ b/splitio/lib/channel/method_channel_manager.dart @@ -20,7 +20,7 @@ class MethodChannelManager { Future handle(MethodCall call) async { for (MethodCallHandler handler in _handlers) { - handler.handle(call); + handler.handle(call.method, call.arguments); } } diff --git a/splitio/lib/events/split_method_call_handler.dart b/splitio/lib/events/split_method_call_handler.dart index 862017a..8311633 100644 --- a/splitio/lib/events/split_method_call_handler.dart +++ b/splitio/lib/events/split_method_call_handler.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:flutter/services.dart'; import 'package:splitio/events/split_events_listener.dart'; import 'package:splitio/method_call_handler.dart'; import 'package:splitio/split_client.dart'; @@ -34,20 +33,20 @@ class SplitEventMethodCallHandler this._matchingKey, this._bucketingKey, this._splitClient); @override - Future handle(MethodCall call) async { - if (_clientEventCallbacks.containsKey(call.method)) { - if (_matchingKey == call.arguments['matchingKey'] && - _bucketingKey == call.arguments['bucketingKey']) { - var clientEventCallback = _clientEventCallbacks[call.method]; + Future handle(String methodName, dynamic methodArguments) async { + if (_clientEventCallbacks.containsKey(methodName)) { + if (_matchingKey == methodArguments['matchingKey'] && + _bucketingKey == methodArguments['bucketingKey']) { + var clientEventCallback = _clientEventCallbacks[methodName]; if (clientEventCallback != null && !clientEventCallback.isCompleted) { clientEventCallback.complete(_splitClient); } - if (_triggeredClientEvents.containsKey(call.method)) { - _triggeredClientEvents[call.method] = true; + if (_triggeredClientEvents.containsKey(methodName)) { + _triggeredClientEvents[methodName] = true; } } - } else if (call.method == _eventClientUpdated && + } else if (methodName == _eventClientUpdated && _updateStreamCompleter.hasListener && !_updateStreamCompleter.isPaused && !_updateStreamCompleter.isClosed) { diff --git a/splitio/lib/impressions/impressions_method_call_handler.dart b/splitio/lib/impressions/impressions_method_call_handler.dart index d09527a..7a34b8d 100644 --- a/splitio/lib/impressions/impressions_method_call_handler.dart +++ b/splitio/lib/impressions/impressions_method_call_handler.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:flutter/services.dart'; import 'package:splitio/impressions/split_impression.dart'; import 'package:splitio/method_call_handler.dart'; @@ -8,13 +7,13 @@ class ImpressionsMethodCallHandler extends StreamMethodCallHandler { final _streamController = StreamController(); @override - Future handle(MethodCall call) async { - if (call.method == 'impressionLog') { + Future handle(String methodName, dynamic methodArguments) async { + if (methodName == 'impressionLog') { if (_streamController.hasListener && !_streamController.isPaused && !_streamController.isClosed) { _streamController.add(Impression.fromMap( - Map.from(call.arguments ?? {}))); + Map.from(methodArguments ?? {}))); } } } diff --git a/splitio/lib/method_call_handler.dart b/splitio/lib/method_call_handler.dart index 42c7c4f..6ef88ac 100644 --- a/splitio/lib/method_call_handler.dart +++ b/splitio/lib/method_call_handler.dart @@ -1,7 +1,5 @@ -import 'package:flutter/services.dart'; - abstract class MethodCallHandler { - Future handle(MethodCall call); + Future handle(String methodName, dynamic methodArguments); } abstract class StreamMethodCallHandler extends MethodCallHandler { diff --git a/splitio/test/impressions_method_call_handler_test.dart b/splitio/test/impressions_method_call_handler_test.dart index f919f9a..4557cfa 100644 --- a/splitio/test/impressions_method_call_handler_test.dart +++ b/splitio/test/impressions_method_call_handler_test.dart @@ -34,8 +34,9 @@ void main() { }), ); - impressionsMethodCallHandler - .handle(const MethodCall('impressionLog', sourceMap)); + const methodCall = MethodCall('impressionLog', sourceMap); + impressionsMethodCallHandler.handle( + methodCall.method, methodCall.arguments); }); test('other method names are ignored', () async { @@ -47,6 +48,8 @@ void main() { expect(event, null); }, count: 0), ); - impressionsMethodCallHandler.handle(const MethodCall('clientReady')); + const methodCall = MethodCall('clientReady'); + impressionsMethodCallHandler.handle( + methodCall.method, methodCall.arguments); }); } diff --git a/splitio/test/method_channel_manager_test.dart b/splitio/test/method_channel_manager_test.dart index 0c98d25..d29a681 100644 --- a/splitio/test/method_channel_manager_test.dart +++ b/splitio/test/method_channel_manager_test.dart @@ -68,8 +68,8 @@ class MethodCallHandlerStub extends MethodCallHandler { final Set handledCalls = {}; @override - Future handle(MethodCall call) async { - handledCalls.add(call.method); + Future handle(String methodName, dynamic methodArguments) async { + handledCalls.add(methodName); } } diff --git a/splitio/test/split_event_listener_test.dart b/splitio/test/split_event_listener_test.dart index 3099e8f..c0bfa2f 100644 --- a/splitio/test/split_event_listener_test.dart +++ b/splitio/test/split_event_listener_test.dart @@ -25,7 +25,8 @@ void main() { setUp(() { _channel.setMockMethodCallHandler((MethodCall methodCall) { - splitEventMethodCallHandler.handle(methodCall); + splitEventMethodCallHandler.handle( + methodCall.method, methodCall.arguments); }); }); From 1e45d6db76c00f488b8e09e3b071c017d602f4c7 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Wed, 31 Aug 2022 16:48:24 -0300 Subject: [PATCH 02/17] Add platform interface dependency --- splitio/example/pubspec.lock | 7 +++++++ splitio/pubspec.yaml | 1 + 2 files changed, 8 insertions(+) diff --git a/splitio/example/pubspec.lock b/splitio/example/pubspec.lock index 3d5ec72..ec56524 100644 --- a/splitio/example/pubspec.lock +++ b/splitio/example/pubspec.lock @@ -109,6 +109,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" sky_engine: dependency: transitive description: flutter diff --git a/splitio/pubspec.yaml b/splitio/pubspec.yaml index a4f85a3..712d009 100644 --- a/splitio/pubspec.yaml +++ b/splitio/pubspec.yaml @@ -11,6 +11,7 @@ environment: dependencies: flutter: sdk: flutter + plugin_platform_interface: ^2.1.2 dev_dependencies: flutter_test: From 5494ad8329e4cce49f7dd4f4d9b9a2ba0496f374 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Wed, 31 Aug 2022 16:51:33 -0300 Subject: [PATCH 03/17] Platform --- splitio/lib/platform/common_platform.dart | 171 ++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 splitio/lib/platform/common_platform.dart diff --git a/splitio/lib/platform/common_platform.dart b/splitio/lib/platform/common_platform.dart new file mode 100644 index 0000000..34805bf --- /dev/null +++ b/splitio/lib/platform/common_platform.dart @@ -0,0 +1,171 @@ +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'package:splitio/split_client.dart'; +import 'package:splitio/split_configuration.dart'; +import 'package:splitio/split_result.dart'; +import 'package:splitio/split_view.dart'; + +abstract class _FactoryPlatform { + Future getClient( + {required String matchingKey, required String? bucketingKey}) { + throw UnimplementedError(); + } + + Future init({ + required String apiKey, + required String matchingKey, + required String? bucketingKey, + SplitConfiguration? sdkConfiguration, + }) { + throw UnimplementedError(); + } + + Future split( + {required String matchingKey, + required String? bucketingKey, + required String splitName}) { + throw UnimplementedError(); + } + + Future> splitNames( + {required String matchingKey, required String? bucketingKey}) { + throw UnimplementedError(); + } + + Future> splits( + {required String matchingKey, required String? bucketingKey}) { + throw UnimplementedError(); + } +} + +abstract class _ClientPlatform { + Future getTreatment( + {required String matchingKey, + required String? bucketingKey, + required String splitName, + Map attributes = const {}}) { + throw UnimplementedError(); + } + + Future getTreatmentWithConfig( + {required String matchingKey, + required String? bucketingKey, + required String splitName, + Map attributes = const {}}) { + throw UnimplementedError(); + } + + Future> getTreatments( + {required String matchingKey, + required String? bucketingKey, + required List splitNames, + Map attributes = const {}}) { + throw UnimplementedError(); + } + + Future> getTreatmentsWithConfig( + {required String matchingKey, + required String? bucketingKey, + required List splitNames, + Map attributes = const {}}) { + throw UnimplementedError(); + } + + Future> getAllAttributes( + {required String matchingKey, required String? bucketingKey}) { + throw UnimplementedError(); + } + + Future setAttribute( + {required String matchingKey, + required String? bucketingKey, + required String attributeName, + required dynamic value}) { + throw UnimplementedError(); + } + + Future setAttributes( + {required String matchingKey, + required String? bucketingKey, + required Map attributes}) { + throw UnimplementedError(); + } + + Future getAttribute( + {required String matchingKey, + required String? bucketingKey, + required String attributeName}) { + throw UnimplementedError(); + } + + Future removeAttribute( + {required String matchingKey, + required String? bucketingKey, + required String attributeName}) { + throw UnimplementedError(); + } + + Future clearAttributes( + {required String matchingKey, required String? bucketingKey}) { + throw UnimplementedError(); + } + + Future flush( + {required String matchingKey, required String? bucketingKey}) { + throw UnimplementedError(); + } + + Future destroy( + {required String matchingKey, required String? bucketingKey}) { + throw UnimplementedError(); + } + + Future track( + {required String matchingKey, + required String? bucketingKey, + required String eventType, + String? trafficType, + double? value, + Map properties = const {}}) { + throw UnimplementedError(); + } +} + +abstract class SplitioPlatform extends PlatformInterface + with _FactoryPlatform, _ClientPlatform { + SplitioPlatform() : super(token: _token); + + static SplitioPlatform _instance = MethodChannelPlatform(); + + static final Object _token = Object(); + + static SplitioPlatform get instance => _instance; + + /// Platform-specific plugins should set this with their own platform-specific + /// class that extends [SplitioPlatform] when they register themselves. + static set instance(SplitioPlatform instance) { + PlatformInterface.verify(instance, _token); + _instance = instance; + } +} + +class MethodChannelPlatform extends SplitioPlatform { + Map _buildParameters( + String matchingKey, String? bucketingKey, + [Map parameters = const {}]) { + Map result = {}; + result.addAll(parameters); + result.addAll(_getKeysMap(matchingKey, bucketingKey)); + + return result; + } + + Map _getKeysMap(String matchingKey, String? bucketingKey) { + Map result = {'matchingKey': matchingKey}; + + if (bucketingKey != null) { + result.addAll({'bucketingKey': bucketingKey!}); + } + + return result; + } +} From 97240ef6f2c92ca261d536c7b6eb9150d29bc036 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Wed, 31 Aug 2022 16:56:04 -0300 Subject: [PATCH 04/17] Use methods directly in channel manager --- .../lib/channel/method_channel_manager.dart | 202 +++++++++++++++- splitio/lib/split_client.dart | 215 ++++++++++-------- splitio/lib/splitio.dart | 101 ++++---- 3 files changed, 361 insertions(+), 157 deletions(-) diff --git a/splitio/lib/channel/method_channel_manager.dart b/splitio/lib/channel/method_channel_manager.dart index 34eab30..ace12e7 100644 --- a/splitio/lib/channel/method_channel_manager.dart +++ b/splitio/lib/channel/method_channel_manager.dart @@ -1,14 +1,17 @@ import 'package:flutter/services.dart'; import 'package:splitio/method_call_handler.dart'; +import 'package:splitio/platform/common_platform.dart'; +import 'package:splitio/split_client.dart'; +import 'package:splitio/split_configuration.dart'; +import 'package:splitio/split_result.dart'; +import 'package:splitio/split_view.dart'; -class MethodChannelManager { - final MethodChannel _channel; +class MethodChannelManager extends SplitioPlatform { + final SplitioPlatform _platform; final Set _handlers = {}; - MethodChannelManager(this._channel) { - _channel.setMethodCallHandler((call) => handle(call)); - } + MethodChannelManager(this._platform); void addHandler(MethodCallHandler handler) { _handlers.add(handler); @@ -24,15 +27,192 @@ class MethodChannelManager { } } - Future invokeMethod(String method, [dynamic arguments]) { - return _channel.invokeMethod(method, arguments); + @override + Future clearAttributes( + {required String matchingKey, required String? bucketingKey}) { + return _platform.clearAttributes( + matchingKey: matchingKey, bucketingKey: bucketingKey); + } + + @override + Future destroy( + {required String matchingKey, required String? bucketingKey}) { + return _platform.destroy( + matchingKey: matchingKey, bucketingKey: bucketingKey); + } + + @override + Future flush( + {required String matchingKey, required String? bucketingKey}) { + return _platform.flush( + matchingKey: matchingKey, bucketingKey: bucketingKey); + } + + @override + Future> getAllAttributes( + {required String matchingKey, required String? bucketingKey}) { + return _platform.getAllAttributes( + matchingKey: matchingKey, bucketingKey: bucketingKey); + } + + @override + Future getAttribute( + {required String matchingKey, + required String attributeName, + required String? bucketingKey}) { + return _platform.getAttribute( + matchingKey: matchingKey, + bucketingKey: bucketingKey, + attributeName: attributeName); + } + + @override + Future getClient( + {required String matchingKey, String? bucketingKey}) { + return _platform.getClient( + matchingKey: matchingKey, bucketingKey: bucketingKey); + } + + @override + Future getTreatment( + {required String matchingKey, + required String? bucketingKey, + required String splitName, + Map attributes = const {}}) { + return _platform.getTreatment( + matchingKey: matchingKey, + bucketingKey: bucketingKey, + splitName: splitName, + attributes: attributes); + } + + @override + Future getTreatmentWithConfig( + {required String matchingKey, + required String? bucketingKey, + required String splitName, + Map attributes = const {}}) { + return _platform.getTreatmentWithConfig( + matchingKey: matchingKey, + bucketingKey: bucketingKey, + splitName: splitName, + attributes: attributes); + } + + @override + Future> getTreatments( + {required String matchingKey, + required String? bucketingKey, + required List splitNames, + Map attributes = const {}}) { + return _platform.getTreatments( + matchingKey: matchingKey, + bucketingKey: bucketingKey, + splitNames: splitNames, + attributes: attributes); + } + + @override + Future> getTreatmentsWithConfig( + {required String matchingKey, + required String? bucketingKey, + required List splitNames, + Map attributes = const {}}) { + return _platform.getTreatmentsWithConfig( + matchingKey: matchingKey, + bucketingKey: bucketingKey, + splitNames: splitNames, + attributes: attributes); + } + + @override + Future init( + {required String apiKey, + required String matchingKey, + required String? bucketingKey, + SplitConfiguration? sdkConfiguration}) { + return _platform.init( + apiKey: apiKey, + matchingKey: matchingKey, + bucketingKey: bucketingKey, + sdkConfiguration: sdkConfiguration); + } + + @override + Future removeAttribute( + {required String matchingKey, + required String? bucketingKey, + required String attributeName}) { + return _platform.removeAttribute( + matchingKey: matchingKey, + bucketingKey: bucketingKey, + attributeName: attributeName); + } + + @override + Future setAttribute({ + required String matchingKey, + required String? bucketingKey, + required String attributeName, + required value, + }) { + return _platform.setAttribute( + matchingKey: matchingKey, + bucketingKey: bucketingKey, + attributeName: attributeName, + value: value); + } + + @override + Future setAttributes( + {required String matchingKey, + required String? bucketingKey, + required Map attributes}) { + return _platform.setAttributes( + matchingKey: matchingKey, + bucketingKey: bucketingKey, + attributes: attributes); + } + + @override + Future split( + {required String matchingKey, + required String? bucketingKey, + required String splitName}) { + return _platform.split( + matchingKey: matchingKey, + bucketingKey: bucketingKey, + splitName: splitName); + } + + @override + Future> splitNames( + {required String matchingKey, required String? bucketingKey}) { + return _platform.splitNames( + matchingKey: matchingKey, bucketingKey: bucketingKey); } - Future?> invokeMapMethod(String method, [dynamic arguments]) { - return _channel.invokeMapMethod(method, arguments); + @override + Future> splits( + {required String matchingKey, required String? bucketingKey}) { + return _platform.splits( + matchingKey: matchingKey, bucketingKey: bucketingKey); } - Future?> invokeListMethod(String method, [dynamic arguments]) { - return _channel.invokeListMethod(method, arguments); + @override + Future track( + {required String matchingKey, + required String? bucketingKey, + required String eventType, + String? trafficType, + double? value, + Map properties = const {}}) { + return _platform.track( + matchingKey: matchingKey, + bucketingKey: bucketingKey, + eventType: eventType, + trafficType: trafficType, + value: value, + properties: properties); } } diff --git a/splitio/lib/split_client.dart b/splitio/lib/split_client.dart index fb72ce0..d65ad9c 100644 --- a/splitio/lib/split_client.dart +++ b/splitio/lib/split_client.dart @@ -168,53 +168,73 @@ class DefaultSplitClient implements SplitClient { @override Future getTreatment(String splitName, [Map attributes = const {}]) async { - return await _methodChannelManager.invokeMethod( - 'getTreatment', - _buildParameters( - {'splitName': splitName, 'attributes': attributes})) ?? - _controlTreatment; + return _methodChannelManager.getTreatment( + matchingKey: _matchingKey, + bucketingKey: _bucketingKey, + splitName: splitName, + attributes: attributes); + // TODO return await _methodChannelManager.invokeMethod( + // TODO 'getTreatment', + // TODO _buildParameters( + // TODO {'splitName': splitName, 'attributes': attributes})) ?? + // TODO _controlTreatment; } @override Future getTreatmentWithConfig(String splitName, [Map attributes = const {}]) async { - Map? treatment = (await _methodChannelManager.invokeMapMethod( - 'getTreatmentWithConfig', - _buildParameters( - {'splitName': splitName, 'attributes': attributes}))) - ?.entries - .first - .value; - if (treatment == null) { - return _controlResult; - } - - return SplitResult(treatment['treatment'], treatment['config']); + return _methodChannelManager.getTreatmentWithConfig( + matchingKey: _matchingKey, + bucketingKey: _bucketingKey, + splitName: splitName, + attributes: attributes); + // TODO Map? treatment = (await _methodChannelManager.invokeMapMethod( + // TODO 'getTreatmentWithConfig', + // TODO _buildParameters( + // TODO {'splitName': splitName, 'attributes': attributes}))) + // TODO ?.entries + // TODO .first + // TODO .value; + // TODO if (treatment == null) { + // TODO return _controlResult; + // TODO } + // TODO + // TODO return SplitResult(treatment['treatment'], treatment['config']); } @override Future> getTreatments(List splitNames, [Map attributes = const {}]) async { - Map? treatments = await _methodChannelManager.invokeMapMethod( - 'getTreatments', - _buildParameters({'splitName': splitNames, 'attributes': attributes})); - - return treatments - ?.map((key, value) => MapEntry(key, value)) ?? - {for (var item in splitNames) item: _controlTreatment}; + return _methodChannelManager.getTreatments( + matchingKey: _matchingKey, + bucketingKey: _bucketingKey, + splitNames: splitNames, + attributes: attributes); + // TODO Map? treatments = await _methodChannelManager.invokeMapMethod( + // TODO 'getTreatments', + // TODO _buildParameters({'splitName': splitNames, 'attributes': attributes})); + // TODO + // TODO return treatments + // TODO ?.map((key, value) => MapEntry(key, value)) ?? + // TODO {for (var item in splitNames) item: _controlTreatment}; } @override Future> getTreatmentsWithConfig( List splitNames, [Map attributes = const {}]) async { - Map? treatments = await _methodChannelManager.invokeMapMethod( - 'getTreatmentsWithConfig', - _buildParameters({'splitName': splitNames, 'attributes': attributes})); - - return treatments?.map((key, value) => - MapEntry(key, SplitResult(value['treatment'], value['config']))) ?? - {for (var item in splitNames) item: _controlResult}; + return _methodChannelManager.getTreatmentsWithConfig( + matchingKey: _matchingKey, + bucketingKey: _bucketingKey, + splitNames: splitNames, + attributes: attributes); + // TODO Map? treatments = await _methodChannelManager.invokeMapMethod( + // TODO 'getTreatmentsWithConfig', + // TODO _buildParameters({'splitName': splitNames, 'attributes': attributes})); + // TODO + // TODO return treatments?.map((key, value) => + // TODO MapEntry(key, SplitResult(value['treatment'], value['config']))) ?? + // TODO {for (var item in splitNames) item: _controlResult}; } @override @@ -222,83 +242,109 @@ class DefaultSplitClient implements SplitClient { {String? trafficType, double? value, Map properties = const {}}) async { - var parameters = _buildParameters({'eventType': eventType}); - - if (trafficType != null) { - parameters['trafficType'] = trafficType; - } - - if (value != null) { - parameters['value'] = value; - } - - try { - return await _methodChannelManager.invokeMethod("track", parameters) - as bool; - } on Exception catch (_) { - return false; - } + return _methodChannelManager.track( + matchingKey: _matchingKey, + bucketingKey: _bucketingKey, + eventType: eventType, + trafficType: trafficType, + value: value, + properties: properties); + // TODO var parameters = _buildParameters({'eventType': eventType}); + // TODO + // TODO if (trafficType != null) { + // TODO parameters['trafficType'] = trafficType; + // TODO } + // TODO + // TODO if (value != null) { + // TODO parameters['value'] = value; + // TODO } + // TODO + // TODO try { + // TODO return await _methodChannelManager.invokeMethod("track", parameters) + // TODO as bool; + // TODO } on Exception catch (_) { + // TODO return false; + // TODO } } @override Future setAttribute(String attributeName, dynamic value) async { - var result = await _methodChannelManager.invokeMethod('setAttribute', - _buildParameters({'attributeName': attributeName, 'value': value})); - - if (result is bool) { - return result; - } - - return false; + return _methodChannelManager.setAttribute( + matchingKey: _matchingKey, + bucketingKey: _bucketingKey, + attributeName: attributeName, + value: value); + // TODO var result = await _methodChannelManager.invokeMethod('setAttribute', + // TODO _buildParameters({'attributeName': attributeName, 'value': value})); + // TODO + // TODO if (result is bool) { + // TODO return result; + // TODO } + // TODO + // TODO return false; } @override Future getAttribute(String attributeName) async { - return _methodChannelManager.invokeMethod( - 'getAttribute', _buildParameters({'attributeName': attributeName})); + return _methodChannelManager.getAttribute( + matchingKey: _matchingKey, + bucketingKey: _bucketingKey, + attributeName: attributeName); + // TODO return _methodChannelManager.invokeMethod( + // TODO 'getAttribute', _buildParameters({'attributeName': attributeName})); } @override Future setAttributes(Map attributes) async { - var result = await _methodChannelManager.invokeMethod( - 'setAttributes', _buildParameters({'attributes': attributes})); - - if (result is bool) { - return result; - } - - return false; + return _methodChannelManager.setAttributes( + matchingKey: _matchingKey, + bucketingKey: _bucketingKey, + attributes: attributes); + // TODO var result = await _methodChannelManager.invokeMethod( + // TODO 'setAttributes', _buildParameters({'attributes': attributes})); + // TODO + // TODO if (result is bool) { + // TODO return result; + // TODO } + // TODO + // TODO return false; } @override Future> getAttributes() async { - return (await _methodChannelManager.invokeMapMethod( - 'getAllAttributes', _buildParameters())) - ?.map((key, value) => MapEntry(key, value)) ?? - {}; + return _methodChannelManager.getAllAttributes( + matchingKey: _matchingKey, bucketingKey: _bucketingKey); + // TODO return (await _methodChannelManager.invokeMapMethod( + // TODO 'getAllAttributes', _buildParameters())) + // TODO ?.map((key, value) => MapEntry(key, value)) ?? + // TODO {}; } @override Future removeAttribute(String attributeName) async { - return await _methodChannelManager.invokeMethod( - 'removeAttribute', _buildParameters({'attributeName': attributeName})); + return _methodChannelManager.removeAttribute( + matchingKey: _matchingKey, + bucketingKey: _bucketingKey, + attributeName: attributeName); } @override Future clearAttributes() async { - return await _methodChannelManager.invokeMethod( - 'clearAttributes', _buildParameters()); + return _methodChannelManager.clearAttributes( + matchingKey: _matchingKey, bucketingKey: _bucketingKey); } @override Future flush() async { - return _methodChannelManager.invokeMethod('flush', _buildParameters()); + return _methodChannelManager.flush( + matchingKey: _matchingKey, bucketingKey: _bucketingKey); } @override Future destroy() async { _splitEventsListener.destroy(); - return _methodChannelManager.invokeMethod('destroy', _buildParameters()); + return _methodChannelManager.destroy( + matchingKey: _matchingKey, bucketingKey: _bucketingKey); } @override @@ -320,23 +366,4 @@ class DefaultSplitClient implements SplitClient { Future whenTimeout() { return _splitEventsListener.onTimeout(); } - - Map _getKeysMap() { - Map result = {'matchingKey': _matchingKey}; - - if (_bucketingKey != null) { - result.addAll({'bucketingKey': _bucketingKey!}); - } - - return result; - } - - Map _buildParameters( - [Map parameters = const {}]) { - Map result = {}; - result.addAll(parameters); - result.addAll(_getKeysMap()); - - return result; - } } diff --git a/splitio/lib/splitio.dart b/splitio/lib/splitio.dart index 254a486..02bdaaf 100644 --- a/splitio/lib/splitio.dart +++ b/splitio/lib/splitio.dart @@ -1,10 +1,10 @@ import 'dart:async'; -import 'package:flutter/services.dart'; import 'package:splitio/channel/method_channel_manager.dart'; import 'package:splitio/impressions/impressions_method_call_handler.dart'; import 'package:splitio/impressions/split_impression.dart'; import 'package:splitio/method_call_handler.dart'; +import 'package:splitio/platform/common_platform.dart'; import 'package:splitio/split_client.dart'; import 'package:splitio/split_configuration.dart'; import 'package:splitio/split_view.dart'; @@ -25,7 +25,7 @@ class Splitio { late final SplitConfiguration? _splitConfiguration; late final StreamMethodCallHandler _impressionsMethodCallHandler; final MethodChannelManager _methodChannelManager = - MethodChannelManager(const MethodChannel('splitio')); + MethodChannelManager(SplitioPlatform.instance); /// SDK instance constructor. /// @@ -97,34 +97,35 @@ class Splitio { client.whenUpdated().listen((client) => onUpdated.call(client)); } - _methodChannelManager.invokeMethod( - 'getClient', _buildGetClientArguments(key, bucketingKey)); + _methodChannelManager.getClient( + matchingKey: key, bucketingKey: bucketingKey); return client; } Future> splitNames() async { - List splitNames = - await _methodChannelManager.invokeListMethod('splitNames') ?? - []; + List splitNames = await _methodChannelManager.splitNames( + matchingKey: _defaultMatchingKey, bucketingKey: _defaultBucketingKey); return splitNames; } Future> splits() async { - List callResult = (await _methodChannelManager - .invokeListMethod>('splits') ?? - []); - - List splits = []; - for (var element in callResult) { - SplitView? splitView = SplitView.fromEntry(element); - if (splitView != null) { - splits.add(splitView); - } - } - - return Future.value(splits); + return _methodChannelManager.splits( + matchingKey: _defaultMatchingKey, bucketingKey: _defaultBucketingKey); + // TODO List callResult = (await _methodChannelManager + // TODO .invokeListMethod>('splits') ?? + // TODO []); + // TODO + // TODO List splits = []; + // TODO for (var element in callResult) { + // TODO SplitView? splitView = SplitView.fromEntry(element); + // TODO if (splitView != null) { + // TODO splits.add(splitView); + // TODO } + // TODO } + // TODO + // TODO return Future.value(splits); } /// If the impressionListener configuration has been enabled, @@ -134,40 +135,36 @@ class Splitio { } Future split(String splitName) async { - Map? mapResult = await _methodChannelManager - .invokeMapMethod('split', {'splitName': splitName}); - - if (mapResult == null) { - return null; - } - - return SplitView.fromEntry(mapResult); + return _methodChannelManager.split( + matchingKey: _defaultMatchingKey, + bucketingKey: _defaultBucketingKey, + splitName: splitName); + // TODO Map? mapResult = await _methodChannelManager + // TODO .invokeMapMethod('split', {'splitName': splitName}); + // TODO + // TODO if (mapResult == null) { + // TODO return null; + // TODO } + // TODO + // TODO return SplitView.fromEntry(mapResult); } Future _init() { - Map arguments = { - 'apiKey': _apiKey, - 'matchingKey': _defaultMatchingKey, - 'sdkConfiguration': _splitConfiguration?.configurationMap ?? {}, - }; - - if (_defaultBucketingKey != null) { - arguments.addAll({'bucketingKey': _defaultBucketingKey}); - } - - return _methodChannelManager.invokeMethod('init', arguments); - } - - Map _buildGetClientArguments( - String key, String? bucketingKey) { - var arguments = { - 'matchingKey': key, - }; - - if (bucketingKey != null) { - arguments.addAll({'bucketingKey': bucketingKey}); - } - - return arguments; + return _methodChannelManager.init( + apiKey: _apiKey, + matchingKey: _defaultMatchingKey, + bucketingKey: _defaultBucketingKey, + sdkConfiguration: _splitConfiguration); + // TODO Map arguments = { + // TODO 'apiKey': _apiKey, + // TODO 'matchingKey': _defaultMatchingKey, + // TODO 'sdkConfiguration': _splitConfiguration?.configurationMap ?? {}, + // TODO }; + // TODO + // TODO if (_defaultBucketingKey != null) { + // TODO arguments.addAll({'bucketingKey': _defaultBucketingKey}); + // TODO } + // TODO + // TODO return _methodChannelManager.invokeMethod('init', arguments); } } From 73ffbd45a64bcff0df438f5a101af66ec9029559 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Wed, 31 Aug 2022 17:28:20 -0300 Subject: [PATCH 05/17] Remove TODOs in splitio.dart --- splitio/lib/platform/common_platform.dart | 188 ++++++++++++++++++++++ splitio/lib/splitio.dart | 37 +---- 2 files changed, 193 insertions(+), 32 deletions(-) diff --git a/splitio/lib/platform/common_platform.dart b/splitio/lib/platform/common_platform.dart index 34805bf..16f8fa9 100644 --- a/splitio/lib/platform/common_platform.dart +++ b/splitio/lib/platform/common_platform.dart @@ -1,3 +1,4 @@ +import 'package:flutter/services.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import 'package:splitio/split_client.dart'; import 'package:splitio/split_configuration.dart'; @@ -149,6 +150,193 @@ abstract class SplitioPlatform extends PlatformInterface } class MethodChannelPlatform extends SplitioPlatform { + final MethodChannel _methodChannel = const MethodChannel('splitio'); + + @override + Future clearAttributes( + {required String matchingKey, required String? bucketingKey}) { + // TODO: implement clearAttributes + throw UnimplementedError(); + } + + @override + Future destroy( + {required String matchingKey, required String? bucketingKey}) { + // TODO: implement destroy + throw UnimplementedError(); + } + + @override + Future flush( + {required String matchingKey, required String? bucketingKey}) { + // TODO: implement flush + throw UnimplementedError(); + } + + @override + Future> getAllAttributes( + {required String matchingKey, required String? bucketingKey}) { + // TODO: implement getAllAttributes + throw UnimplementedError(); + } + + @override + Future getAttribute( + {required String matchingKey, + required String? bucketingKey, + required String attributeName}) { + // TODO: implement getAttribute + throw UnimplementedError(); + } + + @override + Future getClient( + {required String matchingKey, required String? bucketingKey}) { + // TODO: implement getClient + throw UnimplementedError(); + } + + @override + Future getTreatment( + {required String matchingKey, + required String? bucketingKey, + required String splitName, + Map attributes = const {}}) { + // TODO: implement getTreatment + throw UnimplementedError(); + } + + @override + Future getTreatmentWithConfig( + {required String matchingKey, + required String? bucketingKey, + required String splitName, + Map attributes = const {}}) { + // TODO: implement getTreatmentWithConfig + throw UnimplementedError(); + } + + @override + Future> getTreatments( + {required String matchingKey, + required String? bucketingKey, + required List splitNames, + Map attributes = const {}}) { + // TODO: implement getTreatments + throw UnimplementedError(); + } + + @override + Future> getTreatmentsWithConfig( + {required String matchingKey, + required String? bucketingKey, + required List splitNames, + Map attributes = const {}}) { + // TODO: implement getTreatmentsWithConfig + throw UnimplementedError(); + } + + @override + Future init( + {required String apiKey, + required String matchingKey, + required String? bucketingKey, + SplitConfiguration? sdkConfiguration}) { + Map arguments = { + 'apiKey': apiKey, + 'matchingKey': matchingKey, + 'sdkConfiguration': sdkConfiguration?.configurationMap ?? {}, + }; + + if (bucketingKey != null) { + arguments.addAll({'bucketingKey': bucketingKey}); + } + + return _methodChannel.invokeMethod('init', arguments); + } + + @override + Future removeAttribute( + {required String matchingKey, + required String? bucketingKey, + required String attributeName}) { + // TODO: implement removeAttribute + throw UnimplementedError(); + } + + @override + Future setAttribute( + {required String matchingKey, + required String? bucketingKey, + required String attributeName, + required value}) { + // TODO: implement setAttribute + throw UnimplementedError(); + } + + @override + Future setAttributes( + {required String matchingKey, + required String? bucketingKey, + required Map attributes}) { + // TODO: implement setAttributes + throw UnimplementedError(); + } + + @override + Future split( + {required String matchingKey, + required String? bucketingKey, + required String splitName}) async { + Map? mapResult = + await _methodChannel.invokeMapMethod('split', {'splitName': splitName}); + + if (mapResult == null) { + return null; + } + + return SplitView.fromEntry(mapResult); + } + + @override + Future> splitNames( + {required String matchingKey, required String? bucketingKey}) async { + List splitNames = + await _methodChannel.invokeListMethod('splitNames') ?? []; + + return splitNames; + } + + @override + Future> splits( + {required String matchingKey, required String? bucketingKey}) async { + List callResult = (await _methodChannel + .invokeListMethod>('splits') ?? + []); + + List splits = []; + for (var element in callResult) { + SplitView? splitView = SplitView.fromEntry(element); + if (splitView != null) { + splits.add(splitView); + } + } + + return Future.value(splits); + } + + @override + Future track( + {required String matchingKey, + required String? bucketingKey, + required String eventType, + String? trafficType, + double? value, + Map properties = const {}}) { + // TODO: implement track + throw UnimplementedError(); + } + Map _buildParameters( String matchingKey, String? bucketingKey, [Map parameters = const {}]) { diff --git a/splitio/lib/splitio.dart b/splitio/lib/splitio.dart index 02bdaaf..73c27a2 100644 --- a/splitio/lib/splitio.dart +++ b/splitio/lib/splitio.dart @@ -20,10 +20,15 @@ typedef ClientReadinessCallback = void Function(SplitClient splitClient); class Splitio { final String _apiKey; + final String _defaultMatchingKey; + late final String? _defaultBucketingKey; + late final SplitConfiguration? _splitConfiguration; + late final StreamMethodCallHandler _impressionsMethodCallHandler; + final MethodChannelManager _methodChannelManager = MethodChannelManager(SplitioPlatform.instance); @@ -113,19 +118,6 @@ class Splitio { Future> splits() async { return _methodChannelManager.splits( matchingKey: _defaultMatchingKey, bucketingKey: _defaultBucketingKey); - // TODO List callResult = (await _methodChannelManager - // TODO .invokeListMethod>('splits') ?? - // TODO []); - // TODO - // TODO List splits = []; - // TODO for (var element in callResult) { - // TODO SplitView? splitView = SplitView.fromEntry(element); - // TODO if (splitView != null) { - // TODO splits.add(splitView); - // TODO } - // TODO } - // TODO - // TODO return Future.value(splits); } /// If the impressionListener configuration has been enabled, @@ -139,14 +131,6 @@ class Splitio { matchingKey: _defaultMatchingKey, bucketingKey: _defaultBucketingKey, splitName: splitName); - // TODO Map? mapResult = await _methodChannelManager - // TODO .invokeMapMethod('split', {'splitName': splitName}); - // TODO - // TODO if (mapResult == null) { - // TODO return null; - // TODO } - // TODO - // TODO return SplitView.fromEntry(mapResult); } Future _init() { @@ -155,16 +139,5 @@ class Splitio { matchingKey: _defaultMatchingKey, bucketingKey: _defaultBucketingKey, sdkConfiguration: _splitConfiguration); - // TODO Map arguments = { - // TODO 'apiKey': _apiKey, - // TODO 'matchingKey': _defaultMatchingKey, - // TODO 'sdkConfiguration': _splitConfiguration?.configurationMap ?? {}, - // TODO }; - // TODO - // TODO if (_defaultBucketingKey != null) { - // TODO arguments.addAll({'bucketingKey': _defaultBucketingKey}); - // TODO } - // TODO - // TODO return _methodChannelManager.invokeMethod('init', arguments); } } From c763cdfcef357d443ae01f122e22362588d9be29 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Wed, 31 Aug 2022 18:22:29 -0300 Subject: [PATCH 06/17] Remove split_client TODOs --- .../lib/channel/method_channel_manager.dart | 6 +- splitio/lib/platform/common_platform.dart | 216 +------------- .../lib/platform/method_channel_platform.dart | 267 ++++++++++++++++++ splitio/lib/split_client.dart | 73 ----- 4 files changed, 272 insertions(+), 290 deletions(-) create mode 100644 splitio/lib/platform/method_channel_platform.dart diff --git a/splitio/lib/channel/method_channel_manager.dart b/splitio/lib/channel/method_channel_manager.dart index ace12e7..8965824 100644 --- a/splitio/lib/channel/method_channel_manager.dart +++ b/splitio/lib/channel/method_channel_manager.dart @@ -1,7 +1,6 @@ import 'package:flutter/services.dart'; import 'package:splitio/method_call_handler.dart'; import 'package:splitio/platform/common_platform.dart'; -import 'package:splitio/split_client.dart'; import 'package:splitio/split_configuration.dart'; import 'package:splitio/split_result.dart'; import 'package:splitio/split_view.dart'; @@ -49,7 +48,7 @@ class MethodChannelManager extends SplitioPlatform { } @override - Future> getAllAttributes( + Future> getAllAttributes( {required String matchingKey, required String? bucketingKey}) { return _platform.getAllAttributes( matchingKey: matchingKey, bucketingKey: bucketingKey); @@ -67,8 +66,7 @@ class MethodChannelManager extends SplitioPlatform { } @override - Future getClient( - {required String matchingKey, String? bucketingKey}) { + Future getClient({required String matchingKey, String? bucketingKey}) { return _platform.getClient( matchingKey: matchingKey, bucketingKey: bucketingKey); } diff --git a/splitio/lib/platform/common_platform.dart b/splitio/lib/platform/common_platform.dart index 16f8fa9..b8314f5 100644 --- a/splitio/lib/platform/common_platform.dart +++ b/splitio/lib/platform/common_platform.dart @@ -1,12 +1,11 @@ -import 'package:flutter/services.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -import 'package:splitio/split_client.dart'; +import 'package:splitio/platform/method_channel_platform.dart'; import 'package:splitio/split_configuration.dart'; import 'package:splitio/split_result.dart'; import 'package:splitio/split_view.dart'; abstract class _FactoryPlatform { - Future getClient( + Future getClient( {required String matchingKey, required String? bucketingKey}) { throw UnimplementedError(); } @@ -71,7 +70,7 @@ abstract class _ClientPlatform { throw UnimplementedError(); } - Future> getAllAttributes( + Future> getAllAttributes( {required String matchingKey, required String? bucketingKey}) { throw UnimplementedError(); } @@ -148,212 +147,3 @@ abstract class SplitioPlatform extends PlatformInterface _instance = instance; } } - -class MethodChannelPlatform extends SplitioPlatform { - final MethodChannel _methodChannel = const MethodChannel('splitio'); - - @override - Future clearAttributes( - {required String matchingKey, required String? bucketingKey}) { - // TODO: implement clearAttributes - throw UnimplementedError(); - } - - @override - Future destroy( - {required String matchingKey, required String? bucketingKey}) { - // TODO: implement destroy - throw UnimplementedError(); - } - - @override - Future flush( - {required String matchingKey, required String? bucketingKey}) { - // TODO: implement flush - throw UnimplementedError(); - } - - @override - Future> getAllAttributes( - {required String matchingKey, required String? bucketingKey}) { - // TODO: implement getAllAttributes - throw UnimplementedError(); - } - - @override - Future getAttribute( - {required String matchingKey, - required String? bucketingKey, - required String attributeName}) { - // TODO: implement getAttribute - throw UnimplementedError(); - } - - @override - Future getClient( - {required String matchingKey, required String? bucketingKey}) { - // TODO: implement getClient - throw UnimplementedError(); - } - - @override - Future getTreatment( - {required String matchingKey, - required String? bucketingKey, - required String splitName, - Map attributes = const {}}) { - // TODO: implement getTreatment - throw UnimplementedError(); - } - - @override - Future getTreatmentWithConfig( - {required String matchingKey, - required String? bucketingKey, - required String splitName, - Map attributes = const {}}) { - // TODO: implement getTreatmentWithConfig - throw UnimplementedError(); - } - - @override - Future> getTreatments( - {required String matchingKey, - required String? bucketingKey, - required List splitNames, - Map attributes = const {}}) { - // TODO: implement getTreatments - throw UnimplementedError(); - } - - @override - Future> getTreatmentsWithConfig( - {required String matchingKey, - required String? bucketingKey, - required List splitNames, - Map attributes = const {}}) { - // TODO: implement getTreatmentsWithConfig - throw UnimplementedError(); - } - - @override - Future init( - {required String apiKey, - required String matchingKey, - required String? bucketingKey, - SplitConfiguration? sdkConfiguration}) { - Map arguments = { - 'apiKey': apiKey, - 'matchingKey': matchingKey, - 'sdkConfiguration': sdkConfiguration?.configurationMap ?? {}, - }; - - if (bucketingKey != null) { - arguments.addAll({'bucketingKey': bucketingKey}); - } - - return _methodChannel.invokeMethod('init', arguments); - } - - @override - Future removeAttribute( - {required String matchingKey, - required String? bucketingKey, - required String attributeName}) { - // TODO: implement removeAttribute - throw UnimplementedError(); - } - - @override - Future setAttribute( - {required String matchingKey, - required String? bucketingKey, - required String attributeName, - required value}) { - // TODO: implement setAttribute - throw UnimplementedError(); - } - - @override - Future setAttributes( - {required String matchingKey, - required String? bucketingKey, - required Map attributes}) { - // TODO: implement setAttributes - throw UnimplementedError(); - } - - @override - Future split( - {required String matchingKey, - required String? bucketingKey, - required String splitName}) async { - Map? mapResult = - await _methodChannel.invokeMapMethod('split', {'splitName': splitName}); - - if (mapResult == null) { - return null; - } - - return SplitView.fromEntry(mapResult); - } - - @override - Future> splitNames( - {required String matchingKey, required String? bucketingKey}) async { - List splitNames = - await _methodChannel.invokeListMethod('splitNames') ?? []; - - return splitNames; - } - - @override - Future> splits( - {required String matchingKey, required String? bucketingKey}) async { - List callResult = (await _methodChannel - .invokeListMethod>('splits') ?? - []); - - List splits = []; - for (var element in callResult) { - SplitView? splitView = SplitView.fromEntry(element); - if (splitView != null) { - splits.add(splitView); - } - } - - return Future.value(splits); - } - - @override - Future track( - {required String matchingKey, - required String? bucketingKey, - required String eventType, - String? trafficType, - double? value, - Map properties = const {}}) { - // TODO: implement track - throw UnimplementedError(); - } - - Map _buildParameters( - String matchingKey, String? bucketingKey, - [Map parameters = const {}]) { - Map result = {}; - result.addAll(parameters); - result.addAll(_getKeysMap(matchingKey, bucketingKey)); - - return result; - } - - Map _getKeysMap(String matchingKey, String? bucketingKey) { - Map result = {'matchingKey': matchingKey}; - - if (bucketingKey != null) { - result.addAll({'bucketingKey': bucketingKey!}); - } - - return result; - } -} diff --git a/splitio/lib/platform/method_channel_platform.dart b/splitio/lib/platform/method_channel_platform.dart new file mode 100644 index 0000000..666a80d --- /dev/null +++ b/splitio/lib/platform/method_channel_platform.dart @@ -0,0 +1,267 @@ +import 'package:flutter/services.dart'; +import 'package:splitio/platform/common_platform.dart'; +import 'package:splitio/split_configuration.dart'; +import 'package:splitio/split_result.dart'; +import 'package:splitio/split_view.dart'; + +class MethodChannelPlatform extends SplitioPlatform { + static const String _controlTreatment = 'control'; + + static const SplitResult _controlResult = + SplitResult(_controlTreatment, null); + + final MethodChannel _methodChannel = const MethodChannel('splitio'); + + @override + Future clearAttributes( + {required String matchingKey, required String? bucketingKey}) async { + return await _methodChannel.invokeMethod( + 'clearAttributes', _buildParameters(matchingKey, bucketingKey)); + } + + @override + Future destroy( + {required String matchingKey, required String? bucketingKey}) async { + return await _methodChannel.invokeMethod( + 'destroy', _buildParameters(matchingKey, bucketingKey)); + } + + @override + Future flush( + {required String matchingKey, required String? bucketingKey}) async { + return await _methodChannel.invokeMethod( + 'flush', _buildParameters(matchingKey, bucketingKey)); + } + + @override + Future> getAllAttributes( + {required String matchingKey, required String? bucketingKey}) async { + return (await _methodChannel.invokeMapMethod( + 'getAllAttributes', _buildParameters(matchingKey, bucketingKey))) + ?.map((key, value) => MapEntry(key, value)) ?? + {}; + } + + @override + Future getAttribute({required String matchingKey, + required String? bucketingKey, + required String attributeName}) { + return _methodChannel.invokeMethod( + 'getAttribute', _buildParameters( + matchingKey, bucketingKey, {'attributeName': attributeName})); + } + + @override + Future getClient( + {required String matchingKey, required String? bucketingKey}) { + return _methodChannel.invokeMethod( + 'getClient', _buildParameters(matchingKey, bucketingKey)); + } + + @override + Future getTreatment({required String matchingKey, + required String? bucketingKey, + required String splitName, + Map attributes = const {}}) async { + return await _methodChannel.invokeMethod( + 'getTreatment', + _buildParameters(matchingKey, bucketingKey, + {'splitName': splitName, 'attributes': attributes})) ?? + _controlTreatment; + } + + @override + Future getTreatmentWithConfig({required String matchingKey, + required String? bucketingKey, + required String splitName, + Map attributes = const {}}) async { + Map? treatment = (await _methodChannel.invokeMapMethod( + 'getTreatmentWithConfig', + _buildParameters(matchingKey, bucketingKey + {'splitName': splitName, 'attributes': attributes}))) + ?.entries + .first + .value; + if (treatment == null) { + return _controlResult; + } + + return SplitResult(treatment['treatment'], treatment['config']); + } + + @override + Future> getTreatments({required String matchingKey, + required String? bucketingKey, + required List splitNames, + Map attributes = const {}}) async { + Map? treatments = await _methodChannel.invokeMapMethod( + 'getTreatments', + _buildParameters(matchingKey, bucketingKey, + {'splitName': splitNames, 'attributes': attributes})); + + return treatments + ?.map((key, value) => MapEntry(key, value)) ?? + {for (var item in splitNames) item: _controlTreatment}; + } + + @override + Future> getTreatmentsWithConfig( + {required String matchingKey, + required String? bucketingKey, + required List splitNames, + Map attributes = const {}}) async { + Map? treatments = await _methodChannel.invokeMapMethod( + 'getTreatmentsWithConfig', + _buildParameters(matchingKey, bucketingKey, + {'splitName': splitNames, 'attributes': attributes})); + + return treatments?.map((key, value) => + MapEntry(key, SplitResult(value['treatment'], value['config']))) ?? + {for (var item in splitNames) item: _controlResult}; + } + + @override + Future init({required String apiKey, + required String matchingKey, + required String? bucketingKey, + SplitConfiguration? sdkConfiguration}) { + Map arguments = { + 'apiKey': apiKey, + 'matchingKey': matchingKey, + 'sdkConfiguration': sdkConfiguration?.configurationMap ?? {}, + }; + + if (bucketingKey != null) { + arguments.addAll({'bucketingKey': bucketingKey}); + } + + return _methodChannel.invokeMethod('init', arguments); + } + + @override + Future removeAttribute({required String matchingKey, + required String? bucketingKey, + required String attributeName}) async { + return await _methodChannel.invokeMethod( + 'removeAttribute', _buildParameters( + matchingKey, bucketingKey, {'attributeName': attributeName})); + } + + @override + Future setAttribute({required String matchingKey, + required String? bucketingKey, + required String attributeName, + required value}) async { + var result = await _methodChannel.invokeMethod('setAttribute', + _buildParameters(matchingKey, bucketingKey, + {'attributeName': attributeName, 'value': value})); + + if (result is bool) { + return result; + } + + return false; + } + + @override + Future setAttributes({required String matchingKey, + required String? bucketingKey, + required Map attributes}) async { + var result = await _methodChannel.invokeMethod( + 'setAttributes', _buildParameters( + matchingKey, bucketingKey, {'attributes': attributes})); + + if (result is bool) { + return result; + } + + return false; + } + + @override + Future split({required String matchingKey, + required String? bucketingKey, + required String splitName}) async { + Map? mapResult = + await _methodChannel.invokeMapMethod('split', {'splitName': splitName}); + + if (mapResult == null) { + return null; + } + + return SplitView.fromEntry(mapResult); + } + + @override + Future> splitNames( + {required String matchingKey, required String? bucketingKey}) async { + List splitNames = + await _methodChannel.invokeListMethod('splitNames') ?? []; + + return splitNames; + } + + @override + Future> splits( + {required String matchingKey, required String? bucketingKey}) async { + List callResult = (await _methodChannel + .invokeListMethod>('splits') ?? + []); + + List splits = []; + for (var element in callResult) { + SplitView? splitView = SplitView.fromEntry(element); + if (splitView != null) { + splits.add(splitView); + } + } + + return Future.value(splits); + } + + @override + Future track({required String matchingKey, + required String? bucketingKey, + required String eventType, + String? trafficType, + double? value, + Map properties = const {}}) async { + var parameters = _buildParameters( + matchingKey, bucketingKey, {'eventType': eventType}); + + if (trafficType != null) { + parameters['trafficType'] = trafficType; + } + + if (value != null) { + parameters['value'] = value; + } + + try { + return await _methodChannel.invokeMethod("track", parameters) + as bool; + } on Exception catch (_) { + return false; + } + } + + Map _buildParameters(String matchingKey, + String? bucketingKey, + [Map parameters = const {}]) { + Map result = {}; + result.addAll(parameters); + result.addAll(_getKeysMap(matchingKey, bucketingKey)); + + return result; + } + + Map _getKeysMap(String matchingKey, String? bucketingKey) { + Map result = {'matchingKey': matchingKey}; + + if (bucketingKey != null) { + result.addAll({'bucketingKey': bucketingKey}); + } + + return result; + } +} diff --git a/splitio/lib/split_client.dart b/splitio/lib/split_client.dart index d65ad9c..200a487 100644 --- a/splitio/lib/split_client.dart +++ b/splitio/lib/split_client.dart @@ -142,10 +142,6 @@ abstract class SplitClient { } class DefaultSplitClient implements SplitClient { - static const String _controlTreatment = 'control'; - static const SplitResult _controlResult = - SplitResult(_controlTreatment, null); - final MethodChannelManager _methodChannelManager; late final SplitEventMethodCallHandler _methodCallHandler; final String _matchingKey; @@ -173,11 +169,6 @@ class DefaultSplitClient implements SplitClient { bucketingKey: _bucketingKey, splitName: splitName, attributes: attributes); - // TODO return await _methodChannelManager.invokeMethod( - // TODO 'getTreatment', - // TODO _buildParameters( - // TODO {'splitName': splitName, 'attributes': attributes})) ?? - // TODO _controlTreatment; } @override @@ -188,18 +179,6 @@ class DefaultSplitClient implements SplitClient { bucketingKey: _bucketingKey, splitName: splitName, attributes: attributes); - // TODO Map? treatment = (await _methodChannelManager.invokeMapMethod( - // TODO 'getTreatmentWithConfig', - // TODO _buildParameters( - // TODO {'splitName': splitName, 'attributes': attributes}))) - // TODO ?.entries - // TODO .first - // TODO .value; - // TODO if (treatment == null) { - // TODO return _controlResult; - // TODO } - // TODO - // TODO return SplitResult(treatment['treatment'], treatment['config']); } @override @@ -210,13 +189,6 @@ class DefaultSplitClient implements SplitClient { bucketingKey: _bucketingKey, splitNames: splitNames, attributes: attributes); - // TODO Map? treatments = await _methodChannelManager.invokeMapMethod( - // TODO 'getTreatments', - // TODO _buildParameters({'splitName': splitNames, 'attributes': attributes})); - // TODO - // TODO return treatments - // TODO ?.map((key, value) => MapEntry(key, value)) ?? - // TODO {for (var item in splitNames) item: _controlTreatment}; } @override @@ -228,13 +200,6 @@ class DefaultSplitClient implements SplitClient { bucketingKey: _bucketingKey, splitNames: splitNames, attributes: attributes); - // TODO Map? treatments = await _methodChannelManager.invokeMapMethod( - // TODO 'getTreatmentsWithConfig', - // TODO _buildParameters({'splitName': splitNames, 'attributes': attributes})); - // TODO - // TODO return treatments?.map((key, value) => - // TODO MapEntry(key, SplitResult(value['treatment'], value['config']))) ?? - // TODO {for (var item in splitNames) item: _controlResult}; } @override @@ -249,22 +214,6 @@ class DefaultSplitClient implements SplitClient { trafficType: trafficType, value: value, properties: properties); - // TODO var parameters = _buildParameters({'eventType': eventType}); - // TODO - // TODO if (trafficType != null) { - // TODO parameters['trafficType'] = trafficType; - // TODO } - // TODO - // TODO if (value != null) { - // TODO parameters['value'] = value; - // TODO } - // TODO - // TODO try { - // TODO return await _methodChannelManager.invokeMethod("track", parameters) - // TODO as bool; - // TODO } on Exception catch (_) { - // TODO return false; - // TODO } } @override @@ -274,14 +223,6 @@ class DefaultSplitClient implements SplitClient { bucketingKey: _bucketingKey, attributeName: attributeName, value: value); - // TODO var result = await _methodChannelManager.invokeMethod('setAttribute', - // TODO _buildParameters({'attributeName': attributeName, 'value': value})); - // TODO - // TODO if (result is bool) { - // TODO return result; - // TODO } - // TODO - // TODO return false; } @override @@ -290,8 +231,6 @@ class DefaultSplitClient implements SplitClient { matchingKey: _matchingKey, bucketingKey: _bucketingKey, attributeName: attributeName); - // TODO return _methodChannelManager.invokeMethod( - // TODO 'getAttribute', _buildParameters({'attributeName': attributeName})); } @override @@ -300,24 +239,12 @@ class DefaultSplitClient implements SplitClient { matchingKey: _matchingKey, bucketingKey: _bucketingKey, attributes: attributes); - // TODO var result = await _methodChannelManager.invokeMethod( - // TODO 'setAttributes', _buildParameters({'attributes': attributes})); - // TODO - // TODO if (result is bool) { - // TODO return result; - // TODO } - // TODO - // TODO return false; } @override Future> getAttributes() async { return _methodChannelManager.getAllAttributes( matchingKey: _matchingKey, bucketingKey: _bucketingKey); - // TODO return (await _methodChannelManager.invokeMapMethod( - // TODO 'getAllAttributes', _buildParameters())) - // TODO ?.map((key, value) => MapEntry(key, value)) ?? - // TODO {}; } @override From cf87d5702e7ec5a085b6573d47858275c06041bb Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Thu, 1 Sep 2022 11:04:44 -0300 Subject: [PATCH 07/17] WIP handling native calls --- .../lib/channel/method_channel_manager.dart | 19 ++---- splitio/lib/events/split_events_listener.dart | 4 +- splitio/lib/platform/common_platform.dart | 5 ++ .../lib/platform/method_channel_platform.dart | 66 ++++++++++++------- splitio/lib/split_client.dart | 3 +- splitio/lib/splitio.dart | 2 +- splitio/test/method_channel_manager_test.dart | 14 ++-- 7 files changed, 66 insertions(+), 47 deletions(-) diff --git a/splitio/lib/channel/method_channel_manager.dart b/splitio/lib/channel/method_channel_manager.dart index 8965824..d22f580 100644 --- a/splitio/lib/channel/method_channel_manager.dart +++ b/splitio/lib/channel/method_channel_manager.dart @@ -1,4 +1,3 @@ -import 'package:flutter/services.dart'; import 'package:splitio/method_call_handler.dart'; import 'package:splitio/platform/common_platform.dart'; import 'package:splitio/split_configuration.dart'; @@ -8,22 +7,16 @@ import 'package:splitio/split_view.dart'; class MethodChannelManager extends SplitioPlatform { final SplitioPlatform _platform; - final Set _handlers = {}; - MethodChannelManager(this._platform); - void addHandler(MethodCallHandler handler) { - _handlers.add(handler); - } - - void removeHandler(MethodCallHandler handler) { - _handlers.remove(handler); + @override + void addNativeCallHandler(MethodCallHandler handler) { + _platform.addNativeCallHandler(handler); } - Future handle(MethodCall call) async { - for (MethodCallHandler handler in _handlers) { - handler.handle(call.method, call.arguments); - } + @override + void removeNativeCallHandler(MethodCallHandler handler) { + _platform.removeNativeCallHandler(handler); } @override diff --git a/splitio/lib/events/split_events_listener.dart b/splitio/lib/events/split_events_listener.dart index be9c9f6..72a1834 100644 --- a/splitio/lib/events/split_events_listener.dart +++ b/splitio/lib/events/split_events_listener.dart @@ -22,7 +22,7 @@ class DefaultEventsListener implements SplitEventsListener { final SplitEventMethodCallHandler _methodCallHandler; DefaultEventsListener(this._methodChannelManager, this._methodCallHandler) { - _methodChannelManager.addHandler(_methodCallHandler); + _methodChannelManager.addNativeCallHandler(_methodCallHandler); } @override @@ -48,6 +48,6 @@ class DefaultEventsListener implements SplitEventsListener { @override void destroy() { _methodCallHandler.destroy(); - _methodChannelManager.removeHandler(_methodCallHandler); + _methodChannelManager.removeNativeCallHandler(_methodCallHandler); } } diff --git a/splitio/lib/platform/common_platform.dart b/splitio/lib/platform/common_platform.dart index b8314f5..656af36 100644 --- a/splitio/lib/platform/common_platform.dart +++ b/splitio/lib/platform/common_platform.dart @@ -1,4 +1,5 @@ import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'package:splitio/method_call_handler.dart'; import 'package:splitio/platform/method_channel_platform.dart'; import 'package:splitio/split_configuration.dart'; import 'package:splitio/split_result.dart'; @@ -146,4 +147,8 @@ abstract class SplitioPlatform extends PlatformInterface PlatformInterface.verify(instance, _token); _instance = instance; } + + void addNativeCallHandler(MethodCallHandler handler); //TODO + + void removeNativeCallHandler(MethodCallHandler handler); //TODO } diff --git a/splitio/lib/platform/method_channel_platform.dart b/splitio/lib/platform/method_channel_platform.dart index 666a80d..9eeac6a 100644 --- a/splitio/lib/platform/method_channel_platform.dart +++ b/splitio/lib/platform/method_channel_platform.dart @@ -1,17 +1,57 @@ import 'package:flutter/services.dart'; +import 'package:splitio/method_call_handler.dart'; import 'package:splitio/platform/common_platform.dart'; import 'package:splitio/split_configuration.dart'; import 'package:splitio/split_result.dart'; import 'package:splitio/split_view.dart'; -class MethodChannelPlatform extends SplitioPlatform { - static const String _controlTreatment = 'control'; +const String _controlTreatment = 'control'; +const SplitResult _controlResult = SplitResult(_controlTreatment, null); - static const SplitResult _controlResult = - SplitResult(_controlTreatment, null); +class MethodChannelPlatform extends SplitioPlatform { final MethodChannel _methodChannel = const MethodChannel('splitio'); + final Set _handlers = {}; + + MethodChannelPlatform() { + _methodChannel.setMethodCallHandler((call) => handle(call)); + } + + Future handle(MethodCall call) async { + for (MethodCallHandler handler in _handlers) { + handler.handle(call.method, call.arguments); + } + } + + @override + void addNativeCallHandler(MethodCallHandler handler) { + _handlers.add(handler); + } + + @override + void removeNativeCallHandler(MethodCallHandler handler) { + _handlers.remove(handler); + } + + @override + Future init({required String apiKey, + required String matchingKey, + required String? bucketingKey, + SplitConfiguration? sdkConfiguration}) { + Map arguments = { + 'apiKey': apiKey, + 'matchingKey': matchingKey, + 'sdkConfiguration': sdkConfiguration?.configurationMap ?? {}, + }; + + if (bucketingKey != null) { + arguments.addAll({'bucketingKey': bucketingKey}); + } + + return _methodChannel.invokeMethod('init', arguments); + } + @override Future clearAttributes( {required String matchingKey, required String? bucketingKey}) async { @@ -120,24 +160,6 @@ class MethodChannelPlatform extends SplitioPlatform { {for (var item in splitNames) item: _controlResult}; } - @override - Future init({required String apiKey, - required String matchingKey, - required String? bucketingKey, - SplitConfiguration? sdkConfiguration}) { - Map arguments = { - 'apiKey': apiKey, - 'matchingKey': matchingKey, - 'sdkConfiguration': sdkConfiguration?.configurationMap ?? {}, - }; - - if (bucketingKey != null) { - arguments.addAll({'bucketingKey': bucketingKey}); - } - - return _methodChannel.invokeMethod('init', arguments); - } - @override Future removeAttribute({required String matchingKey, required String? bucketingKey, diff --git a/splitio/lib/split_client.dart b/splitio/lib/split_client.dart index 200a487..74814c3 100644 --- a/splitio/lib/split_client.dart +++ b/splitio/lib/split_client.dart @@ -143,7 +143,6 @@ abstract class SplitClient { class DefaultSplitClient implements SplitClient { final MethodChannelManager _methodChannelManager; - late final SplitEventMethodCallHandler _methodCallHandler; final String _matchingKey; final String? _bucketingKey; @@ -151,7 +150,7 @@ class DefaultSplitClient implements SplitClient { DefaultSplitClient( this._methodChannelManager, this._matchingKey, this._bucketingKey) { - _methodCallHandler = + SplitEventMethodCallHandler _methodCallHandler = SplitEventMethodCallHandler(_matchingKey, _bucketingKey, this); _splitEventsListener = DefaultEventsListener(_methodChannelManager, _methodCallHandler); diff --git a/splitio/lib/splitio.dart b/splitio/lib/splitio.dart index 73c27a2..bd457e2 100644 --- a/splitio/lib/splitio.dart +++ b/splitio/lib/splitio.dart @@ -47,7 +47,7 @@ class Splitio { _defaultBucketingKey = bucketingKey; _splitConfiguration = configuration; _impressionsMethodCallHandler = ImpressionsMethodCallHandler(); - _methodChannelManager.addHandler(_impressionsMethodCallHandler); + _methodChannelManager.addNativeCallHandler(_impressionsMethodCallHandler); _init(); } diff --git a/splitio/test/method_channel_manager_test.dart b/splitio/test/method_channel_manager_test.dart index d29a681..a752a2f 100644 --- a/splitio/test/method_channel_manager_test.dart +++ b/splitio/test/method_channel_manager_test.dart @@ -17,9 +17,9 @@ void main() { var mockMethodHandler1 = MethodCallHandlerStub(); var mockMethodHandler2 = MethodCallHandlerStub(); var mockMethodHandler3 = MethodCallHandlerStub(); - _methodChannelManager.addHandler(mockMethodHandler1); - _methodChannelManager.addHandler(mockMethodHandler2); - _methodChannelManager.addHandler(mockMethodHandler3); + _methodChannelManager.addNativeCallHandler(mockMethodHandler1); + _methodChannelManager.addNativeCallHandler(mockMethodHandler2); + _methodChannelManager.addNativeCallHandler(mockMethodHandler3); _methodChannelManager.handle(const MethodCall('test-method')); @@ -50,11 +50,11 @@ void main() { final MethodCallHandlerStub handler1 = MethodCallHandlerStub(); final MethodCallHandlerStub handler2 = MethodCallHandlerStub(); final MethodCallHandlerStub handler3 = MethodCallHandlerStub(); - _methodChannelManager.addHandler(handler1); - _methodChannelManager.addHandler(handler2); - _methodChannelManager.addHandler(handler3); + _methodChannelManager.addNativeCallHandler(handler1); + _methodChannelManager.addNativeCallHandler(handler2); + _methodChannelManager.addNativeCallHandler(handler3); - _methodChannelManager.removeHandler(handler2); + _methodChannelManager.removeNativeCallHandler(handler2); _methodChannelManager.handle(const MethodCall('test-method')); From ed0103c4023f6a916cd1322dbc37330df67b87d9 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Thu, 1 Sep 2022 12:44:50 -0300 Subject: [PATCH 08/17] Impressions stream --- .../lib/channel/method_channel_manager.dart | 35 +++++++++++++++++-- splitio/lib/events/split_events_listener.dart | 7 ++-- splitio/lib/platform/common_platform.dart | 33 +++++++++++++++++ .../lib/platform/method_channel_platform.dart | 35 +++++++++++++++++++ splitio/lib/split_client.dart | 7 ++-- splitio/lib/splitio.dart | 8 +---- 6 files changed, 109 insertions(+), 16 deletions(-) diff --git a/splitio/lib/channel/method_channel_manager.dart b/splitio/lib/channel/method_channel_manager.dart index d22f580..3e7e571 100644 --- a/splitio/lib/channel/method_channel_manager.dart +++ b/splitio/lib/channel/method_channel_manager.dart @@ -1,5 +1,7 @@ +import 'package:splitio/impressions/split_impression.dart'; import 'package:splitio/method_call_handler.dart'; import 'package:splitio/platform/common_platform.dart'; +import 'package:splitio/split_client.dart'; import 'package:splitio/split_configuration.dart'; import 'package:splitio/split_result.dart'; import 'package:splitio/split_view.dart'; @@ -11,12 +13,12 @@ class MethodChannelManager extends SplitioPlatform { @override void addNativeCallHandler(MethodCallHandler handler) { - _platform.addNativeCallHandler(handler); + _platform.addNativeCallHandler(handler); // TODO } @override void removeNativeCallHandler(MethodCallHandler handler) { - _platform.removeNativeCallHandler(handler); + _platform.removeNativeCallHandler(handler); // TODO } @override @@ -206,4 +208,33 @@ class MethodChannelManager extends SplitioPlatform { value: value, properties: properties); } + + @override + Future onReady( + {required String matchingKey, required String? bucketingKey}) { + throw UnimplementedError(); + } + + @override + Future onReadyFromCache( + {required String matchingKey, required String? bucketingKey}) { + throw UnimplementedError(); + } + + @override + Stream onUpdated( + {required String matchingKey, required String? bucketingKey}) { + throw UnimplementedError(); + } + + @override + Future onTimeout( + {required String matchingKey, required String? bucketingKey}) { + throw UnimplementedError(); + } + + @override + Stream impressionsStream() { + return _platform.impressionsStream(); + } } diff --git a/splitio/lib/events/split_events_listener.dart b/splitio/lib/events/split_events_listener.dart index 72a1834..117f55a 100644 --- a/splitio/lib/events/split_events_listener.dart +++ b/splitio/lib/events/split_events_listener.dart @@ -19,9 +19,12 @@ abstract class SplitEventsListener { class DefaultEventsListener implements SplitEventsListener { final MethodChannelManager _methodChannelManager; - final SplitEventMethodCallHandler _methodCallHandler; + late final SplitEventMethodCallHandler _methodCallHandler; - DefaultEventsListener(this._methodChannelManager, this._methodCallHandler) { + DefaultEventsListener(this._methodChannelManager, String matchingKey, + String? bucketingKey, SplitClient splitClient) { + _methodCallHandler = + SplitEventMethodCallHandler(matchingKey, bucketingKey, splitClient); _methodChannelManager.addNativeCallHandler(_methodCallHandler); } diff --git a/splitio/lib/platform/common_platform.dart b/splitio/lib/platform/common_platform.dart index 656af36..61185c7 100644 --- a/splitio/lib/platform/common_platform.dart +++ b/splitio/lib/platform/common_platform.dart @@ -1,6 +1,8 @@ import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'package:splitio/impressions/split_impression.dart'; import 'package:splitio/method_call_handler.dart'; import 'package:splitio/platform/method_channel_platform.dart'; +import 'package:splitio/split_client.dart'; import 'package:splitio/split_configuration.dart'; import 'package:splitio/split_result.dart'; import 'package:splitio/split_view.dart'; @@ -36,6 +38,10 @@ abstract class _FactoryPlatform { {required String matchingKey, required String? bucketingKey}) { throw UnimplementedError(); } + + Stream impressionsStream() { + throw UnimplementedError(); + } } abstract class _ClientPlatform { @@ -129,8 +135,35 @@ abstract class _ClientPlatform { Map properties = const {}}) { throw UnimplementedError(); } + + Future onReady( + {required String matchingKey, required String? bucketingKey}) { + throw UnimplementedError(); + } + + Future onReadyFromCache( + {required String matchingKey, required String? bucketingKey}) { + throw UnimplementedError(); + } + + Stream onUpdated( + {required String matchingKey, required String? bucketingKey}) { + throw UnimplementedError(); + } + + Future onTimeout( + {required String matchingKey, required String? bucketingKey}) { + throw UnimplementedError(); + } } +/// The interface that implementations of splitio must implement. +/// +/// Platform implementations should extend this class rather than implement it as `splitio` +/// does not consider newly added methods to be breaking changes. Extending this class +/// (using `extends`) ensures that the subclass will get the default implementation, while +/// platform implementations that `implements` this interface will be broken by newly added +/// [SplitioPlatform] methods. abstract class SplitioPlatform extends PlatformInterface with _FactoryPlatform, _ClientPlatform { SplitioPlatform() : super(token: _token); diff --git a/splitio/lib/platform/method_channel_platform.dart b/splitio/lib/platform/method_channel_platform.dart index 9eeac6a..a9fd155 100644 --- a/splitio/lib/platform/method_channel_platform.dart +++ b/splitio/lib/platform/method_channel_platform.dart @@ -1,6 +1,9 @@ import 'package:flutter/services.dart'; +import 'package:splitio/impressions/impressions_method_call_handler.dart'; +import 'package:splitio/impressions/split_impression.dart'; import 'package:splitio/method_call_handler.dart'; import 'package:splitio/platform/common_platform.dart'; +import 'package:splitio/split_client.dart'; import 'package:splitio/split_configuration.dart'; import 'package:splitio/split_result.dart'; import 'package:splitio/split_view.dart'; @@ -14,7 +17,10 @@ class MethodChannelPlatform extends SplitioPlatform { final Set _handlers = {}; + final ImpressionsMethodCallHandler _impressionsMethodCallHandler = ImpressionsMethodCallHandler(); + MethodChannelPlatform() { + _handlers.add(_impressionsMethodCallHandler); _methodChannel.setMethodCallHandler((call) => handle(call)); } @@ -286,4 +292,33 @@ class MethodChannelPlatform extends SplitioPlatform { return result; } + + @override + Future onReady( + {required String matchingKey, required String? bucketingKey}) { + throw UnimplementedError(); + } + + @override + Future onReadyFromCache( + {required String matchingKey, required String? bucketingKey}) { + throw UnimplementedError(); + } + + @override + Stream onUpdated( + {required String matchingKey, required String? bucketingKey}) { + throw UnimplementedError(); + } + + @override + Future onTimeout( + {required String matchingKey, required String? bucketingKey}) { + throw UnimplementedError(); + } + + @override + Stream impressionsStream() { + return _impressionsMethodCallHandler.stream(); + } } diff --git a/splitio/lib/split_client.dart b/splitio/lib/split_client.dart index 74814c3..368620b 100644 --- a/splitio/lib/split_client.dart +++ b/splitio/lib/split_client.dart @@ -1,7 +1,6 @@ import 'package:flutter/foundation.dart'; import 'package:splitio/channel/method_channel_manager.dart'; import 'package:splitio/events/split_events_listener.dart'; -import 'package:splitio/events/split_method_call_handler.dart'; import 'package:splitio/split_result.dart'; abstract class SplitClient { @@ -150,10 +149,8 @@ class DefaultSplitClient implements SplitClient { DefaultSplitClient( this._methodChannelManager, this._matchingKey, this._bucketingKey) { - SplitEventMethodCallHandler _methodCallHandler = - SplitEventMethodCallHandler(_matchingKey, _bucketingKey, this); - _splitEventsListener = - DefaultEventsListener(_methodChannelManager, _methodCallHandler); + _splitEventsListener = DefaultEventsListener( + _methodChannelManager, _matchingKey, _bucketingKey, this); } @visibleForTesting diff --git a/splitio/lib/splitio.dart b/splitio/lib/splitio.dart index bd457e2..06e01fe 100644 --- a/splitio/lib/splitio.dart +++ b/splitio/lib/splitio.dart @@ -1,9 +1,7 @@ import 'dart:async'; import 'package:splitio/channel/method_channel_manager.dart'; -import 'package:splitio/impressions/impressions_method_call_handler.dart'; import 'package:splitio/impressions/split_impression.dart'; -import 'package:splitio/method_call_handler.dart'; import 'package:splitio/platform/common_platform.dart'; import 'package:splitio/split_client.dart'; import 'package:splitio/split_configuration.dart'; @@ -27,8 +25,6 @@ class Splitio { late final SplitConfiguration? _splitConfiguration; - late final StreamMethodCallHandler _impressionsMethodCallHandler; - final MethodChannelManager _methodChannelManager = MethodChannelManager(SplitioPlatform.instance); @@ -46,8 +42,6 @@ class Splitio { {String? bucketingKey, SplitConfiguration? configuration}) { _defaultBucketingKey = bucketingKey; _splitConfiguration = configuration; - _impressionsMethodCallHandler = ImpressionsMethodCallHandler(); - _methodChannelManager.addNativeCallHandler(_impressionsMethodCallHandler); _init(); } @@ -123,7 +117,7 @@ class Splitio { /// If the impressionListener configuration has been enabled, /// generated impressions will be streamed here. Stream impressionsStream() { - return _impressionsMethodCallHandler.stream(); + return _methodChannelManager.impressionsStream(); } Future split(String splitName) async { From d2cb220d8761f4012f4caf906d3ecda6edf5a8b7 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Thu, 1 Sep 2022 17:50:38 -0300 Subject: [PATCH 09/17] WIP events --- .../lib/channel/method_channel_manager.dart | 32 ++++------- splitio/lib/events/split_events_listener.dart | 56 ------------------- .../lib/events/split_method_call_handler.dart | 35 ++++-------- splitio/lib/platform/common_platform.dart | 16 ++---- .../lib/platform/method_channel_platform.dart | 53 ++++++++---------- splitio/lib/split_client.dart | 39 +++++++------ splitio/lib/splitio.dart | 45 ++++++++------- 7 files changed, 101 insertions(+), 175 deletions(-) delete mode 100644 splitio/lib/events/split_events_listener.dart diff --git a/splitio/lib/channel/method_channel_manager.dart b/splitio/lib/channel/method_channel_manager.dart index 3e7e571..92e1711 100644 --- a/splitio/lib/channel/method_channel_manager.dart +++ b/splitio/lib/channel/method_channel_manager.dart @@ -1,7 +1,5 @@ import 'package:splitio/impressions/split_impression.dart'; -import 'package:splitio/method_call_handler.dart'; import 'package:splitio/platform/common_platform.dart'; -import 'package:splitio/split_client.dart'; import 'package:splitio/split_configuration.dart'; import 'package:splitio/split_result.dart'; import 'package:splitio/split_view.dart'; @@ -11,16 +9,6 @@ class MethodChannelManager extends SplitioPlatform { MethodChannelManager(this._platform); - @override - void addNativeCallHandler(MethodCallHandler handler) { - _platform.addNativeCallHandler(handler); // TODO - } - - @override - void removeNativeCallHandler(MethodCallHandler handler) { - _platform.removeNativeCallHandler(handler); // TODO - } - @override Future clearAttributes( {required String matchingKey, required String? bucketingKey}) { @@ -210,27 +198,31 @@ class MethodChannelManager extends SplitioPlatform { } @override - Future onReady( + Future? onReady( {required String matchingKey, required String? bucketingKey}) { - throw UnimplementedError(); + return _platform.onReady( + matchingKey: matchingKey, bucketingKey: bucketingKey); } @override - Future onReadyFromCache( + Future? onReadyFromCache( {required String matchingKey, required String? bucketingKey}) { - throw UnimplementedError(); + return _platform.onReadyFromCache( + matchingKey: matchingKey, bucketingKey: bucketingKey); } @override - Stream onUpdated( + Future? onTimeout( {required String matchingKey, required String? bucketingKey}) { - throw UnimplementedError(); + return _platform.onTimeout( + matchingKey: matchingKey, bucketingKey: bucketingKey); } @override - Future onTimeout( + Stream? onUpdated( {required String matchingKey, required String? bucketingKey}) { - throw UnimplementedError(); + return _platform.onUpdated( + matchingKey: matchingKey, bucketingKey: bucketingKey); } @override diff --git a/splitio/lib/events/split_events_listener.dart b/splitio/lib/events/split_events_listener.dart deleted file mode 100644 index 117f55a..0000000 --- a/splitio/lib/events/split_events_listener.dart +++ /dev/null @@ -1,56 +0,0 @@ -import 'dart:async'; - -import 'package:splitio/channel/method_channel_manager.dart'; -import 'package:splitio/events/split_method_call_handler.dart'; -import 'package:splitio/split_client.dart'; - -abstract class SplitEventsListener { - Future onReady(); - - Future onReadyFromCache(); - - Stream onUpdated(); - - Future onTimeout(); - - void destroy(); -} - -class DefaultEventsListener implements SplitEventsListener { - final MethodChannelManager _methodChannelManager; - - late final SplitEventMethodCallHandler _methodCallHandler; - - DefaultEventsListener(this._methodChannelManager, String matchingKey, - String? bucketingKey, SplitClient splitClient) { - _methodCallHandler = - SplitEventMethodCallHandler(matchingKey, bucketingKey, splitClient); - _methodChannelManager.addNativeCallHandler(_methodCallHandler); - } - - @override - Future onReady() { - return _methodCallHandler.onReady(); - } - - @override - Future onReadyFromCache() { - return _methodCallHandler.onReadyFromCache(); - } - - @override - Stream onUpdated() { - return _methodCallHandler.onUpdated(); - } - - @override - Future onTimeout() { - return _methodCallHandler.onTimeout(); - } - - @override - void destroy() { - _methodCallHandler.destroy(); - _methodChannelManager.removeNativeCallHandler(_methodCallHandler); - } -} diff --git a/splitio/lib/events/split_method_call_handler.dart b/splitio/lib/events/split_method_call_handler.dart index 8311633..138d8b3 100644 --- a/splitio/lib/events/split_method_call_handler.dart +++ b/splitio/lib/events/split_method_call_handler.dart @@ -1,11 +1,8 @@ import 'dart:async'; -import 'package:splitio/events/split_events_listener.dart'; import 'package:splitio/method_call_handler.dart'; -import 'package:splitio/split_client.dart'; -class SplitEventMethodCallHandler - implements MethodCallHandler, SplitEventsListener { +class SplitEventMethodCallHandler implements MethodCallHandler { static const String _eventClientReady = 'clientReady'; static const String _eventClientReadyFromCache = 'clientReadyFromCache'; static const String _eventClientTimeout = 'clientTimeout'; @@ -13,11 +10,9 @@ class SplitEventMethodCallHandler final String _matchingKey; final String? _bucketingKey; - final SplitClient _splitClient; - final StreamController _updateStreamCompleter = - StreamController(); + final StreamController _updateStreamCompleter = StreamController(); - final Map> _clientEventCallbacks = { + final Map> _clientEventCallbacks = { _eventClientReady: Completer(), _eventClientReadyFromCache: Completer(), _eventClientTimeout: Completer(), @@ -29,8 +24,7 @@ class SplitEventMethodCallHandler _eventClientTimeout: false, }; - SplitEventMethodCallHandler( - this._matchingKey, this._bucketingKey, this._splitClient); + SplitEventMethodCallHandler(this._matchingKey, this._bucketingKey); @override Future handle(String methodName, dynamic methodArguments) async { @@ -39,7 +33,7 @@ class SplitEventMethodCallHandler _bucketingKey == methodArguments['bucketingKey']) { var clientEventCallback = _clientEventCallbacks[methodName]; if (clientEventCallback != null && !clientEventCallback.isCompleted) { - clientEventCallback.complete(_splitClient); + clientEventCallback.complete(); } if (_triggeredClientEvents.containsKey(methodName)) { @@ -50,39 +44,34 @@ class SplitEventMethodCallHandler _updateStreamCompleter.hasListener && !_updateStreamCompleter.isPaused && !_updateStreamCompleter.isClosed) { - _updateStreamCompleter.add(_splitClient); + _updateStreamCompleter.add(null); } } - @override - Future onReady() { + Future onReady() { return _onEvent(_eventClientReady); } - @override - Future onReadyFromCache() { + Future onReadyFromCache() { return _onEvent(_eventClientReadyFromCache); } - @override - Stream onUpdated() { + Stream onUpdated() { return _updateStreamCompleter.stream; } - @override - Future onTimeout() { + Future onTimeout() { return _onEvent(_eventClientTimeout); } - @override void destroy() { _updateStreamCompleter.close(); } - Future _onEvent(String sdkEvent) { + Future _onEvent(String sdkEvent) { if (_triggeredClientEvents.containsKey(sdkEvent) && _triggeredClientEvents[sdkEvent] == true) { - return Future.value(_splitClient); + return Future.value(); } return (_clientEventCallbacks[sdkEvent])?.future ?? Future.error(Object()); diff --git a/splitio/lib/platform/common_platform.dart b/splitio/lib/platform/common_platform.dart index 61185c7..a1f7a14 100644 --- a/splitio/lib/platform/common_platform.dart +++ b/splitio/lib/platform/common_platform.dart @@ -1,8 +1,8 @@ +import 'dart:async'; + import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import 'package:splitio/impressions/split_impression.dart'; -import 'package:splitio/method_call_handler.dart'; import 'package:splitio/platform/method_channel_platform.dart'; -import 'package:splitio/split_client.dart'; import 'package:splitio/split_configuration.dart'; import 'package:splitio/split_result.dart'; import 'package:splitio/split_view.dart'; @@ -136,22 +136,22 @@ abstract class _ClientPlatform { throw UnimplementedError(); } - Future onReady( + Future? onReady( {required String matchingKey, required String? bucketingKey}) { throw UnimplementedError(); } - Future onReadyFromCache( + Future? onReadyFromCache( {required String matchingKey, required String? bucketingKey}) { throw UnimplementedError(); } - Stream onUpdated( + Stream? onUpdated( {required String matchingKey, required String? bucketingKey}) { throw UnimplementedError(); } - Future onTimeout( + Future? onTimeout( {required String matchingKey, required String? bucketingKey}) { throw UnimplementedError(); } @@ -180,8 +180,4 @@ abstract class SplitioPlatform extends PlatformInterface PlatformInterface.verify(instance, _token); _instance = instance; } - - void addNativeCallHandler(MethodCallHandler handler); //TODO - - void removeNativeCallHandler(MethodCallHandler handler); //TODO } diff --git a/splitio/lib/platform/method_channel_platform.dart b/splitio/lib/platform/method_channel_platform.dart index a9fd155..48be14d 100644 --- a/splitio/lib/platform/method_channel_platform.dart +++ b/splitio/lib/platform/method_channel_platform.dart @@ -1,9 +1,9 @@ import 'package:flutter/services.dart'; +import 'package:splitio/events/split_method_call_handler.dart'; import 'package:splitio/impressions/impressions_method_call_handler.dart'; import 'package:splitio/impressions/split_impression.dart'; import 'package:splitio/method_call_handler.dart'; import 'package:splitio/platform/common_platform.dart'; -import 'package:splitio/split_client.dart'; import 'package:splitio/split_configuration.dart'; import 'package:splitio/split_result.dart'; import 'package:splitio/split_view.dart'; @@ -15,31 +15,21 @@ class MethodChannelPlatform extends SplitioPlatform { final MethodChannel _methodChannel = const MethodChannel('splitio'); - final Set _handlers = {}; + final Map _handlers = {}; final ImpressionsMethodCallHandler _impressionsMethodCallHandler = ImpressionsMethodCallHandler(); MethodChannelPlatform() { - _handlers.add(_impressionsMethodCallHandler); _methodChannel.setMethodCallHandler((call) => handle(call)); } Future handle(MethodCall call) async { - for (MethodCallHandler handler in _handlers) { + _impressionsMethodCallHandler.handle(call.method, call.arguments); + for (MethodCallHandler handler in _handlers.values) { handler.handle(call.method, call.arguments); } } - @override - void addNativeCallHandler(MethodCallHandler handler) { - _handlers.add(handler); - } - - @override - void removeNativeCallHandler(MethodCallHandler handler) { - _handlers.remove(handler); - } - @override Future init({required String apiKey, required String matchingKey, @@ -58,6 +48,16 @@ class MethodChannelPlatform extends SplitioPlatform { return _methodChannel.invokeMethod('init', arguments); } + @override + Future getClient( + {required String matchingKey, required String? bucketingKey}) { + _handlers.addAll( + {'${matchingKey}_$bucketingKey': SplitEventMethodCallHandler(matchingKey, bucketingKey)}); + + return _methodChannel.invokeMethod( + 'getClient', _buildParameters(matchingKey, bucketingKey)); + } + @override Future clearAttributes( {required String matchingKey, required String? bucketingKey}) async { @@ -68,6 +68,8 @@ class MethodChannelPlatform extends SplitioPlatform { @override Future destroy( {required String matchingKey, required String? bucketingKey}) async { + _handlers['${matchingKey}_$bucketingKey']?.destroy(); + _handlers.remove('${matchingKey}_$bucketingKey'); return await _methodChannel.invokeMethod( 'destroy', _buildParameters(matchingKey, bucketingKey)); } @@ -97,13 +99,6 @@ class MethodChannelPlatform extends SplitioPlatform { matchingKey, bucketingKey, {'attributeName': attributeName})); } - @override - Future getClient( - {required String matchingKey, required String? bucketingKey}) { - return _methodChannel.invokeMethod( - 'getClient', _buildParameters(matchingKey, bucketingKey)); - } - @override Future getTreatment({required String matchingKey, required String? bucketingKey, @@ -294,27 +289,27 @@ class MethodChannelPlatform extends SplitioPlatform { } @override - Future onReady( + Future? onReady( {required String matchingKey, required String? bucketingKey}) { - throw UnimplementedError(); + return _handlers['${matchingKey}_$bucketingKey']?.onReady(); } @override - Future onReadyFromCache( + Future? onReadyFromCache( {required String matchingKey, required String? bucketingKey}) { - throw UnimplementedError(); + return _handlers['${matchingKey}_$bucketingKey']?.onReadyFromCache(); } @override - Stream onUpdated( + Stream? onUpdated( {required String matchingKey, required String? bucketingKey}) { - throw UnimplementedError(); + return _handlers['${matchingKey}_$bucketingKey']?.onUpdated(); } @override - Future onTimeout( + Future? onTimeout( {required String matchingKey, required String? bucketingKey}) { - throw UnimplementedError(); + return _handlers['${matchingKey}_$bucketingKey']?.onTimeout(); } @override diff --git a/splitio/lib/split_client.dart b/splitio/lib/split_client.dart index 368620b..ae196b1 100644 --- a/splitio/lib/split_client.dart +++ b/splitio/lib/split_client.dart @@ -1,6 +1,5 @@ import 'package:flutter/foundation.dart'; import 'package:splitio/channel/method_channel_manager.dart'; -import 'package:splitio/events/split_events_listener.dart'; import 'package:splitio/split_result.dart'; abstract class SplitClient { @@ -145,17 +144,12 @@ class DefaultSplitClient implements SplitClient { final String _matchingKey; final String? _bucketingKey; - late final SplitEventsListener _splitEventsListener; - DefaultSplitClient( - this._methodChannelManager, this._matchingKey, this._bucketingKey) { - _splitEventsListener = DefaultEventsListener( - _methodChannelManager, _matchingKey, _bucketingKey, this); - } + this._methodChannelManager, this._matchingKey, this._bucketingKey); @visibleForTesting - DefaultSplitClient.withEventListener(this._methodChannelManager, - this._matchingKey, this._bucketingKey, this._splitEventsListener); + DefaultSplitClient.withEventListener( + this._methodChannelManager, this._matchingKey, this._bucketingKey); @override Future getTreatment(String splitName, @@ -265,28 +259,39 @@ class DefaultSplitClient implements SplitClient { @override Future destroy() async { - _splitEventsListener.destroy(); return _methodChannelManager.destroy( matchingKey: _matchingKey, bucketingKey: _bucketingKey); } @override - Future whenReady() { - return _splitEventsListener.onReady(); + Future whenReady() async { + await _methodChannelManager.onReady( + matchingKey: _matchingKey, bucketingKey: _bucketingKey); + + return Future.value(this); } @override - Future whenReadyFromCache() { - return _splitEventsListener.onReadyFromCache(); + Future whenReadyFromCache() async { + await _methodChannelManager.onReadyFromCache( + matchingKey: _matchingKey, bucketingKey: _bucketingKey); + + return Future.value(this); } @override Stream whenUpdated() { - return _splitEventsListener.onUpdated(); + return _methodChannelManager + .onUpdated(matchingKey: _matchingKey, bucketingKey: _bucketingKey) + ?.map((event) => this) ?? + const Stream.empty(); } @override - Future whenTimeout() { - return _splitEventsListener.onTimeout(); + Future whenTimeout() async { + await _methodChannelManager.onTimeout( + matchingKey: _matchingKey, bucketingKey: _bucketingKey); + + return Future.value(this); } } diff --git a/splitio/lib/splitio.dart b/splitio/lib/splitio.dart index 06e01fe..99b1249 100644 --- a/splitio/lib/splitio.dart +++ b/splitio/lib/splitio.dart @@ -76,30 +76,35 @@ class Splitio { ClientReadinessCallback? onUpdated, ClientReadinessCallback? onTimeout}) { String? key = matchingKey ?? _defaultMatchingKey; - - var client = DefaultSplitClient(_methodChannelManager, key, bucketingKey); - if (onReady != null) { - client.whenReady().then((client) => onReady.call(client)); - } - - if (onReadyFromCache != null) { - client - .whenReadyFromCache() - .then((client) => onReadyFromCache.call(client)); - } - - if (onTimeout != null) { - client.whenTimeout().then((client) => onTimeout.call(client)); - } - - if (onUpdated != null) { - client.whenUpdated().listen((client) => onUpdated.call(client)); - } + // + // if (onReady != null) { + // _methodChannelManager + // .onReady(matchingKey: key, bucketingKey: bucketingKey)? + // .then((client) => onReady.call(client)); + // } + // + // if (onReadyFromCache != null) { + // _methodChannelManager + // .onReadyFromCache(matchingKey: key, bucketingKey: bucketingKey) + // .then((client) => onReadyFromCache.call(client)); + // } + // + // if (onTimeout != null) { + // _methodChannelManager + // .onTimeout(matchingKey: key, bucketingKey: bucketingKey) + // .then((client) => onTimeout.call(client)); + // } + // + // if (onUpdated != null) { + // _methodChannelManager + // .onUpdated(matchingKey: key, bucketingKey: bucketingKey) + // .listen((client) => onUpdated.call(client)); + // } _methodChannelManager.getClient( matchingKey: key, bucketingKey: bucketingKey); - return client; + return DefaultSplitClient(_methodChannelManager, key, bucketingKey); } Future> splitNames() async { From 514fdf28207d4a0ccc573f81dcabebb96be5f792 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Thu, 1 Sep 2022 18:02:37 -0300 Subject: [PATCH 10/17] Set up callbacks --- splitio/lib/splitio.dart | 52 ++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/splitio/lib/splitio.dart b/splitio/lib/splitio.dart index 99b1249..2611180 100644 --- a/splitio/lib/splitio.dart +++ b/splitio/lib/splitio.dart @@ -76,35 +76,35 @@ class Splitio { ClientReadinessCallback? onUpdated, ClientReadinessCallback? onTimeout}) { String? key = matchingKey ?? _defaultMatchingKey; - // - // if (onReady != null) { - // _methodChannelManager - // .onReady(matchingKey: key, bucketingKey: bucketingKey)? - // .then((client) => onReady.call(client)); - // } - // - // if (onReadyFromCache != null) { - // _methodChannelManager - // .onReadyFromCache(matchingKey: key, bucketingKey: bucketingKey) - // .then((client) => onReadyFromCache.call(client)); - // } - // - // if (onTimeout != null) { - // _methodChannelManager - // .onTimeout(matchingKey: key, bucketingKey: bucketingKey) - // .then((client) => onTimeout.call(client)); - // } - // - // if (onUpdated != null) { - // _methodChannelManager - // .onUpdated(matchingKey: key, bucketingKey: bucketingKey) - // .listen((client) => onUpdated.call(client)); - // } - _methodChannelManager.getClient( matchingKey: key, bucketingKey: bucketingKey); - return DefaultSplitClient(_methodChannelManager, key, bucketingKey); + var client = DefaultSplitClient(_methodChannelManager, key, bucketingKey); + if (onReady != null) { + _methodChannelManager + .onReady(matchingKey: key, bucketingKey: bucketingKey) + ?.then((val) => onReady.call(client)); + } + + if (onReadyFromCache != null) { + _methodChannelManager + .onReadyFromCache(matchingKey: key, bucketingKey: bucketingKey) + ?.then((val) => onReadyFromCache.call(client)); + } + + if (onTimeout != null) { + _methodChannelManager + .onTimeout(matchingKey: key, bucketingKey: bucketingKey) + ?.then((val) => onTimeout.call(client)); + } + + if (onUpdated != null) { + _methodChannelManager + .onUpdated(matchingKey: key, bucketingKey: bucketingKey) + ?.listen((event) => onUpdated.call(client)); + } + + return client; } Future> splitNames() async { From 94b4312461aab4504e7b848ac47764b5d218fe4b Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Thu, 1 Sep 2022 18:17:42 -0300 Subject: [PATCH 11/17] Remove unnecessary class --- .../lib/channel/method_channel_manager.dart | 232 ------------------ .../lib/platform/method_channel_platform.dart | 26 +- splitio/lib/split_client.dart | 43 ++-- splitio/lib/splitio.dart | 27 +- 4 files changed, 52 insertions(+), 276 deletions(-) delete mode 100644 splitio/lib/channel/method_channel_manager.dart diff --git a/splitio/lib/channel/method_channel_manager.dart b/splitio/lib/channel/method_channel_manager.dart deleted file mode 100644 index 92e1711..0000000 --- a/splitio/lib/channel/method_channel_manager.dart +++ /dev/null @@ -1,232 +0,0 @@ -import 'package:splitio/impressions/split_impression.dart'; -import 'package:splitio/platform/common_platform.dart'; -import 'package:splitio/split_configuration.dart'; -import 'package:splitio/split_result.dart'; -import 'package:splitio/split_view.dart'; - -class MethodChannelManager extends SplitioPlatform { - final SplitioPlatform _platform; - - MethodChannelManager(this._platform); - - @override - Future clearAttributes( - {required String matchingKey, required String? bucketingKey}) { - return _platform.clearAttributes( - matchingKey: matchingKey, bucketingKey: bucketingKey); - } - - @override - Future destroy( - {required String matchingKey, required String? bucketingKey}) { - return _platform.destroy( - matchingKey: matchingKey, bucketingKey: bucketingKey); - } - - @override - Future flush( - {required String matchingKey, required String? bucketingKey}) { - return _platform.flush( - matchingKey: matchingKey, bucketingKey: bucketingKey); - } - - @override - Future> getAllAttributes( - {required String matchingKey, required String? bucketingKey}) { - return _platform.getAllAttributes( - matchingKey: matchingKey, bucketingKey: bucketingKey); - } - - @override - Future getAttribute( - {required String matchingKey, - required String attributeName, - required String? bucketingKey}) { - return _platform.getAttribute( - matchingKey: matchingKey, - bucketingKey: bucketingKey, - attributeName: attributeName); - } - - @override - Future getClient({required String matchingKey, String? bucketingKey}) { - return _platform.getClient( - matchingKey: matchingKey, bucketingKey: bucketingKey); - } - - @override - Future getTreatment( - {required String matchingKey, - required String? bucketingKey, - required String splitName, - Map attributes = const {}}) { - return _platform.getTreatment( - matchingKey: matchingKey, - bucketingKey: bucketingKey, - splitName: splitName, - attributes: attributes); - } - - @override - Future getTreatmentWithConfig( - {required String matchingKey, - required String? bucketingKey, - required String splitName, - Map attributes = const {}}) { - return _platform.getTreatmentWithConfig( - matchingKey: matchingKey, - bucketingKey: bucketingKey, - splitName: splitName, - attributes: attributes); - } - - @override - Future> getTreatments( - {required String matchingKey, - required String? bucketingKey, - required List splitNames, - Map attributes = const {}}) { - return _platform.getTreatments( - matchingKey: matchingKey, - bucketingKey: bucketingKey, - splitNames: splitNames, - attributes: attributes); - } - - @override - Future> getTreatmentsWithConfig( - {required String matchingKey, - required String? bucketingKey, - required List splitNames, - Map attributes = const {}}) { - return _platform.getTreatmentsWithConfig( - matchingKey: matchingKey, - bucketingKey: bucketingKey, - splitNames: splitNames, - attributes: attributes); - } - - @override - Future init( - {required String apiKey, - required String matchingKey, - required String? bucketingKey, - SplitConfiguration? sdkConfiguration}) { - return _platform.init( - apiKey: apiKey, - matchingKey: matchingKey, - bucketingKey: bucketingKey, - sdkConfiguration: sdkConfiguration); - } - - @override - Future removeAttribute( - {required String matchingKey, - required String? bucketingKey, - required String attributeName}) { - return _platform.removeAttribute( - matchingKey: matchingKey, - bucketingKey: bucketingKey, - attributeName: attributeName); - } - - @override - Future setAttribute({ - required String matchingKey, - required String? bucketingKey, - required String attributeName, - required value, - }) { - return _platform.setAttribute( - matchingKey: matchingKey, - bucketingKey: bucketingKey, - attributeName: attributeName, - value: value); - } - - @override - Future setAttributes( - {required String matchingKey, - required String? bucketingKey, - required Map attributes}) { - return _platform.setAttributes( - matchingKey: matchingKey, - bucketingKey: bucketingKey, - attributes: attributes); - } - - @override - Future split( - {required String matchingKey, - required String? bucketingKey, - required String splitName}) { - return _platform.split( - matchingKey: matchingKey, - bucketingKey: bucketingKey, - splitName: splitName); - } - - @override - Future> splitNames( - {required String matchingKey, required String? bucketingKey}) { - return _platform.splitNames( - matchingKey: matchingKey, bucketingKey: bucketingKey); - } - - @override - Future> splits( - {required String matchingKey, required String? bucketingKey}) { - return _platform.splits( - matchingKey: matchingKey, bucketingKey: bucketingKey); - } - - @override - Future track( - {required String matchingKey, - required String? bucketingKey, - required String eventType, - String? trafficType, - double? value, - Map properties = const {}}) { - return _platform.track( - matchingKey: matchingKey, - bucketingKey: bucketingKey, - eventType: eventType, - trafficType: trafficType, - value: value, - properties: properties); - } - - @override - Future? onReady( - {required String matchingKey, required String? bucketingKey}) { - return _platform.onReady( - matchingKey: matchingKey, bucketingKey: bucketingKey); - } - - @override - Future? onReadyFromCache( - {required String matchingKey, required String? bucketingKey}) { - return _platform.onReadyFromCache( - matchingKey: matchingKey, bucketingKey: bucketingKey); - } - - @override - Future? onTimeout( - {required String matchingKey, required String? bucketingKey}) { - return _platform.onTimeout( - matchingKey: matchingKey, bucketingKey: bucketingKey); - } - - @override - Stream? onUpdated( - {required String matchingKey, required String? bucketingKey}) { - return _platform.onUpdated( - matchingKey: matchingKey, bucketingKey: bucketingKey); - } - - @override - Stream impressionsStream() { - return _platform.impressionsStream(); - } -} diff --git a/splitio/lib/platform/method_channel_platform.dart b/splitio/lib/platform/method_channel_platform.dart index 48be14d..b959237 100644 --- a/splitio/lib/platform/method_channel_platform.dart +++ b/splitio/lib/platform/method_channel_platform.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:splitio/events/split_method_call_handler.dart'; import 'package:splitio/impressions/impressions_method_call_handler.dart'; @@ -23,6 +24,7 @@ class MethodChannelPlatform extends SplitioPlatform { _methodChannel.setMethodCallHandler((call) => handle(call)); } + @visibleForTesting Future handle(MethodCall call) async { _impressionsMethodCallHandler.handle(call.method, call.arguments); for (MethodCallHandler handler in _handlers.values) { @@ -52,7 +54,10 @@ class MethodChannelPlatform extends SplitioPlatform { Future getClient( {required String matchingKey, required String? bucketingKey}) { _handlers.addAll( - {'${matchingKey}_$bucketingKey': SplitEventMethodCallHandler(matchingKey, bucketingKey)}); + { + _buildMapKey(matchingKey, bucketingKey): SplitEventMethodCallHandler( + matchingKey, bucketingKey) + }); return _methodChannel.invokeMethod( 'getClient', _buildParameters(matchingKey, bucketingKey)); @@ -68,8 +73,10 @@ class MethodChannelPlatform extends SplitioPlatform { @override Future destroy( {required String matchingKey, required String? bucketingKey}) async { - _handlers['${matchingKey}_$bucketingKey']?.destroy(); - _handlers.remove('${matchingKey}_$bucketingKey'); + var handlerKey = _buildMapKey(matchingKey, bucketingKey); + _handlers[handlerKey]?.destroy(); + _handlers.remove(handlerKey); + return await _methodChannel.invokeMethod( 'destroy', _buildParameters(matchingKey, bucketingKey)); } @@ -291,29 +298,34 @@ class MethodChannelPlatform extends SplitioPlatform { @override Future? onReady( {required String matchingKey, required String? bucketingKey}) { - return _handlers['${matchingKey}_$bucketingKey']?.onReady(); + return _handlers[_buildMapKey(matchingKey, bucketingKey)]?.onReady(); } @override Future? onReadyFromCache( {required String matchingKey, required String? bucketingKey}) { - return _handlers['${matchingKey}_$bucketingKey']?.onReadyFromCache(); + return _handlers[_buildMapKey(matchingKey, bucketingKey)] + ?.onReadyFromCache(); } @override Stream? onUpdated( {required String matchingKey, required String? bucketingKey}) { - return _handlers['${matchingKey}_$bucketingKey']?.onUpdated(); + return _handlers[_buildMapKey(matchingKey, bucketingKey)]?.onUpdated(); } @override Future? onTimeout( {required String matchingKey, required String? bucketingKey}) { - return _handlers['${matchingKey}_$bucketingKey']?.onTimeout(); + return _handlers[_buildMapKey(matchingKey, bucketingKey)]?.onTimeout(); } @override Stream impressionsStream() { return _impressionsMethodCallHandler.stream(); } + + String _buildMapKey(String matchingKey, String? bucketingKey) { + return '${matchingKey}_$bucketingKey'; + } } diff --git a/splitio/lib/split_client.dart b/splitio/lib/split_client.dart index ae196b1..23f4c43 100644 --- a/splitio/lib/split_client.dart +++ b/splitio/lib/split_client.dart @@ -1,5 +1,5 @@ import 'package:flutter/foundation.dart'; -import 'package:splitio/channel/method_channel_manager.dart'; +import 'package:splitio/platform/common_platform.dart'; import 'package:splitio/split_result.dart'; abstract class SplitClient { @@ -140,21 +140,20 @@ abstract class SplitClient { } class DefaultSplitClient implements SplitClient { - final MethodChannelManager _methodChannelManager; + final SplitioPlatform _platform; final String _matchingKey; final String? _bucketingKey; - DefaultSplitClient( - this._methodChannelManager, this._matchingKey, this._bucketingKey); + DefaultSplitClient(this._platform, this._matchingKey, this._bucketingKey); @visibleForTesting DefaultSplitClient.withEventListener( - this._methodChannelManager, this._matchingKey, this._bucketingKey); + this._platform, this._matchingKey, this._bucketingKey); @override Future getTreatment(String splitName, [Map attributes = const {}]) async { - return _methodChannelManager.getTreatment( + return _platform.getTreatment( matchingKey: _matchingKey, bucketingKey: _bucketingKey, splitName: splitName, @@ -164,7 +163,7 @@ class DefaultSplitClient implements SplitClient { @override Future getTreatmentWithConfig(String splitName, [Map attributes = const {}]) async { - return _methodChannelManager.getTreatmentWithConfig( + return _platform.getTreatmentWithConfig( matchingKey: _matchingKey, bucketingKey: _bucketingKey, splitName: splitName, @@ -174,7 +173,7 @@ class DefaultSplitClient implements SplitClient { @override Future> getTreatments(List splitNames, [Map attributes = const {}]) async { - return _methodChannelManager.getTreatments( + return _platform.getTreatments( matchingKey: _matchingKey, bucketingKey: _bucketingKey, splitNames: splitNames, @@ -185,7 +184,7 @@ class DefaultSplitClient implements SplitClient { Future> getTreatmentsWithConfig( List splitNames, [Map attributes = const {}]) async { - return _methodChannelManager.getTreatmentsWithConfig( + return _platform.getTreatmentsWithConfig( matchingKey: _matchingKey, bucketingKey: _bucketingKey, splitNames: splitNames, @@ -197,7 +196,7 @@ class DefaultSplitClient implements SplitClient { {String? trafficType, double? value, Map properties = const {}}) async { - return _methodChannelManager.track( + return _platform.track( matchingKey: _matchingKey, bucketingKey: _bucketingKey, eventType: eventType, @@ -208,7 +207,7 @@ class DefaultSplitClient implements SplitClient { @override Future setAttribute(String attributeName, dynamic value) async { - return _methodChannelManager.setAttribute( + return _platform.setAttribute( matchingKey: _matchingKey, bucketingKey: _bucketingKey, attributeName: attributeName, @@ -217,7 +216,7 @@ class DefaultSplitClient implements SplitClient { @override Future getAttribute(String attributeName) async { - return _methodChannelManager.getAttribute( + return _platform.getAttribute( matchingKey: _matchingKey, bucketingKey: _bucketingKey, attributeName: attributeName); @@ -225,7 +224,7 @@ class DefaultSplitClient implements SplitClient { @override Future setAttributes(Map attributes) async { - return _methodChannelManager.setAttributes( + return _platform.setAttributes( matchingKey: _matchingKey, bucketingKey: _bucketingKey, attributes: attributes); @@ -233,13 +232,13 @@ class DefaultSplitClient implements SplitClient { @override Future> getAttributes() async { - return _methodChannelManager.getAllAttributes( + return _platform.getAllAttributes( matchingKey: _matchingKey, bucketingKey: _bucketingKey); } @override Future removeAttribute(String attributeName) async { - return _methodChannelManager.removeAttribute( + return _platform.removeAttribute( matchingKey: _matchingKey, bucketingKey: _bucketingKey, attributeName: attributeName); @@ -247,25 +246,25 @@ class DefaultSplitClient implements SplitClient { @override Future clearAttributes() async { - return _methodChannelManager.clearAttributes( + return _platform.clearAttributes( matchingKey: _matchingKey, bucketingKey: _bucketingKey); } @override Future flush() async { - return _methodChannelManager.flush( + return _platform.flush( matchingKey: _matchingKey, bucketingKey: _bucketingKey); } @override Future destroy() async { - return _methodChannelManager.destroy( + return _platform.destroy( matchingKey: _matchingKey, bucketingKey: _bucketingKey); } @override Future whenReady() async { - await _methodChannelManager.onReady( + await _platform.onReady( matchingKey: _matchingKey, bucketingKey: _bucketingKey); return Future.value(this); @@ -273,7 +272,7 @@ class DefaultSplitClient implements SplitClient { @override Future whenReadyFromCache() async { - await _methodChannelManager.onReadyFromCache( + await _platform.onReadyFromCache( matchingKey: _matchingKey, bucketingKey: _bucketingKey); return Future.value(this); @@ -281,7 +280,7 @@ class DefaultSplitClient implements SplitClient { @override Stream whenUpdated() { - return _methodChannelManager + return _platform .onUpdated(matchingKey: _matchingKey, bucketingKey: _bucketingKey) ?.map((event) => this) ?? const Stream.empty(); @@ -289,7 +288,7 @@ class DefaultSplitClient implements SplitClient { @override Future whenTimeout() async { - await _methodChannelManager.onTimeout( + await _platform.onTimeout( matchingKey: _matchingKey, bucketingKey: _bucketingKey); return Future.value(this); diff --git a/splitio/lib/splitio.dart b/splitio/lib/splitio.dart index 2611180..7d8ddd0 100644 --- a/splitio/lib/splitio.dart +++ b/splitio/lib/splitio.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:splitio/channel/method_channel_manager.dart'; import 'package:splitio/impressions/split_impression.dart'; import 'package:splitio/platform/common_platform.dart'; import 'package:splitio/split_client.dart'; @@ -25,8 +24,7 @@ class Splitio { late final SplitConfiguration? _splitConfiguration; - final MethodChannelManager _methodChannelManager = - MethodChannelManager(SplitioPlatform.instance); + final SplitioPlatform _platform = SplitioPlatform.instance; /// SDK instance constructor. /// @@ -76,30 +74,29 @@ class Splitio { ClientReadinessCallback? onUpdated, ClientReadinessCallback? onTimeout}) { String? key = matchingKey ?? _defaultMatchingKey; - _methodChannelManager.getClient( - matchingKey: key, bucketingKey: bucketingKey); + _platform.getClient(matchingKey: key, bucketingKey: bucketingKey); - var client = DefaultSplitClient(_methodChannelManager, key, bucketingKey); + var client = DefaultSplitClient(_platform, key, bucketingKey); if (onReady != null) { - _methodChannelManager + _platform .onReady(matchingKey: key, bucketingKey: bucketingKey) ?.then((val) => onReady.call(client)); } if (onReadyFromCache != null) { - _methodChannelManager + _platform .onReadyFromCache(matchingKey: key, bucketingKey: bucketingKey) ?.then((val) => onReadyFromCache.call(client)); } if (onTimeout != null) { - _methodChannelManager + _platform .onTimeout(matchingKey: key, bucketingKey: bucketingKey) ?.then((val) => onTimeout.call(client)); } if (onUpdated != null) { - _methodChannelManager + _platform .onUpdated(matchingKey: key, bucketingKey: bucketingKey) ?.listen((event) => onUpdated.call(client)); } @@ -108,32 +105,32 @@ class Splitio { } Future> splitNames() async { - List splitNames = await _methodChannelManager.splitNames( + List splitNames = await _platform.splitNames( matchingKey: _defaultMatchingKey, bucketingKey: _defaultBucketingKey); return splitNames; } Future> splits() async { - return _methodChannelManager.splits( + return _platform.splits( matchingKey: _defaultMatchingKey, bucketingKey: _defaultBucketingKey); } /// If the impressionListener configuration has been enabled, /// generated impressions will be streamed here. Stream impressionsStream() { - return _methodChannelManager.impressionsStream(); + return _platform.impressionsStream(); } Future split(String splitName) async { - return _methodChannelManager.split( + return _platform.split( matchingKey: _defaultMatchingKey, bucketingKey: _defaultBucketingKey, splitName: splitName); } Future _init() { - return _methodChannelManager.init( + return _platform.init( apiKey: _apiKey, matchingKey: _defaultMatchingKey, bucketingKey: _defaultBucketingKey, From 7dc6da2751b1c724174ade7210d8b868bc6a661f Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Fri, 2 Sep 2022 16:39:14 -0300 Subject: [PATCH 12/17] Platform interface package --- splitio/example/pubspec.lock | 7 + splitio/lib/split_client.dart | 4 +- splitio/lib/splitio.dart | 16 +- splitio/pubspec.yaml | 3 +- splitio/test/splitio_client_test.dart | 2 - splitio_platform_interface/.gitignore | 29 +++ splitio_platform_interface/CHANGELOG.md | 0 splitio_platform_interface/LICENSE | 201 ++++++++++++++++++ splitio_platform_interface/README.md | 0 .../lib/events/split_method_call_handler.dart | 2 +- .../impressions_method_call_handler.dart | 4 +- .../lib/method_call_handler.dart | 0 .../lib}/method_channel_platform.dart | 17 +- .../lib/split_configuration.dart | 2 +- .../lib}/split_impression.dart | 0 .../lib/split_result.dart | 0 .../lib/split_sync_config.dart | 0 .../lib/split_view.dart | 0 .../lib/splitio_platform_interface.dart | 10 +- splitio_platform_interface/pubspec.yaml | 23 ++ .../impressions_method_call_handler_test.dart | 3 +- .../test/method_channel_manager_test.dart | 3 +- .../test/split_event_listener_test.dart | 3 +- .../test/split_impression_test.dart | 0 .../test/split_view_test.dart | 0 .../test/splitio_configuration_test.dart | 0 26 files changed, 295 insertions(+), 34 deletions(-) create mode 100644 splitio_platform_interface/.gitignore create mode 100644 splitio_platform_interface/CHANGELOG.md create mode 100644 splitio_platform_interface/LICENSE create mode 100644 splitio_platform_interface/README.md rename {splitio => splitio_platform_interface}/lib/events/split_method_call_handler.dart (97%) rename {splitio => splitio_platform_interface}/lib/impressions/impressions_method_call_handler.dart (82%) rename {splitio => splitio_platform_interface}/lib/method_call_handler.dart (100%) rename {splitio/lib/platform => splitio_platform_interface/lib}/method_channel_platform.dart (94%) rename {splitio => splitio_platform_interface}/lib/split_configuration.dart (98%) rename {splitio/lib/impressions => splitio_platform_interface/lib}/split_impression.dart (100%) rename {splitio => splitio_platform_interface}/lib/split_result.dart (100%) rename {splitio => splitio_platform_interface}/lib/split_sync_config.dart (100%) rename {splitio => splitio_platform_interface}/lib/split_view.dart (100%) rename splitio/lib/platform/common_platform.dart => splitio_platform_interface/lib/splitio_platform_interface.dart (93%) create mode 100644 splitio_platform_interface/pubspec.yaml rename {splitio => splitio_platform_interface}/test/impressions_method_call_handler_test.dart (96%) rename {splitio => splitio_platform_interface}/test/method_channel_manager_test.dart (98%) rename {splitio => splitio_platform_interface}/test/split_event_listener_test.dart (99%) rename {splitio => splitio_platform_interface}/test/split_impression_test.dart (100%) rename {splitio => splitio_platform_interface}/test/split_view_test.dart (100%) rename {splitio => splitio_platform_interface}/test/splitio_configuration_test.dart (100%) diff --git a/splitio/example/pubspec.lock b/splitio/example/pubspec.lock index ec56524..1de670f 100644 --- a/splitio/example/pubspec.lock +++ b/splitio/example/pubspec.lock @@ -135,6 +135,13 @@ packages: relative: true source: path version: "0.1.1" + splitio_platform_interface: + dependency: transitive + description: + path: "../../splitio_platform_interface" + relative: true + source: path + version: "1.0.0" stack_trace: dependency: transitive description: diff --git a/splitio/lib/split_client.dart b/splitio/lib/split_client.dart index 23f4c43..ec333b6 100644 --- a/splitio/lib/split_client.dart +++ b/splitio/lib/split_client.dart @@ -1,6 +1,6 @@ import 'package:flutter/foundation.dart'; -import 'package:splitio/platform/common_platform.dart'; -import 'package:splitio/split_result.dart'; +import 'package:splitio_platform_interface/split_result.dart'; +import 'package:splitio_platform_interface/splitio_platform_interface.dart'; abstract class SplitClient { /// Performs an evaluation for the [splitName] feature. diff --git a/splitio/lib/splitio.dart b/splitio/lib/splitio.dart index 7d8ddd0..203153e 100644 --- a/splitio/lib/splitio.dart +++ b/splitio/lib/splitio.dart @@ -1,17 +1,15 @@ import 'dart:async'; -import 'package:splitio/impressions/split_impression.dart'; -import 'package:splitio/platform/common_platform.dart'; import 'package:splitio/split_client.dart'; -import 'package:splitio/split_configuration.dart'; -import 'package:splitio/split_view.dart'; +import 'package:splitio_platform_interface/split_configuration.dart'; +import 'package:splitio_platform_interface/split_impression.dart'; +import 'package:splitio_platform_interface/split_view.dart'; +import 'package:splitio_platform_interface/splitio_platform_interface.dart'; -export 'package:splitio/impressions/split_impression.dart'; export 'package:splitio/split_client.dart'; -export 'package:splitio/split_configuration.dart'; -export 'package:splitio/split_result.dart'; -export 'package:splitio/split_sync_config.dart'; -export 'package:splitio/split_view.dart'; +export 'package:splitio_platform_interface/split_configuration.dart'; +export 'package:splitio_platform_interface/split_impression.dart'; +export 'package:splitio_platform_interface/split_view.dart'; typedef ClientReadinessCallback = void Function(SplitClient splitClient); diff --git a/splitio/pubspec.yaml b/splitio/pubspec.yaml index 712d009..ddc7878 100644 --- a/splitio/pubspec.yaml +++ b/splitio/pubspec.yaml @@ -11,7 +11,8 @@ environment: dependencies: flutter: sdk: flutter - plugin_platform_interface: ^2.1.2 + splitio_platform_interface: # ^1.0.0 + path: ../splitio_platform_interface dev_dependencies: flutter_test: diff --git a/splitio/test/splitio_client_test.dart b/splitio/test/splitio_client_test.dart index 52e7a44..0babeb2 100644 --- a/splitio/test/splitio_client_test.dart +++ b/splitio/test/splitio_client_test.dart @@ -1,7 +1,5 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:splitio/channel/method_channel_manager.dart'; -import 'package:splitio/events/split_events_listener.dart'; import 'package:splitio/split_client.dart'; void main() { diff --git a/splitio_platform_interface/.gitignore b/splitio_platform_interface/.gitignore new file mode 100644 index 0000000..9be145f --- /dev/null +++ b/splitio_platform_interface/.gitignore @@ -0,0 +1,29 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +.packages +build/ diff --git a/splitio_platform_interface/CHANGELOG.md b/splitio_platform_interface/CHANGELOG.md new file mode 100644 index 0000000..e69de29 diff --git a/splitio_platform_interface/LICENSE b/splitio_platform_interface/LICENSE new file mode 100644 index 0000000..20e4bd8 --- /dev/null +++ b/splitio_platform_interface/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/splitio_platform_interface/README.md b/splitio_platform_interface/README.md new file mode 100644 index 0000000..e69de29 diff --git a/splitio/lib/events/split_method_call_handler.dart b/splitio_platform_interface/lib/events/split_method_call_handler.dart similarity index 97% rename from splitio/lib/events/split_method_call_handler.dart rename to splitio_platform_interface/lib/events/split_method_call_handler.dart index 138d8b3..6a0db02 100644 --- a/splitio/lib/events/split_method_call_handler.dart +++ b/splitio_platform_interface/lib/events/split_method_call_handler.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import 'package:splitio/method_call_handler.dart'; +import 'package:splitio_platform_interface/method_call_handler.dart'; class SplitEventMethodCallHandler implements MethodCallHandler { static const String _eventClientReady = 'clientReady'; diff --git a/splitio/lib/impressions/impressions_method_call_handler.dart b/splitio_platform_interface/lib/impressions/impressions_method_call_handler.dart similarity index 82% rename from splitio/lib/impressions/impressions_method_call_handler.dart rename to splitio_platform_interface/lib/impressions/impressions_method_call_handler.dart index 7a34b8d..ea4e4a4 100644 --- a/splitio/lib/impressions/impressions_method_call_handler.dart +++ b/splitio_platform_interface/lib/impressions/impressions_method_call_handler.dart @@ -1,7 +1,7 @@ import 'dart:async'; -import 'package:splitio/impressions/split_impression.dart'; -import 'package:splitio/method_call_handler.dart'; +import 'package:splitio_platform_interface/method_call_handler.dart'; +import 'package:splitio_platform_interface/split_impression.dart'; class ImpressionsMethodCallHandler extends StreamMethodCallHandler { final _streamController = StreamController(); diff --git a/splitio/lib/method_call_handler.dart b/splitio_platform_interface/lib/method_call_handler.dart similarity index 100% rename from splitio/lib/method_call_handler.dart rename to splitio_platform_interface/lib/method_call_handler.dart diff --git a/splitio/lib/platform/method_channel_platform.dart b/splitio_platform_interface/lib/method_channel_platform.dart similarity index 94% rename from splitio/lib/platform/method_channel_platform.dart rename to splitio_platform_interface/lib/method_channel_platform.dart index b959237..95ff186 100644 --- a/splitio/lib/platform/method_channel_platform.dart +++ b/splitio_platform_interface/lib/method_channel_platform.dart @@ -1,13 +1,14 @@ + import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; -import 'package:splitio/events/split_method_call_handler.dart'; -import 'package:splitio/impressions/impressions_method_call_handler.dart'; -import 'package:splitio/impressions/split_impression.dart'; -import 'package:splitio/method_call_handler.dart'; -import 'package:splitio/platform/common_platform.dart'; -import 'package:splitio/split_configuration.dart'; -import 'package:splitio/split_result.dart'; -import 'package:splitio/split_view.dart'; +import 'package:splitio_platform_interface/events/split_method_call_handler.dart'; +import 'package:splitio_platform_interface/impressions/impressions_method_call_handler.dart'; +import 'package:splitio_platform_interface/method_call_handler.dart'; +import 'package:splitio_platform_interface/split_configuration.dart'; +import 'package:splitio_platform_interface/split_impression.dart'; +import 'package:splitio_platform_interface/split_result.dart'; +import 'package:splitio_platform_interface/split_view.dart'; +import 'package:splitio_platform_interface/splitio_platform_interface.dart'; const String _controlTreatment = 'control'; const SplitResult _controlResult = SplitResult(_controlTreatment, null); diff --git a/splitio/lib/split_configuration.dart b/splitio_platform_interface/lib/split_configuration.dart similarity index 98% rename from splitio/lib/split_configuration.dart rename to splitio_platform_interface/lib/split_configuration.dart index 5e93df9..80c55fb 100644 --- a/splitio/lib/split_configuration.dart +++ b/splitio_platform_interface/lib/split_configuration.dart @@ -1,4 +1,4 @@ -import 'package:splitio/split_sync_config.dart'; +import 'package:splitio_platform_interface/split_sync_config.dart'; class SplitConfiguration { final Map configurationMap = {}; diff --git a/splitio/lib/impressions/split_impression.dart b/splitio_platform_interface/lib/split_impression.dart similarity index 100% rename from splitio/lib/impressions/split_impression.dart rename to splitio_platform_interface/lib/split_impression.dart diff --git a/splitio/lib/split_result.dart b/splitio_platform_interface/lib/split_result.dart similarity index 100% rename from splitio/lib/split_result.dart rename to splitio_platform_interface/lib/split_result.dart diff --git a/splitio/lib/split_sync_config.dart b/splitio_platform_interface/lib/split_sync_config.dart similarity index 100% rename from splitio/lib/split_sync_config.dart rename to splitio_platform_interface/lib/split_sync_config.dart diff --git a/splitio/lib/split_view.dart b/splitio_platform_interface/lib/split_view.dart similarity index 100% rename from splitio/lib/split_view.dart rename to splitio_platform_interface/lib/split_view.dart diff --git a/splitio/lib/platform/common_platform.dart b/splitio_platform_interface/lib/splitio_platform_interface.dart similarity index 93% rename from splitio/lib/platform/common_platform.dart rename to splitio_platform_interface/lib/splitio_platform_interface.dart index a1f7a14..e368763 100644 --- a/splitio/lib/platform/common_platform.dart +++ b/splitio_platform_interface/lib/splitio_platform_interface.dart @@ -1,11 +1,11 @@ import 'dart:async'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -import 'package:splitio/impressions/split_impression.dart'; -import 'package:splitio/platform/method_channel_platform.dart'; -import 'package:splitio/split_configuration.dart'; -import 'package:splitio/split_result.dart'; -import 'package:splitio/split_view.dart'; +import 'package:splitio_platform_interface/method_channel_platform.dart'; +import 'package:splitio_platform_interface/split_configuration.dart'; +import 'package:splitio_platform_interface/split_impression.dart'; +import 'package:splitio_platform_interface/split_result.dart'; +import 'package:splitio_platform_interface/split_view.dart'; abstract class _FactoryPlatform { Future getClient( diff --git a/splitio_platform_interface/pubspec.yaml b/splitio_platform_interface/pubspec.yaml new file mode 100644 index 0000000..0d43036 --- /dev/null +++ b/splitio_platform_interface/pubspec.yaml @@ -0,0 +1,23 @@ +name: splitio_platform_interface +description: Common platform interface for the splitio plugin. +# NOTE: We strongly prefer non-breaking changes, even at the expense of a +# less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes +version: 1.0.0 +homepage: https://split.io/ +repository: https://github.com/splitio/flutter-sdk-plugin/splitio/splitio_platform_interface + +publish_to: none + +environment: + sdk: ">=2.16.2 <3.0.0" + flutter: ">=2.5.0" + +dependencies: + flutter: + sdk: flutter + plugin_platform_interface: ^2.1.2 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^1.0.0 diff --git a/splitio/test/impressions_method_call_handler_test.dart b/splitio_platform_interface/test/impressions_method_call_handler_test.dart similarity index 96% rename from splitio/test/impressions_method_call_handler_test.dart rename to splitio_platform_interface/test/impressions_method_call_handler_test.dart index 4557cfa..6ae0cbb 100644 --- a/splitio/test/impressions_method_call_handler_test.dart +++ b/splitio_platform_interface/test/impressions_method_call_handler_test.dart @@ -1,8 +1,9 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:splitio/impressions/impressions_method_call_handler.dart'; import 'package:splitio/impressions/split_impression.dart'; +import '../lib/impressions/impressions_method_call_handler.dart'; + void main() { test('correct impressionLog method call emits value on stream', () async { const Map sourceMap = { diff --git a/splitio/test/method_channel_manager_test.dart b/splitio_platform_interface/test/method_channel_manager_test.dart similarity index 98% rename from splitio/test/method_channel_manager_test.dart rename to splitio_platform_interface/test/method_channel_manager_test.dart index a752a2f..3d89ed5 100644 --- a/splitio/test/method_channel_manager_test.dart +++ b/splitio_platform_interface/test/method_channel_manager_test.dart @@ -1,7 +1,8 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:splitio/channel/method_channel_manager.dart'; -import 'package:splitio/method_call_handler.dart'; + +import '../lib/method_call_handler.dart'; void main() { late MethodChannelStub _channel; diff --git a/splitio/test/split_event_listener_test.dart b/splitio_platform_interface/test/split_event_listener_test.dart similarity index 99% rename from splitio/test/split_event_listener_test.dart rename to splitio_platform_interface/test/split_event_listener_test.dart index c0bfa2f..f06c463 100644 --- a/splitio/test/split_event_listener_test.dart +++ b/splitio_platform_interface/test/split_event_listener_test.dart @@ -3,9 +3,10 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:splitio/channel/method_channel_manager.dart'; import 'package:splitio/events/split_events_listener.dart'; import 'package:splitio/events/split_method_call_handler.dart'; -import 'package:splitio/split_client.dart'; import 'package:splitio/split_result.dart'; +import '../lib/split_client.dart'; + void main() { const MethodChannel _channel = MethodChannel('splitio'); diff --git a/splitio/test/split_impression_test.dart b/splitio_platform_interface/test/split_impression_test.dart similarity index 100% rename from splitio/test/split_impression_test.dart rename to splitio_platform_interface/test/split_impression_test.dart diff --git a/splitio/test/split_view_test.dart b/splitio_platform_interface/test/split_view_test.dart similarity index 100% rename from splitio/test/split_view_test.dart rename to splitio_platform_interface/test/split_view_test.dart diff --git a/splitio/test/splitio_configuration_test.dart b/splitio_platform_interface/test/splitio_configuration_test.dart similarity index 100% rename from splitio/test/splitio_configuration_test.dart rename to splitio_platform_interface/test/splitio_configuration_test.dart From 5832f73e4f5651e66a3327e5d3f7881cbce34c2f Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Fri, 2 Sep 2022 16:59:35 -0300 Subject: [PATCH 13/17] Revert files to reduce diff noise --- splitio/example/pubspec.lock | 14 -- splitio/lib/split_client.dart | 198 +++++++++++------- splitio/lib/splitio.dart | 116 ++++++---- .../impressions_method_call_handler_test.dart | 12 +- .../test/method_channel_manager_test.dart | 100 --------- .../test/split_event_listener_test.dart | 6 +- 6 files changed, 204 insertions(+), 242 deletions(-) delete mode 100644 splitio_platform_interface/test/method_channel_manager_test.dart diff --git a/splitio/example/pubspec.lock b/splitio/example/pubspec.lock index 1de670f..3d5ec72 100644 --- a/splitio/example/pubspec.lock +++ b/splitio/example/pubspec.lock @@ -109,13 +109,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.0" - plugin_platform_interface: - dependency: transitive - description: - name: plugin_platform_interface - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.2" sky_engine: dependency: transitive description: flutter @@ -135,13 +128,6 @@ packages: relative: true source: path version: "0.1.1" - splitio_platform_interface: - dependency: transitive - description: - path: "../../splitio_platform_interface" - relative: true - source: path - version: "1.0.0" stack_trace: dependency: transitive description: diff --git a/splitio/lib/split_client.dart b/splitio/lib/split_client.dart index ec333b6..fb72ce0 100644 --- a/splitio/lib/split_client.dart +++ b/splitio/lib/split_client.dart @@ -1,6 +1,8 @@ import 'package:flutter/foundation.dart'; -import 'package:splitio_platform_interface/split_result.dart'; -import 'package:splitio_platform_interface/splitio_platform_interface.dart'; +import 'package:splitio/channel/method_channel_manager.dart'; +import 'package:splitio/events/split_events_listener.dart'; +import 'package:splitio/events/split_method_call_handler.dart'; +import 'package:splitio/split_result.dart'; abstract class SplitClient { /// Performs an evaluation for the [splitName] feature. @@ -140,55 +142,79 @@ abstract class SplitClient { } class DefaultSplitClient implements SplitClient { - final SplitioPlatform _platform; + static const String _controlTreatment = 'control'; + static const SplitResult _controlResult = + SplitResult(_controlTreatment, null); + + final MethodChannelManager _methodChannelManager; + late final SplitEventMethodCallHandler _methodCallHandler; final String _matchingKey; final String? _bucketingKey; - DefaultSplitClient(this._platform, this._matchingKey, this._bucketingKey); + late final SplitEventsListener _splitEventsListener; + + DefaultSplitClient( + this._methodChannelManager, this._matchingKey, this._bucketingKey) { + _methodCallHandler = + SplitEventMethodCallHandler(_matchingKey, _bucketingKey, this); + _splitEventsListener = + DefaultEventsListener(_methodChannelManager, _methodCallHandler); + } @visibleForTesting - DefaultSplitClient.withEventListener( - this._platform, this._matchingKey, this._bucketingKey); + DefaultSplitClient.withEventListener(this._methodChannelManager, + this._matchingKey, this._bucketingKey, this._splitEventsListener); @override Future getTreatment(String splitName, [Map attributes = const {}]) async { - return _platform.getTreatment( - matchingKey: _matchingKey, - bucketingKey: _bucketingKey, - splitName: splitName, - attributes: attributes); + return await _methodChannelManager.invokeMethod( + 'getTreatment', + _buildParameters( + {'splitName': splitName, 'attributes': attributes})) ?? + _controlTreatment; } @override Future getTreatmentWithConfig(String splitName, [Map attributes = const {}]) async { - return _platform.getTreatmentWithConfig( - matchingKey: _matchingKey, - bucketingKey: _bucketingKey, - splitName: splitName, - attributes: attributes); + Map? treatment = (await _methodChannelManager.invokeMapMethod( + 'getTreatmentWithConfig', + _buildParameters( + {'splitName': splitName, 'attributes': attributes}))) + ?.entries + .first + .value; + if (treatment == null) { + return _controlResult; + } + + return SplitResult(treatment['treatment'], treatment['config']); } @override Future> getTreatments(List splitNames, [Map attributes = const {}]) async { - return _platform.getTreatments( - matchingKey: _matchingKey, - bucketingKey: _bucketingKey, - splitNames: splitNames, - attributes: attributes); + Map? treatments = await _methodChannelManager.invokeMapMethod( + 'getTreatments', + _buildParameters({'splitName': splitNames, 'attributes': attributes})); + + return treatments + ?.map((key, value) => MapEntry(key, value)) ?? + {for (var item in splitNames) item: _controlTreatment}; } @override Future> getTreatmentsWithConfig( List splitNames, [Map attributes = const {}]) async { - return _platform.getTreatmentsWithConfig( - matchingKey: _matchingKey, - bucketingKey: _bucketingKey, - splitNames: splitNames, - attributes: attributes); + Map? treatments = await _methodChannelManager.invokeMapMethod( + 'getTreatmentsWithConfig', + _buildParameters({'splitName': splitNames, 'attributes': attributes})); + + return treatments?.map((key, value) => + MapEntry(key, SplitResult(value['treatment'], value['config']))) ?? + {for (var item in splitNames) item: _controlResult}; } @override @@ -196,101 +222,121 @@ class DefaultSplitClient implements SplitClient { {String? trafficType, double? value, Map properties = const {}}) async { - return _platform.track( - matchingKey: _matchingKey, - bucketingKey: _bucketingKey, - eventType: eventType, - trafficType: trafficType, - value: value, - properties: properties); + var parameters = _buildParameters({'eventType': eventType}); + + if (trafficType != null) { + parameters['trafficType'] = trafficType; + } + + if (value != null) { + parameters['value'] = value; + } + + try { + return await _methodChannelManager.invokeMethod("track", parameters) + as bool; + } on Exception catch (_) { + return false; + } } @override Future setAttribute(String attributeName, dynamic value) async { - return _platform.setAttribute( - matchingKey: _matchingKey, - bucketingKey: _bucketingKey, - attributeName: attributeName, - value: value); + var result = await _methodChannelManager.invokeMethod('setAttribute', + _buildParameters({'attributeName': attributeName, 'value': value})); + + if (result is bool) { + return result; + } + + return false; } @override Future getAttribute(String attributeName) async { - return _platform.getAttribute( - matchingKey: _matchingKey, - bucketingKey: _bucketingKey, - attributeName: attributeName); + return _methodChannelManager.invokeMethod( + 'getAttribute', _buildParameters({'attributeName': attributeName})); } @override Future setAttributes(Map attributes) async { - return _platform.setAttributes( - matchingKey: _matchingKey, - bucketingKey: _bucketingKey, - attributes: attributes); + var result = await _methodChannelManager.invokeMethod( + 'setAttributes', _buildParameters({'attributes': attributes})); + + if (result is bool) { + return result; + } + + return false; } @override Future> getAttributes() async { - return _platform.getAllAttributes( - matchingKey: _matchingKey, bucketingKey: _bucketingKey); + return (await _methodChannelManager.invokeMapMethod( + 'getAllAttributes', _buildParameters())) + ?.map((key, value) => MapEntry(key, value)) ?? + {}; } @override Future removeAttribute(String attributeName) async { - return _platform.removeAttribute( - matchingKey: _matchingKey, - bucketingKey: _bucketingKey, - attributeName: attributeName); + return await _methodChannelManager.invokeMethod( + 'removeAttribute', _buildParameters({'attributeName': attributeName})); } @override Future clearAttributes() async { - return _platform.clearAttributes( - matchingKey: _matchingKey, bucketingKey: _bucketingKey); + return await _methodChannelManager.invokeMethod( + 'clearAttributes', _buildParameters()); } @override Future flush() async { - return _platform.flush( - matchingKey: _matchingKey, bucketingKey: _bucketingKey); + return _methodChannelManager.invokeMethod('flush', _buildParameters()); } @override Future destroy() async { - return _platform.destroy( - matchingKey: _matchingKey, bucketingKey: _bucketingKey); + _splitEventsListener.destroy(); + return _methodChannelManager.invokeMethod('destroy', _buildParameters()); } @override - Future whenReady() async { - await _platform.onReady( - matchingKey: _matchingKey, bucketingKey: _bucketingKey); - - return Future.value(this); + Future whenReady() { + return _splitEventsListener.onReady(); } @override - Future whenReadyFromCache() async { - await _platform.onReadyFromCache( - matchingKey: _matchingKey, bucketingKey: _bucketingKey); - - return Future.value(this); + Future whenReadyFromCache() { + return _splitEventsListener.onReadyFromCache(); } @override Stream whenUpdated() { - return _platform - .onUpdated(matchingKey: _matchingKey, bucketingKey: _bucketingKey) - ?.map((event) => this) ?? - const Stream.empty(); + return _splitEventsListener.onUpdated(); } @override - Future whenTimeout() async { - await _platform.onTimeout( - matchingKey: _matchingKey, bucketingKey: _bucketingKey); + Future whenTimeout() { + return _splitEventsListener.onTimeout(); + } + + Map _getKeysMap() { + Map result = {'matchingKey': _matchingKey}; + + if (_bucketingKey != null) { + result.addAll({'bucketingKey': _bucketingKey!}); + } + + return result; + } + + Map _buildParameters( + [Map parameters = const {}]) { + Map result = {}; + result.addAll(parameters); + result.addAll(_getKeysMap()); - return Future.value(this); + return result; } } diff --git a/splitio/lib/splitio.dart b/splitio/lib/splitio.dart index 203153e..254a486 100644 --- a/splitio/lib/splitio.dart +++ b/splitio/lib/splitio.dart @@ -1,28 +1,31 @@ import 'dart:async'; +import 'package:flutter/services.dart'; +import 'package:splitio/channel/method_channel_manager.dart'; +import 'package:splitio/impressions/impressions_method_call_handler.dart'; +import 'package:splitio/impressions/split_impression.dart'; +import 'package:splitio/method_call_handler.dart'; import 'package:splitio/split_client.dart'; -import 'package:splitio_platform_interface/split_configuration.dart'; -import 'package:splitio_platform_interface/split_impression.dart'; -import 'package:splitio_platform_interface/split_view.dart'; -import 'package:splitio_platform_interface/splitio_platform_interface.dart'; +import 'package:splitio/split_configuration.dart'; +import 'package:splitio/split_view.dart'; +export 'package:splitio/impressions/split_impression.dart'; export 'package:splitio/split_client.dart'; -export 'package:splitio_platform_interface/split_configuration.dart'; -export 'package:splitio_platform_interface/split_impression.dart'; -export 'package:splitio_platform_interface/split_view.dart'; +export 'package:splitio/split_configuration.dart'; +export 'package:splitio/split_result.dart'; +export 'package:splitio/split_sync_config.dart'; +export 'package:splitio/split_view.dart'; typedef ClientReadinessCallback = void Function(SplitClient splitClient); class Splitio { final String _apiKey; - final String _defaultMatchingKey; - late final String? _defaultBucketingKey; - late final SplitConfiguration? _splitConfiguration; - - final SplitioPlatform _platform = SplitioPlatform.instance; + late final StreamMethodCallHandler _impressionsMethodCallHandler; + final MethodChannelManager _methodChannelManager = + MethodChannelManager(const MethodChannel('splitio')); /// SDK instance constructor. /// @@ -38,6 +41,8 @@ class Splitio { {String? bucketingKey, SplitConfiguration? configuration}) { _defaultBucketingKey = bucketingKey; _splitConfiguration = configuration; + _impressionsMethodCallHandler = ImpressionsMethodCallHandler(); + _methodChannelManager.addHandler(_impressionsMethodCallHandler); _init(); } @@ -72,66 +77,97 @@ class Splitio { ClientReadinessCallback? onUpdated, ClientReadinessCallback? onTimeout}) { String? key = matchingKey ?? _defaultMatchingKey; - _platform.getClient(matchingKey: key, bucketingKey: bucketingKey); - var client = DefaultSplitClient(_platform, key, bucketingKey); + var client = DefaultSplitClient(_methodChannelManager, key, bucketingKey); if (onReady != null) { - _platform - .onReady(matchingKey: key, bucketingKey: bucketingKey) - ?.then((val) => onReady.call(client)); + client.whenReady().then((client) => onReady.call(client)); } if (onReadyFromCache != null) { - _platform - .onReadyFromCache(matchingKey: key, bucketingKey: bucketingKey) - ?.then((val) => onReadyFromCache.call(client)); + client + .whenReadyFromCache() + .then((client) => onReadyFromCache.call(client)); } if (onTimeout != null) { - _platform - .onTimeout(matchingKey: key, bucketingKey: bucketingKey) - ?.then((val) => onTimeout.call(client)); + client.whenTimeout().then((client) => onTimeout.call(client)); } if (onUpdated != null) { - _platform - .onUpdated(matchingKey: key, bucketingKey: bucketingKey) - ?.listen((event) => onUpdated.call(client)); + client.whenUpdated().listen((client) => onUpdated.call(client)); } + _methodChannelManager.invokeMethod( + 'getClient', _buildGetClientArguments(key, bucketingKey)); + return client; } Future> splitNames() async { - List splitNames = await _platform.splitNames( - matchingKey: _defaultMatchingKey, bucketingKey: _defaultBucketingKey); + List splitNames = + await _methodChannelManager.invokeListMethod('splitNames') ?? + []; return splitNames; } Future> splits() async { - return _platform.splits( - matchingKey: _defaultMatchingKey, bucketingKey: _defaultBucketingKey); + List callResult = (await _methodChannelManager + .invokeListMethod>('splits') ?? + []); + + List splits = []; + for (var element in callResult) { + SplitView? splitView = SplitView.fromEntry(element); + if (splitView != null) { + splits.add(splitView); + } + } + + return Future.value(splits); } /// If the impressionListener configuration has been enabled, /// generated impressions will be streamed here. Stream impressionsStream() { - return _platform.impressionsStream(); + return _impressionsMethodCallHandler.stream(); } Future split(String splitName) async { - return _platform.split( - matchingKey: _defaultMatchingKey, - bucketingKey: _defaultBucketingKey, - splitName: splitName); + Map? mapResult = await _methodChannelManager + .invokeMapMethod('split', {'splitName': splitName}); + + if (mapResult == null) { + return null; + } + + return SplitView.fromEntry(mapResult); } Future _init() { - return _platform.init( - apiKey: _apiKey, - matchingKey: _defaultMatchingKey, - bucketingKey: _defaultBucketingKey, - sdkConfiguration: _splitConfiguration); + Map arguments = { + 'apiKey': _apiKey, + 'matchingKey': _defaultMatchingKey, + 'sdkConfiguration': _splitConfiguration?.configurationMap ?? {}, + }; + + if (_defaultBucketingKey != null) { + arguments.addAll({'bucketingKey': _defaultBucketingKey}); + } + + return _methodChannelManager.invokeMethod('init', arguments); + } + + Map _buildGetClientArguments( + String key, String? bucketingKey) { + var arguments = { + 'matchingKey': key, + }; + + if (bucketingKey != null) { + arguments.addAll({'bucketingKey': bucketingKey}); + } + + return arguments; } } diff --git a/splitio_platform_interface/test/impressions_method_call_handler_test.dart b/splitio_platform_interface/test/impressions_method_call_handler_test.dart index 6ae0cbb..f919f9a 100644 --- a/splitio_platform_interface/test/impressions_method_call_handler_test.dart +++ b/splitio_platform_interface/test/impressions_method_call_handler_test.dart @@ -1,9 +1,8 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:splitio/impressions/impressions_method_call_handler.dart'; import 'package:splitio/impressions/split_impression.dart'; -import '../lib/impressions/impressions_method_call_handler.dart'; - void main() { test('correct impressionLog method call emits value on stream', () async { const Map sourceMap = { @@ -35,9 +34,8 @@ void main() { }), ); - const methodCall = MethodCall('impressionLog', sourceMap); - impressionsMethodCallHandler.handle( - methodCall.method, methodCall.arguments); + impressionsMethodCallHandler + .handle(const MethodCall('impressionLog', sourceMap)); }); test('other method names are ignored', () async { @@ -49,8 +47,6 @@ void main() { expect(event, null); }, count: 0), ); - const methodCall = MethodCall('clientReady'); - impressionsMethodCallHandler.handle( - methodCall.method, methodCall.arguments); + impressionsMethodCallHandler.handle(const MethodCall('clientReady')); }); } diff --git a/splitio_platform_interface/test/method_channel_manager_test.dart b/splitio_platform_interface/test/method_channel_manager_test.dart deleted file mode 100644 index 3d89ed5..0000000 --- a/splitio_platform_interface/test/method_channel_manager_test.dart +++ /dev/null @@ -1,100 +0,0 @@ -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:splitio/channel/method_channel_manager.dart'; - -import '../lib/method_call_handler.dart'; - -void main() { - late MethodChannelStub _channel; - late MethodChannelManager _methodChannelManager; - - setUp(() { - _channel = MethodChannelStub('mock'); - TestWidgetsFlutterBinding.ensureInitialized(); - _methodChannelManager = MethodChannelManager(_channel); - }); - - test('handle calls are delegated to each handler', () { - var mockMethodHandler1 = MethodCallHandlerStub(); - var mockMethodHandler2 = MethodCallHandlerStub(); - var mockMethodHandler3 = MethodCallHandlerStub(); - _methodChannelManager.addNativeCallHandler(mockMethodHandler1); - _methodChannelManager.addNativeCallHandler(mockMethodHandler2); - _methodChannelManager.addNativeCallHandler(mockMethodHandler3); - - _methodChannelManager.handle(const MethodCall('test-method')); - - expect(mockMethodHandler1.handledCalls, {'test-method'}); - expect(mockMethodHandler2.handledCalls, {'test-method'}); - expect(mockMethodHandler3.handledCalls, {'test-method'}); - }); - - test('invokeMethod calls are delegated to channel', () { - _methodChannelManager.invokeMethod('any-method'); - - expect(_channel.calledMethods, {'invokeMethod'}); - }); - - test('invokeMapMethod calls are delegated to channel', () { - _methodChannelManager.invokeMapMethod('any-method'); - - expect(_channel.calledMethods, {'invokeMapMethod'}); - }); - - test('invokeListMethod calls are delegated to channel', () { - _methodChannelManager.invokeListMethod('any-method'); - - expect(_channel.calledMethods, {'invokeListMethod'}); - }); - - test('removed call handlers do not handle methods', () { - final MethodCallHandlerStub handler1 = MethodCallHandlerStub(); - final MethodCallHandlerStub handler2 = MethodCallHandlerStub(); - final MethodCallHandlerStub handler3 = MethodCallHandlerStub(); - _methodChannelManager.addNativeCallHandler(handler1); - _methodChannelManager.addNativeCallHandler(handler2); - _methodChannelManager.addNativeCallHandler(handler3); - - _methodChannelManager.removeNativeCallHandler(handler2); - - _methodChannelManager.handle(const MethodCall('test-method')); - - expect(handler1.handledCalls, {'test-method'}); - expect(handler2.handledCalls, isEmpty); - expect(handler3.handledCalls, {'test-method'}); - }); -} - -class MethodCallHandlerStub extends MethodCallHandler { - final Set handledCalls = {}; - - @override - Future handle(String methodName, dynamic methodArguments) async { - handledCalls.add(methodName); - } -} - -class MethodChannelStub extends MethodChannel { - MethodChannelStub(String name) : super(name); - Set calledMethods = {}; - - @override - Future invokeMethod(String method, [dynamic arguments]) async { - calledMethods.add('invokeMethod'); - return Future.value(null); - } - - @override - Future?> invokeMapMethod(String method, - [dynamic arguments]) async { - calledMethods.add('invokeMapMethod'); - return Future.value(null); - } - - @override - Future?> invokeListMethod(String method, - [dynamic arguments]) async { - calledMethods.add('invokeListMethod'); - return Future.value(null); - } -} diff --git a/splitio_platform_interface/test/split_event_listener_test.dart b/splitio_platform_interface/test/split_event_listener_test.dart index f06c463..3099e8f 100644 --- a/splitio_platform_interface/test/split_event_listener_test.dart +++ b/splitio_platform_interface/test/split_event_listener_test.dart @@ -3,10 +3,9 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:splitio/channel/method_channel_manager.dart'; import 'package:splitio/events/split_events_listener.dart'; import 'package:splitio/events/split_method_call_handler.dart'; +import 'package:splitio/split_client.dart'; import 'package:splitio/split_result.dart'; -import '../lib/split_client.dart'; - void main() { const MethodChannel _channel = MethodChannel('splitio'); @@ -26,8 +25,7 @@ void main() { setUp(() { _channel.setMockMethodCallHandler((MethodCall methodCall) { - splitEventMethodCallHandler.handle( - methodCall.method, methodCall.arguments); + splitEventMethodCallHandler.handle(methodCall); }); }); From cc87f7d384ef66ba028e199ce22fe08ea50c2f2d Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Fri, 2 Sep 2022 17:14:47 -0300 Subject: [PATCH 14/17] nits --- splitio/pubspec.yaml | 2 -- splitio/test/splitio_client_test.dart | 2 ++ splitio_platform_interface/CHANGELOG.md | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/splitio/pubspec.yaml b/splitio/pubspec.yaml index ddc7878..a4f85a3 100644 --- a/splitio/pubspec.yaml +++ b/splitio/pubspec.yaml @@ -11,8 +11,6 @@ environment: dependencies: flutter: sdk: flutter - splitio_platform_interface: # ^1.0.0 - path: ../splitio_platform_interface dev_dependencies: flutter_test: diff --git a/splitio/test/splitio_client_test.dart b/splitio/test/splitio_client_test.dart index 0babeb2..52e7a44 100644 --- a/splitio/test/splitio_client_test.dart +++ b/splitio/test/splitio_client_test.dart @@ -1,5 +1,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:splitio/channel/method_channel_manager.dart'; +import 'package:splitio/events/split_events_listener.dart'; import 'package:splitio/split_client.dart'; void main() { diff --git a/splitio_platform_interface/CHANGELOG.md b/splitio_platform_interface/CHANGELOG.md index e69de29..2dd7b16 100644 --- a/splitio_platform_interface/CHANGELOG.md +++ b/splitio_platform_interface/CHANGELOG.md @@ -0,0 +1,3 @@ +# 1.0.0 + +Initial release. From 8e25e519454f51e5e21fa248de41d1bccc4b9bf8 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Fri, 2 Sep 2022 17:15:33 -0300 Subject: [PATCH 15/17] nit --- splitio_platform_interface/lib/split_configuration.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/splitio_platform_interface/lib/split_configuration.dart b/splitio_platform_interface/lib/split_configuration.dart index 80c55fb..5e93df9 100644 --- a/splitio_platform_interface/lib/split_configuration.dart +++ b/splitio_platform_interface/lib/split_configuration.dart @@ -1,4 +1,4 @@ -import 'package:splitio_platform_interface/split_sync_config.dart'; +import 'package:splitio/split_sync_config.dart'; class SplitConfiguration { final Map configurationMap = {}; From 58be176c91fbcc6d244b9c77dda6d362e1bf4ea3 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Fri, 2 Sep 2022 17:17:16 -0300 Subject: [PATCH 16/17] Delete file for future use --- .../lib/method_channel_platform.dart | 332 ------------------ 1 file changed, 332 deletions(-) delete mode 100644 splitio_platform_interface/lib/method_channel_platform.dart diff --git a/splitio_platform_interface/lib/method_channel_platform.dart b/splitio_platform_interface/lib/method_channel_platform.dart deleted file mode 100644 index 95ff186..0000000 --- a/splitio_platform_interface/lib/method_channel_platform.dart +++ /dev/null @@ -1,332 +0,0 @@ - -import 'package:flutter/foundation.dart'; -import 'package:flutter/services.dart'; -import 'package:splitio_platform_interface/events/split_method_call_handler.dart'; -import 'package:splitio_platform_interface/impressions/impressions_method_call_handler.dart'; -import 'package:splitio_platform_interface/method_call_handler.dart'; -import 'package:splitio_platform_interface/split_configuration.dart'; -import 'package:splitio_platform_interface/split_impression.dart'; -import 'package:splitio_platform_interface/split_result.dart'; -import 'package:splitio_platform_interface/split_view.dart'; -import 'package:splitio_platform_interface/splitio_platform_interface.dart'; - -const String _controlTreatment = 'control'; -const SplitResult _controlResult = SplitResult(_controlTreatment, null); - -class MethodChannelPlatform extends SplitioPlatform { - - final MethodChannel _methodChannel = const MethodChannel('splitio'); - - final Map _handlers = {}; - - final ImpressionsMethodCallHandler _impressionsMethodCallHandler = ImpressionsMethodCallHandler(); - - MethodChannelPlatform() { - _methodChannel.setMethodCallHandler((call) => handle(call)); - } - - @visibleForTesting - Future handle(MethodCall call) async { - _impressionsMethodCallHandler.handle(call.method, call.arguments); - for (MethodCallHandler handler in _handlers.values) { - handler.handle(call.method, call.arguments); - } - } - - @override - Future init({required String apiKey, - required String matchingKey, - required String? bucketingKey, - SplitConfiguration? sdkConfiguration}) { - Map arguments = { - 'apiKey': apiKey, - 'matchingKey': matchingKey, - 'sdkConfiguration': sdkConfiguration?.configurationMap ?? {}, - }; - - if (bucketingKey != null) { - arguments.addAll({'bucketingKey': bucketingKey}); - } - - return _methodChannel.invokeMethod('init', arguments); - } - - @override - Future getClient( - {required String matchingKey, required String? bucketingKey}) { - _handlers.addAll( - { - _buildMapKey(matchingKey, bucketingKey): SplitEventMethodCallHandler( - matchingKey, bucketingKey) - }); - - return _methodChannel.invokeMethod( - 'getClient', _buildParameters(matchingKey, bucketingKey)); - } - - @override - Future clearAttributes( - {required String matchingKey, required String? bucketingKey}) async { - return await _methodChannel.invokeMethod( - 'clearAttributes', _buildParameters(matchingKey, bucketingKey)); - } - - @override - Future destroy( - {required String matchingKey, required String? bucketingKey}) async { - var handlerKey = _buildMapKey(matchingKey, bucketingKey); - _handlers[handlerKey]?.destroy(); - _handlers.remove(handlerKey); - - return await _methodChannel.invokeMethod( - 'destroy', _buildParameters(matchingKey, bucketingKey)); - } - - @override - Future flush( - {required String matchingKey, required String? bucketingKey}) async { - return await _methodChannel.invokeMethod( - 'flush', _buildParameters(matchingKey, bucketingKey)); - } - - @override - Future> getAllAttributes( - {required String matchingKey, required String? bucketingKey}) async { - return (await _methodChannel.invokeMapMethod( - 'getAllAttributes', _buildParameters(matchingKey, bucketingKey))) - ?.map((key, value) => MapEntry(key, value)) ?? - {}; - } - - @override - Future getAttribute({required String matchingKey, - required String? bucketingKey, - required String attributeName}) { - return _methodChannel.invokeMethod( - 'getAttribute', _buildParameters( - matchingKey, bucketingKey, {'attributeName': attributeName})); - } - - @override - Future getTreatment({required String matchingKey, - required String? bucketingKey, - required String splitName, - Map attributes = const {}}) async { - return await _methodChannel.invokeMethod( - 'getTreatment', - _buildParameters(matchingKey, bucketingKey, - {'splitName': splitName, 'attributes': attributes})) ?? - _controlTreatment; - } - - @override - Future getTreatmentWithConfig({required String matchingKey, - required String? bucketingKey, - required String splitName, - Map attributes = const {}}) async { - Map? treatment = (await _methodChannel.invokeMapMethod( - 'getTreatmentWithConfig', - _buildParameters(matchingKey, bucketingKey - {'splitName': splitName, 'attributes': attributes}))) - ?.entries - .first - .value; - if (treatment == null) { - return _controlResult; - } - - return SplitResult(treatment['treatment'], treatment['config']); - } - - @override - Future> getTreatments({required String matchingKey, - required String? bucketingKey, - required List splitNames, - Map attributes = const {}}) async { - Map? treatments = await _methodChannel.invokeMapMethod( - 'getTreatments', - _buildParameters(matchingKey, bucketingKey, - {'splitName': splitNames, 'attributes': attributes})); - - return treatments - ?.map((key, value) => MapEntry(key, value)) ?? - {for (var item in splitNames) item: _controlTreatment}; - } - - @override - Future> getTreatmentsWithConfig( - {required String matchingKey, - required String? bucketingKey, - required List splitNames, - Map attributes = const {}}) async { - Map? treatments = await _methodChannel.invokeMapMethod( - 'getTreatmentsWithConfig', - _buildParameters(matchingKey, bucketingKey, - {'splitName': splitNames, 'attributes': attributes})); - - return treatments?.map((key, value) => - MapEntry(key, SplitResult(value['treatment'], value['config']))) ?? - {for (var item in splitNames) item: _controlResult}; - } - - @override - Future removeAttribute({required String matchingKey, - required String? bucketingKey, - required String attributeName}) async { - return await _methodChannel.invokeMethod( - 'removeAttribute', _buildParameters( - matchingKey, bucketingKey, {'attributeName': attributeName})); - } - - @override - Future setAttribute({required String matchingKey, - required String? bucketingKey, - required String attributeName, - required value}) async { - var result = await _methodChannel.invokeMethod('setAttribute', - _buildParameters(matchingKey, bucketingKey, - {'attributeName': attributeName, 'value': value})); - - if (result is bool) { - return result; - } - - return false; - } - - @override - Future setAttributes({required String matchingKey, - required String? bucketingKey, - required Map attributes}) async { - var result = await _methodChannel.invokeMethod( - 'setAttributes', _buildParameters( - matchingKey, bucketingKey, {'attributes': attributes})); - - if (result is bool) { - return result; - } - - return false; - } - - @override - Future split({required String matchingKey, - required String? bucketingKey, - required String splitName}) async { - Map? mapResult = - await _methodChannel.invokeMapMethod('split', {'splitName': splitName}); - - if (mapResult == null) { - return null; - } - - return SplitView.fromEntry(mapResult); - } - - @override - Future> splitNames( - {required String matchingKey, required String? bucketingKey}) async { - List splitNames = - await _methodChannel.invokeListMethod('splitNames') ?? []; - - return splitNames; - } - - @override - Future> splits( - {required String matchingKey, required String? bucketingKey}) async { - List callResult = (await _methodChannel - .invokeListMethod>('splits') ?? - []); - - List splits = []; - for (var element in callResult) { - SplitView? splitView = SplitView.fromEntry(element); - if (splitView != null) { - splits.add(splitView); - } - } - - return Future.value(splits); - } - - @override - Future track({required String matchingKey, - required String? bucketingKey, - required String eventType, - String? trafficType, - double? value, - Map properties = const {}}) async { - var parameters = _buildParameters( - matchingKey, bucketingKey, {'eventType': eventType}); - - if (trafficType != null) { - parameters['trafficType'] = trafficType; - } - - if (value != null) { - parameters['value'] = value; - } - - try { - return await _methodChannel.invokeMethod("track", parameters) - as bool; - } on Exception catch (_) { - return false; - } - } - - Map _buildParameters(String matchingKey, - String? bucketingKey, - [Map parameters = const {}]) { - Map result = {}; - result.addAll(parameters); - result.addAll(_getKeysMap(matchingKey, bucketingKey)); - - return result; - } - - Map _getKeysMap(String matchingKey, String? bucketingKey) { - Map result = {'matchingKey': matchingKey}; - - if (bucketingKey != null) { - result.addAll({'bucketingKey': bucketingKey}); - } - - return result; - } - - @override - Future? onReady( - {required String matchingKey, required String? bucketingKey}) { - return _handlers[_buildMapKey(matchingKey, bucketingKey)]?.onReady(); - } - - @override - Future? onReadyFromCache( - {required String matchingKey, required String? bucketingKey}) { - return _handlers[_buildMapKey(matchingKey, bucketingKey)] - ?.onReadyFromCache(); - } - - @override - Stream? onUpdated( - {required String matchingKey, required String? bucketingKey}) { - return _handlers[_buildMapKey(matchingKey, bucketingKey)]?.onUpdated(); - } - - @override - Future? onTimeout( - {required String matchingKey, required String? bucketingKey}) { - return _handlers[_buildMapKey(matchingKey, bucketingKey)]?.onTimeout(); - } - - @override - Stream impressionsStream() { - return _impressionsMethodCallHandler.stream(); - } - - String _buildMapKey(String matchingKey, String? bucketingKey) { - return '${matchingKey}_$bucketingKey'; - } -} From 264dce457e35819e68f14e3e4ae70c882533b0a1 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Fri, 2 Sep 2022 17:31:04 -0300 Subject: [PATCH 17/17] Platform interface --- splitio/example/pubspec.lock | 14 + splitio/lib/split_client.dart | 198 ++++------- splitio/lib/splitio.dart | 116 +++--- splitio/pubspec.yaml | 2 + splitio/test/splitio_client_test.dart | 2 - .../lib/method_channel_platform.dart | 332 ++++++++++++++++++ .../lib/split_configuration.dart | 2 +- .../impressions_method_call_handler_test.dart | 12 +- .../test/split_event_listener_test.dart | 6 +- 9 files changed, 477 insertions(+), 207 deletions(-) create mode 100644 splitio_platform_interface/lib/method_channel_platform.dart diff --git a/splitio/example/pubspec.lock b/splitio/example/pubspec.lock index 3d5ec72..1de670f 100644 --- a/splitio/example/pubspec.lock +++ b/splitio/example/pubspec.lock @@ -109,6 +109,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" sky_engine: dependency: transitive description: flutter @@ -128,6 +135,13 @@ packages: relative: true source: path version: "0.1.1" + splitio_platform_interface: + dependency: transitive + description: + path: "../../splitio_platform_interface" + relative: true + source: path + version: "1.0.0" stack_trace: dependency: transitive description: diff --git a/splitio/lib/split_client.dart b/splitio/lib/split_client.dart index fb72ce0..ec333b6 100644 --- a/splitio/lib/split_client.dart +++ b/splitio/lib/split_client.dart @@ -1,8 +1,6 @@ import 'package:flutter/foundation.dart'; -import 'package:splitio/channel/method_channel_manager.dart'; -import 'package:splitio/events/split_events_listener.dart'; -import 'package:splitio/events/split_method_call_handler.dart'; -import 'package:splitio/split_result.dart'; +import 'package:splitio_platform_interface/split_result.dart'; +import 'package:splitio_platform_interface/splitio_platform_interface.dart'; abstract class SplitClient { /// Performs an evaluation for the [splitName] feature. @@ -142,79 +140,55 @@ abstract class SplitClient { } class DefaultSplitClient implements SplitClient { - static const String _controlTreatment = 'control'; - static const SplitResult _controlResult = - SplitResult(_controlTreatment, null); - - final MethodChannelManager _methodChannelManager; - late final SplitEventMethodCallHandler _methodCallHandler; + final SplitioPlatform _platform; final String _matchingKey; final String? _bucketingKey; - late final SplitEventsListener _splitEventsListener; - - DefaultSplitClient( - this._methodChannelManager, this._matchingKey, this._bucketingKey) { - _methodCallHandler = - SplitEventMethodCallHandler(_matchingKey, _bucketingKey, this); - _splitEventsListener = - DefaultEventsListener(_methodChannelManager, _methodCallHandler); - } + DefaultSplitClient(this._platform, this._matchingKey, this._bucketingKey); @visibleForTesting - DefaultSplitClient.withEventListener(this._methodChannelManager, - this._matchingKey, this._bucketingKey, this._splitEventsListener); + DefaultSplitClient.withEventListener( + this._platform, this._matchingKey, this._bucketingKey); @override Future getTreatment(String splitName, [Map attributes = const {}]) async { - return await _methodChannelManager.invokeMethod( - 'getTreatment', - _buildParameters( - {'splitName': splitName, 'attributes': attributes})) ?? - _controlTreatment; + return _platform.getTreatment( + matchingKey: _matchingKey, + bucketingKey: _bucketingKey, + splitName: splitName, + attributes: attributes); } @override Future getTreatmentWithConfig(String splitName, [Map attributes = const {}]) async { - Map? treatment = (await _methodChannelManager.invokeMapMethod( - 'getTreatmentWithConfig', - _buildParameters( - {'splitName': splitName, 'attributes': attributes}))) - ?.entries - .first - .value; - if (treatment == null) { - return _controlResult; - } - - return SplitResult(treatment['treatment'], treatment['config']); + return _platform.getTreatmentWithConfig( + matchingKey: _matchingKey, + bucketingKey: _bucketingKey, + splitName: splitName, + attributes: attributes); } @override Future> getTreatments(List splitNames, [Map attributes = const {}]) async { - Map? treatments = await _methodChannelManager.invokeMapMethod( - 'getTreatments', - _buildParameters({'splitName': splitNames, 'attributes': attributes})); - - return treatments - ?.map((key, value) => MapEntry(key, value)) ?? - {for (var item in splitNames) item: _controlTreatment}; + return _platform.getTreatments( + matchingKey: _matchingKey, + bucketingKey: _bucketingKey, + splitNames: splitNames, + attributes: attributes); } @override Future> getTreatmentsWithConfig( List splitNames, [Map attributes = const {}]) async { - Map? treatments = await _methodChannelManager.invokeMapMethod( - 'getTreatmentsWithConfig', - _buildParameters({'splitName': splitNames, 'attributes': attributes})); - - return treatments?.map((key, value) => - MapEntry(key, SplitResult(value['treatment'], value['config']))) ?? - {for (var item in splitNames) item: _controlResult}; + return _platform.getTreatmentsWithConfig( + matchingKey: _matchingKey, + bucketingKey: _bucketingKey, + splitNames: splitNames, + attributes: attributes); } @override @@ -222,121 +196,101 @@ class DefaultSplitClient implements SplitClient { {String? trafficType, double? value, Map properties = const {}}) async { - var parameters = _buildParameters({'eventType': eventType}); - - if (trafficType != null) { - parameters['trafficType'] = trafficType; - } - - if (value != null) { - parameters['value'] = value; - } - - try { - return await _methodChannelManager.invokeMethod("track", parameters) - as bool; - } on Exception catch (_) { - return false; - } + return _platform.track( + matchingKey: _matchingKey, + bucketingKey: _bucketingKey, + eventType: eventType, + trafficType: trafficType, + value: value, + properties: properties); } @override Future setAttribute(String attributeName, dynamic value) async { - var result = await _methodChannelManager.invokeMethod('setAttribute', - _buildParameters({'attributeName': attributeName, 'value': value})); - - if (result is bool) { - return result; - } - - return false; + return _platform.setAttribute( + matchingKey: _matchingKey, + bucketingKey: _bucketingKey, + attributeName: attributeName, + value: value); } @override Future getAttribute(String attributeName) async { - return _methodChannelManager.invokeMethod( - 'getAttribute', _buildParameters({'attributeName': attributeName})); + return _platform.getAttribute( + matchingKey: _matchingKey, + bucketingKey: _bucketingKey, + attributeName: attributeName); } @override Future setAttributes(Map attributes) async { - var result = await _methodChannelManager.invokeMethod( - 'setAttributes', _buildParameters({'attributes': attributes})); - - if (result is bool) { - return result; - } - - return false; + return _platform.setAttributes( + matchingKey: _matchingKey, + bucketingKey: _bucketingKey, + attributes: attributes); } @override Future> getAttributes() async { - return (await _methodChannelManager.invokeMapMethod( - 'getAllAttributes', _buildParameters())) - ?.map((key, value) => MapEntry(key, value)) ?? - {}; + return _platform.getAllAttributes( + matchingKey: _matchingKey, bucketingKey: _bucketingKey); } @override Future removeAttribute(String attributeName) async { - return await _methodChannelManager.invokeMethod( - 'removeAttribute', _buildParameters({'attributeName': attributeName})); + return _platform.removeAttribute( + matchingKey: _matchingKey, + bucketingKey: _bucketingKey, + attributeName: attributeName); } @override Future clearAttributes() async { - return await _methodChannelManager.invokeMethod( - 'clearAttributes', _buildParameters()); + return _platform.clearAttributes( + matchingKey: _matchingKey, bucketingKey: _bucketingKey); } @override Future flush() async { - return _methodChannelManager.invokeMethod('flush', _buildParameters()); + return _platform.flush( + matchingKey: _matchingKey, bucketingKey: _bucketingKey); } @override Future destroy() async { - _splitEventsListener.destroy(); - return _methodChannelManager.invokeMethod('destroy', _buildParameters()); + return _platform.destroy( + matchingKey: _matchingKey, bucketingKey: _bucketingKey); } @override - Future whenReady() { - return _splitEventsListener.onReady(); + Future whenReady() async { + await _platform.onReady( + matchingKey: _matchingKey, bucketingKey: _bucketingKey); + + return Future.value(this); } @override - Future whenReadyFromCache() { - return _splitEventsListener.onReadyFromCache(); + Future whenReadyFromCache() async { + await _platform.onReadyFromCache( + matchingKey: _matchingKey, bucketingKey: _bucketingKey); + + return Future.value(this); } @override Stream whenUpdated() { - return _splitEventsListener.onUpdated(); + return _platform + .onUpdated(matchingKey: _matchingKey, bucketingKey: _bucketingKey) + ?.map((event) => this) ?? + const Stream.empty(); } @override - Future whenTimeout() { - return _splitEventsListener.onTimeout(); - } - - Map _getKeysMap() { - Map result = {'matchingKey': _matchingKey}; - - if (_bucketingKey != null) { - result.addAll({'bucketingKey': _bucketingKey!}); - } - - return result; - } - - Map _buildParameters( - [Map parameters = const {}]) { - Map result = {}; - result.addAll(parameters); - result.addAll(_getKeysMap()); + Future whenTimeout() async { + await _platform.onTimeout( + matchingKey: _matchingKey, bucketingKey: _bucketingKey); - return result; + return Future.value(this); } } diff --git a/splitio/lib/splitio.dart b/splitio/lib/splitio.dart index 254a486..203153e 100644 --- a/splitio/lib/splitio.dart +++ b/splitio/lib/splitio.dart @@ -1,31 +1,28 @@ import 'dart:async'; -import 'package:flutter/services.dart'; -import 'package:splitio/channel/method_channel_manager.dart'; -import 'package:splitio/impressions/impressions_method_call_handler.dart'; -import 'package:splitio/impressions/split_impression.dart'; -import 'package:splitio/method_call_handler.dart'; import 'package:splitio/split_client.dart'; -import 'package:splitio/split_configuration.dart'; -import 'package:splitio/split_view.dart'; +import 'package:splitio_platform_interface/split_configuration.dart'; +import 'package:splitio_platform_interface/split_impression.dart'; +import 'package:splitio_platform_interface/split_view.dart'; +import 'package:splitio_platform_interface/splitio_platform_interface.dart'; -export 'package:splitio/impressions/split_impression.dart'; export 'package:splitio/split_client.dart'; -export 'package:splitio/split_configuration.dart'; -export 'package:splitio/split_result.dart'; -export 'package:splitio/split_sync_config.dart'; -export 'package:splitio/split_view.dart'; +export 'package:splitio_platform_interface/split_configuration.dart'; +export 'package:splitio_platform_interface/split_impression.dart'; +export 'package:splitio_platform_interface/split_view.dart'; typedef ClientReadinessCallback = void Function(SplitClient splitClient); class Splitio { final String _apiKey; + final String _defaultMatchingKey; + late final String? _defaultBucketingKey; + late final SplitConfiguration? _splitConfiguration; - late final StreamMethodCallHandler _impressionsMethodCallHandler; - final MethodChannelManager _methodChannelManager = - MethodChannelManager(const MethodChannel('splitio')); + + final SplitioPlatform _platform = SplitioPlatform.instance; /// SDK instance constructor. /// @@ -41,8 +38,6 @@ class Splitio { {String? bucketingKey, SplitConfiguration? configuration}) { _defaultBucketingKey = bucketingKey; _splitConfiguration = configuration; - _impressionsMethodCallHandler = ImpressionsMethodCallHandler(); - _methodChannelManager.addHandler(_impressionsMethodCallHandler); _init(); } @@ -77,97 +72,66 @@ class Splitio { ClientReadinessCallback? onUpdated, ClientReadinessCallback? onTimeout}) { String? key = matchingKey ?? _defaultMatchingKey; + _platform.getClient(matchingKey: key, bucketingKey: bucketingKey); - var client = DefaultSplitClient(_methodChannelManager, key, bucketingKey); + var client = DefaultSplitClient(_platform, key, bucketingKey); if (onReady != null) { - client.whenReady().then((client) => onReady.call(client)); + _platform + .onReady(matchingKey: key, bucketingKey: bucketingKey) + ?.then((val) => onReady.call(client)); } if (onReadyFromCache != null) { - client - .whenReadyFromCache() - .then((client) => onReadyFromCache.call(client)); + _platform + .onReadyFromCache(matchingKey: key, bucketingKey: bucketingKey) + ?.then((val) => onReadyFromCache.call(client)); } if (onTimeout != null) { - client.whenTimeout().then((client) => onTimeout.call(client)); + _platform + .onTimeout(matchingKey: key, bucketingKey: bucketingKey) + ?.then((val) => onTimeout.call(client)); } if (onUpdated != null) { - client.whenUpdated().listen((client) => onUpdated.call(client)); + _platform + .onUpdated(matchingKey: key, bucketingKey: bucketingKey) + ?.listen((event) => onUpdated.call(client)); } - _methodChannelManager.invokeMethod( - 'getClient', _buildGetClientArguments(key, bucketingKey)); - return client; } Future> splitNames() async { - List splitNames = - await _methodChannelManager.invokeListMethod('splitNames') ?? - []; + List splitNames = await _platform.splitNames( + matchingKey: _defaultMatchingKey, bucketingKey: _defaultBucketingKey); return splitNames; } Future> splits() async { - List callResult = (await _methodChannelManager - .invokeListMethod>('splits') ?? - []); - - List splits = []; - for (var element in callResult) { - SplitView? splitView = SplitView.fromEntry(element); - if (splitView != null) { - splits.add(splitView); - } - } - - return Future.value(splits); + return _platform.splits( + matchingKey: _defaultMatchingKey, bucketingKey: _defaultBucketingKey); } /// If the impressionListener configuration has been enabled, /// generated impressions will be streamed here. Stream impressionsStream() { - return _impressionsMethodCallHandler.stream(); + return _platform.impressionsStream(); } Future split(String splitName) async { - Map? mapResult = await _methodChannelManager - .invokeMapMethod('split', {'splitName': splitName}); - - if (mapResult == null) { - return null; - } - - return SplitView.fromEntry(mapResult); + return _platform.split( + matchingKey: _defaultMatchingKey, + bucketingKey: _defaultBucketingKey, + splitName: splitName); } Future _init() { - Map arguments = { - 'apiKey': _apiKey, - 'matchingKey': _defaultMatchingKey, - 'sdkConfiguration': _splitConfiguration?.configurationMap ?? {}, - }; - - if (_defaultBucketingKey != null) { - arguments.addAll({'bucketingKey': _defaultBucketingKey}); - } - - return _methodChannelManager.invokeMethod('init', arguments); - } - - Map _buildGetClientArguments( - String key, String? bucketingKey) { - var arguments = { - 'matchingKey': key, - }; - - if (bucketingKey != null) { - arguments.addAll({'bucketingKey': bucketingKey}); - } - - return arguments; + return _platform.init( + apiKey: _apiKey, + matchingKey: _defaultMatchingKey, + bucketingKey: _defaultBucketingKey, + sdkConfiguration: _splitConfiguration); } } diff --git a/splitio/pubspec.yaml b/splitio/pubspec.yaml index a4f85a3..ddc7878 100644 --- a/splitio/pubspec.yaml +++ b/splitio/pubspec.yaml @@ -11,6 +11,8 @@ environment: dependencies: flutter: sdk: flutter + splitio_platform_interface: # ^1.0.0 + path: ../splitio_platform_interface dev_dependencies: flutter_test: diff --git a/splitio/test/splitio_client_test.dart b/splitio/test/splitio_client_test.dart index 52e7a44..0babeb2 100644 --- a/splitio/test/splitio_client_test.dart +++ b/splitio/test/splitio_client_test.dart @@ -1,7 +1,5 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:splitio/channel/method_channel_manager.dart'; -import 'package:splitio/events/split_events_listener.dart'; import 'package:splitio/split_client.dart'; void main() { diff --git a/splitio_platform_interface/lib/method_channel_platform.dart b/splitio_platform_interface/lib/method_channel_platform.dart new file mode 100644 index 0000000..95ff186 --- /dev/null +++ b/splitio_platform_interface/lib/method_channel_platform.dart @@ -0,0 +1,332 @@ + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:splitio_platform_interface/events/split_method_call_handler.dart'; +import 'package:splitio_platform_interface/impressions/impressions_method_call_handler.dart'; +import 'package:splitio_platform_interface/method_call_handler.dart'; +import 'package:splitio_platform_interface/split_configuration.dart'; +import 'package:splitio_platform_interface/split_impression.dart'; +import 'package:splitio_platform_interface/split_result.dart'; +import 'package:splitio_platform_interface/split_view.dart'; +import 'package:splitio_platform_interface/splitio_platform_interface.dart'; + +const String _controlTreatment = 'control'; +const SplitResult _controlResult = SplitResult(_controlTreatment, null); + +class MethodChannelPlatform extends SplitioPlatform { + + final MethodChannel _methodChannel = const MethodChannel('splitio'); + + final Map _handlers = {}; + + final ImpressionsMethodCallHandler _impressionsMethodCallHandler = ImpressionsMethodCallHandler(); + + MethodChannelPlatform() { + _methodChannel.setMethodCallHandler((call) => handle(call)); + } + + @visibleForTesting + Future handle(MethodCall call) async { + _impressionsMethodCallHandler.handle(call.method, call.arguments); + for (MethodCallHandler handler in _handlers.values) { + handler.handle(call.method, call.arguments); + } + } + + @override + Future init({required String apiKey, + required String matchingKey, + required String? bucketingKey, + SplitConfiguration? sdkConfiguration}) { + Map arguments = { + 'apiKey': apiKey, + 'matchingKey': matchingKey, + 'sdkConfiguration': sdkConfiguration?.configurationMap ?? {}, + }; + + if (bucketingKey != null) { + arguments.addAll({'bucketingKey': bucketingKey}); + } + + return _methodChannel.invokeMethod('init', arguments); + } + + @override + Future getClient( + {required String matchingKey, required String? bucketingKey}) { + _handlers.addAll( + { + _buildMapKey(matchingKey, bucketingKey): SplitEventMethodCallHandler( + matchingKey, bucketingKey) + }); + + return _methodChannel.invokeMethod( + 'getClient', _buildParameters(matchingKey, bucketingKey)); + } + + @override + Future clearAttributes( + {required String matchingKey, required String? bucketingKey}) async { + return await _methodChannel.invokeMethod( + 'clearAttributes', _buildParameters(matchingKey, bucketingKey)); + } + + @override + Future destroy( + {required String matchingKey, required String? bucketingKey}) async { + var handlerKey = _buildMapKey(matchingKey, bucketingKey); + _handlers[handlerKey]?.destroy(); + _handlers.remove(handlerKey); + + return await _methodChannel.invokeMethod( + 'destroy', _buildParameters(matchingKey, bucketingKey)); + } + + @override + Future flush( + {required String matchingKey, required String? bucketingKey}) async { + return await _methodChannel.invokeMethod( + 'flush', _buildParameters(matchingKey, bucketingKey)); + } + + @override + Future> getAllAttributes( + {required String matchingKey, required String? bucketingKey}) async { + return (await _methodChannel.invokeMapMethod( + 'getAllAttributes', _buildParameters(matchingKey, bucketingKey))) + ?.map((key, value) => MapEntry(key, value)) ?? + {}; + } + + @override + Future getAttribute({required String matchingKey, + required String? bucketingKey, + required String attributeName}) { + return _methodChannel.invokeMethod( + 'getAttribute', _buildParameters( + matchingKey, bucketingKey, {'attributeName': attributeName})); + } + + @override + Future getTreatment({required String matchingKey, + required String? bucketingKey, + required String splitName, + Map attributes = const {}}) async { + return await _methodChannel.invokeMethod( + 'getTreatment', + _buildParameters(matchingKey, bucketingKey, + {'splitName': splitName, 'attributes': attributes})) ?? + _controlTreatment; + } + + @override + Future getTreatmentWithConfig({required String matchingKey, + required String? bucketingKey, + required String splitName, + Map attributes = const {}}) async { + Map? treatment = (await _methodChannel.invokeMapMethod( + 'getTreatmentWithConfig', + _buildParameters(matchingKey, bucketingKey + {'splitName': splitName, 'attributes': attributes}))) + ?.entries + .first + .value; + if (treatment == null) { + return _controlResult; + } + + return SplitResult(treatment['treatment'], treatment['config']); + } + + @override + Future> getTreatments({required String matchingKey, + required String? bucketingKey, + required List splitNames, + Map attributes = const {}}) async { + Map? treatments = await _methodChannel.invokeMapMethod( + 'getTreatments', + _buildParameters(matchingKey, bucketingKey, + {'splitName': splitNames, 'attributes': attributes})); + + return treatments + ?.map((key, value) => MapEntry(key, value)) ?? + {for (var item in splitNames) item: _controlTreatment}; + } + + @override + Future> getTreatmentsWithConfig( + {required String matchingKey, + required String? bucketingKey, + required List splitNames, + Map attributes = const {}}) async { + Map? treatments = await _methodChannel.invokeMapMethod( + 'getTreatmentsWithConfig', + _buildParameters(matchingKey, bucketingKey, + {'splitName': splitNames, 'attributes': attributes})); + + return treatments?.map((key, value) => + MapEntry(key, SplitResult(value['treatment'], value['config']))) ?? + {for (var item in splitNames) item: _controlResult}; + } + + @override + Future removeAttribute({required String matchingKey, + required String? bucketingKey, + required String attributeName}) async { + return await _methodChannel.invokeMethod( + 'removeAttribute', _buildParameters( + matchingKey, bucketingKey, {'attributeName': attributeName})); + } + + @override + Future setAttribute({required String matchingKey, + required String? bucketingKey, + required String attributeName, + required value}) async { + var result = await _methodChannel.invokeMethod('setAttribute', + _buildParameters(matchingKey, bucketingKey, + {'attributeName': attributeName, 'value': value})); + + if (result is bool) { + return result; + } + + return false; + } + + @override + Future setAttributes({required String matchingKey, + required String? bucketingKey, + required Map attributes}) async { + var result = await _methodChannel.invokeMethod( + 'setAttributes', _buildParameters( + matchingKey, bucketingKey, {'attributes': attributes})); + + if (result is bool) { + return result; + } + + return false; + } + + @override + Future split({required String matchingKey, + required String? bucketingKey, + required String splitName}) async { + Map? mapResult = + await _methodChannel.invokeMapMethod('split', {'splitName': splitName}); + + if (mapResult == null) { + return null; + } + + return SplitView.fromEntry(mapResult); + } + + @override + Future> splitNames( + {required String matchingKey, required String? bucketingKey}) async { + List splitNames = + await _methodChannel.invokeListMethod('splitNames') ?? []; + + return splitNames; + } + + @override + Future> splits( + {required String matchingKey, required String? bucketingKey}) async { + List callResult = (await _methodChannel + .invokeListMethod>('splits') ?? + []); + + List splits = []; + for (var element in callResult) { + SplitView? splitView = SplitView.fromEntry(element); + if (splitView != null) { + splits.add(splitView); + } + } + + return Future.value(splits); + } + + @override + Future track({required String matchingKey, + required String? bucketingKey, + required String eventType, + String? trafficType, + double? value, + Map properties = const {}}) async { + var parameters = _buildParameters( + matchingKey, bucketingKey, {'eventType': eventType}); + + if (trafficType != null) { + parameters['trafficType'] = trafficType; + } + + if (value != null) { + parameters['value'] = value; + } + + try { + return await _methodChannel.invokeMethod("track", parameters) + as bool; + } on Exception catch (_) { + return false; + } + } + + Map _buildParameters(String matchingKey, + String? bucketingKey, + [Map parameters = const {}]) { + Map result = {}; + result.addAll(parameters); + result.addAll(_getKeysMap(matchingKey, bucketingKey)); + + return result; + } + + Map _getKeysMap(String matchingKey, String? bucketingKey) { + Map result = {'matchingKey': matchingKey}; + + if (bucketingKey != null) { + result.addAll({'bucketingKey': bucketingKey}); + } + + return result; + } + + @override + Future? onReady( + {required String matchingKey, required String? bucketingKey}) { + return _handlers[_buildMapKey(matchingKey, bucketingKey)]?.onReady(); + } + + @override + Future? onReadyFromCache( + {required String matchingKey, required String? bucketingKey}) { + return _handlers[_buildMapKey(matchingKey, bucketingKey)] + ?.onReadyFromCache(); + } + + @override + Stream? onUpdated( + {required String matchingKey, required String? bucketingKey}) { + return _handlers[_buildMapKey(matchingKey, bucketingKey)]?.onUpdated(); + } + + @override + Future? onTimeout( + {required String matchingKey, required String? bucketingKey}) { + return _handlers[_buildMapKey(matchingKey, bucketingKey)]?.onTimeout(); + } + + @override + Stream impressionsStream() { + return _impressionsMethodCallHandler.stream(); + } + + String _buildMapKey(String matchingKey, String? bucketingKey) { + return '${matchingKey}_$bucketingKey'; + } +} diff --git a/splitio_platform_interface/lib/split_configuration.dart b/splitio_platform_interface/lib/split_configuration.dart index 5e93df9..80c55fb 100644 --- a/splitio_platform_interface/lib/split_configuration.dart +++ b/splitio_platform_interface/lib/split_configuration.dart @@ -1,4 +1,4 @@ -import 'package:splitio/split_sync_config.dart'; +import 'package:splitio_platform_interface/split_sync_config.dart'; class SplitConfiguration { final Map configurationMap = {}; diff --git a/splitio_platform_interface/test/impressions_method_call_handler_test.dart b/splitio_platform_interface/test/impressions_method_call_handler_test.dart index f919f9a..6ae0cbb 100644 --- a/splitio_platform_interface/test/impressions_method_call_handler_test.dart +++ b/splitio_platform_interface/test/impressions_method_call_handler_test.dart @@ -1,8 +1,9 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:splitio/impressions/impressions_method_call_handler.dart'; import 'package:splitio/impressions/split_impression.dart'; +import '../lib/impressions/impressions_method_call_handler.dart'; + void main() { test('correct impressionLog method call emits value on stream', () async { const Map sourceMap = { @@ -34,8 +35,9 @@ void main() { }), ); - impressionsMethodCallHandler - .handle(const MethodCall('impressionLog', sourceMap)); + const methodCall = MethodCall('impressionLog', sourceMap); + impressionsMethodCallHandler.handle( + methodCall.method, methodCall.arguments); }); test('other method names are ignored', () async { @@ -47,6 +49,8 @@ void main() { expect(event, null); }, count: 0), ); - impressionsMethodCallHandler.handle(const MethodCall('clientReady')); + const methodCall = MethodCall('clientReady'); + impressionsMethodCallHandler.handle( + methodCall.method, methodCall.arguments); }); } diff --git a/splitio_platform_interface/test/split_event_listener_test.dart b/splitio_platform_interface/test/split_event_listener_test.dart index 3099e8f..f06c463 100644 --- a/splitio_platform_interface/test/split_event_listener_test.dart +++ b/splitio_platform_interface/test/split_event_listener_test.dart @@ -3,9 +3,10 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:splitio/channel/method_channel_manager.dart'; import 'package:splitio/events/split_events_listener.dart'; import 'package:splitio/events/split_method_call_handler.dart'; -import 'package:splitio/split_client.dart'; import 'package:splitio/split_result.dart'; +import '../lib/split_client.dart'; + void main() { const MethodChannel _channel = MethodChannel('splitio'); @@ -25,7 +26,8 @@ void main() { setUp(() { _channel.setMockMethodCallHandler((MethodCall methodCall) { - splitEventMethodCallHandler.handle(methodCall); + splitEventMethodCallHandler.handle( + methodCall.method, methodCall.arguments); }); });