From 09cda0b2bd8cfc00b3c9287110d02701d24fb464 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Wed, 31 Aug 2022 11:47:05 -0300 Subject: [PATCH 01/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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); }); }); From 3f24ae65a6d966dc660a7d1dd56d636c4ec10ca0 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Mon, 5 Sep 2022 17:51:06 -0300 Subject: [PATCH 18/23] Upgrade tests --- splitio/lib/split_client.dart | 5 - splitio/lib/splitio.dart | 15 +- splitio/test/splitio_client_test.dart | 232 ++-------- splitio/test/splitio_platform_stub.dart | 346 ++++++++++++++ splitio/test/splitio_test.dart | 59 +-- .../lib/method_channel_platform.dart | 19 +- .../lib/split_sync_config.dart | 13 +- .../lib/splitio_platform_interface.dart | 10 +- .../impressions_method_call_handler_test.dart | 2 +- ...ethod_channel_platform_interface_test.dart | 438 ++++++++++++++++++ .../test/split_event_listener_test.dart | 170 +------ .../test/split_impression_test.dart | 2 +- .../test/split_view_test.dart | 2 +- .../test/splitio_configuration_test.dart | 11 +- 14 files changed, 903 insertions(+), 421 deletions(-) create mode 100644 splitio/test/splitio_platform_stub.dart create mode 100644 splitio_platform_interface/test/method_channel_platform_interface_test.dart diff --git a/splitio/lib/split_client.dart b/splitio/lib/split_client.dart index ec333b6..673c978 100644 --- a/splitio/lib/split_client.dart +++ b/splitio/lib/split_client.dart @@ -1,4 +1,3 @@ -import 'package:flutter/foundation.dart'; import 'package:splitio_platform_interface/split_result.dart'; import 'package:splitio_platform_interface/splitio_platform_interface.dart'; @@ -146,10 +145,6 @@ class DefaultSplitClient implements SplitClient { DefaultSplitClient(this._platform, this._matchingKey, this._bucketingKey); - @visibleForTesting - DefaultSplitClient.withEventListener( - this._platform, this._matchingKey, this._bucketingKey); - @override Future getTreatment(String splitName, [Map attributes = const {}]) async { diff --git a/splitio/lib/splitio.dart b/splitio/lib/splitio.dart index 203153e..d5620cb 100644 --- a/splitio/lib/splitio.dart +++ b/splitio/lib/splitio.dart @@ -18,9 +18,9 @@ class Splitio { final String _defaultMatchingKey; - late final String? _defaultBucketingKey; + late String? _defaultBucketingKey; - late final SplitConfiguration? _splitConfiguration; + late SplitConfiguration? _splitConfiguration; final SplitioPlatform _platform = SplitioPlatform.instance; @@ -103,15 +103,13 @@ class Splitio { } Future> splitNames() async { - List splitNames = await _platform.splitNames( - matchingKey: _defaultMatchingKey, bucketingKey: _defaultBucketingKey); + List splitNames = await _platform.splitNames(); return splitNames; } Future> splits() async { - return _platform.splits( - matchingKey: _defaultMatchingKey, bucketingKey: _defaultBucketingKey); + return _platform.splits(); } /// If the impressionListener configuration has been enabled, @@ -121,10 +119,7 @@ class Splitio { } Future split(String splitName) async { - return _platform.split( - matchingKey: _defaultMatchingKey, - bucketingKey: _defaultBucketingKey, - splitName: splitName); + return _platform.split(splitName: splitName); } Future _init() { diff --git a/splitio/test/splitio_client_test.dart b/splitio/test/splitio_client_test.dart index 0babeb2..1e2e144 100644 --- a/splitio/test/splitio_client_test.dart +++ b/splitio/test/splitio_client_test.dart @@ -1,59 +1,24 @@ -import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:splitio/split_client.dart'; +import 'package:splitio_platform_interface/splitio_platform_interface.dart'; -void main() { - const MethodChannel _channel = MethodChannel('splitio'); - - String methodName = ''; - dynamic methodArguments; +import 'splitio_platform_stub.dart'; +void main() { TestWidgetsFlutterBinding.ensureInitialized(); - MethodChannelManager _methodChannelWrapper = MethodChannelManager(_channel); + SplitioPlatformStub _platform = SplitioPlatformStub(); - SplitClient _getClient([SplitEventsListener? splitEventsListener]) { - if (splitEventsListener != null) { - return DefaultSplitClient.withEventListener(_methodChannelWrapper, - 'matching-key', 'bucketing-key', splitEventsListener); + SplitClient _getClient([SplitioPlatform? platform]) { + if (platform != null) { + return DefaultSplitClient(platform, 'matching-key', 'bucketing-key'); } - return DefaultSplitClient( - _methodChannelWrapper, 'matching-key', 'bucketing-key'); + return DefaultSplitClient(_platform, 'matching-key', 'bucketing-key'); } setUp(() { - _channel.setMockMethodCallHandler((MethodCall methodCall) async { - methodName = methodCall.method; - methodArguments = methodCall.arguments; - - switch (methodCall.method) { - case 'getTreatment': - return ''; - case 'getTreatments': - return {'split1': 'on', 'split2': 'off'}; - case 'getTreatmentsWithConfig': - return { - 'split1': {'treatment': 'on', 'config': null}, - 'split2': {'treatment': 'off', 'config': null} - }; - case 'track': - return true; - case 'getAttribute': - return true; - case 'getAllAttributes': - return { - 'attr_1': true, - 'attr_2': ['list-element'], - 'attr_3': 28.20 - }; - case 'setAttribute': - case 'setAttributes': - case 'removeAttribute': - case 'clearAttributes': - return true; - } - }); + _platform = SplitioPlatformStub(); }); group('evaluation', () { @@ -62,8 +27,8 @@ void main() { client.getTreatment('split'); - expect(methodName, 'getTreatment'); - expect(methodArguments, { + expect(_platform.methodName, 'getTreatment'); + expect(_platform.methodArguments, { 'splitName': 'split', 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', @@ -76,8 +41,8 @@ void main() { client.getTreatment('split', {'attr1': true}); - expect(methodName, 'getTreatment'); - expect(methodArguments, { + expect(_platform.methodName, 'getTreatment'); + expect(_platform.methodArguments, { 'splitName': 'split', 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', @@ -90,8 +55,8 @@ void main() { client.getTreatments(['split1', 'split2']); - expect(methodName, 'getTreatments'); - expect(methodArguments, { + expect(_platform.methodName, 'getTreatments'); + expect(_platform.methodArguments, { 'splitName': ['split1', 'split2'], 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', @@ -104,8 +69,8 @@ void main() { client.getTreatments(['split1', 'split2'], {'attr1': true}); - expect(methodName, 'getTreatments'); - expect(methodArguments, { + expect(_platform.methodName, 'getTreatments'); + expect(_platform.methodArguments, { 'splitName': ['split1', 'split2'], 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', @@ -118,8 +83,8 @@ void main() { client.getTreatmentWithConfig('split1', {'attr1': true}); - expect(methodName, 'getTreatmentWithConfig'); - expect(methodArguments, { + expect(_platform.methodName, 'getTreatmentWithConfig'); + expect(_platform.methodArguments, { 'splitName': 'split1', 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', @@ -132,8 +97,8 @@ void main() { client.getTreatmentWithConfig('split1'); - expect(methodName, 'getTreatmentWithConfig'); - expect(methodArguments, { + expect(_platform.methodName, 'getTreatmentWithConfig'); + expect(_platform.methodArguments, { 'splitName': 'split1', 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', @@ -146,8 +111,8 @@ void main() { client.getTreatmentsWithConfig(['split1', 'split2']); - expect(methodName, 'getTreatmentsWithConfig'); - expect(methodArguments, { + expect(_platform.methodName, 'getTreatmentsWithConfig'); + expect(_platform.methodArguments, { 'splitName': ['split1', 'split2'], 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', @@ -160,8 +125,8 @@ void main() { client.getTreatmentsWithConfig(['split1', 'split2'], {'attr1': true}); - expect(methodName, 'getTreatmentsWithConfig'); - expect(methodArguments, { + expect(_platform.methodName, 'getTreatmentsWithConfig'); + expect(_platform.methodArguments, { 'splitName': ['split1', 'split2'], 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', @@ -175,8 +140,8 @@ void main() { SplitClient client = _getClient(); client.track('my_event', trafficType: 'my_traffic_type', value: 25.10); - expect(methodName, 'track'); - expect(methodArguments, { + expect(_platform.methodName, 'track'); + expect(_platform.methodArguments, { 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', 'eventType': 'my_event', @@ -189,8 +154,8 @@ void main() { SplitClient client = _getClient(); client.track('my_event', value: 25.10); - expect(methodName, 'track'); - expect(methodArguments, { + expect(_platform.methodName, 'track'); + expect(_platform.methodArguments, { 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', 'eventType': 'my_event', @@ -202,8 +167,8 @@ void main() { SplitClient client = _getClient(); client.track('my_event', trafficType: 'my_traffic_type'); - expect(methodName, 'track'); - expect(methodArguments, { + expect(_platform.methodName, 'track'); + expect(_platform.methodArguments, { 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', 'eventType': 'my_event', @@ -217,8 +182,8 @@ void main() { SplitClient client = _getClient(); client.getAttribute('attribute-name'); - expect(methodName, 'getAttribute'); - expect(methodArguments, { + expect(_platform.methodName, 'getAttribute'); + expect(_platform.methodArguments, { 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', 'attributeName': 'attribute-name', @@ -229,8 +194,8 @@ void main() { SplitClient client = _getClient(); client.getAttributes(); - expect(methodName, 'getAllAttributes'); - expect(methodArguments, { + expect(_platform.methodName, 'getAllAttributes'); + expect(_platform.methodArguments, { 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', }); @@ -240,8 +205,8 @@ void main() { SplitClient client = _getClient(); client.setAttribute('my_attr', 'attr_value'); - expect(methodName, 'setAttribute'); - expect(methodArguments, { + expect(_platform.methodName, 'setAttribute'); + expect(_platform.methodArguments, { 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', 'attributeName': 'my_attr', @@ -258,8 +223,8 @@ void main() { 'string_attr': 'attr-value', 'list_attr': ['one', 'two'], }); - expect(methodName, 'setAttributes'); - expect(methodArguments, { + expect(_platform.methodName, 'setAttributes'); + expect(_platform.methodArguments, { 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', 'attributes': { @@ -275,8 +240,8 @@ void main() { SplitClient client = _getClient(); client.removeAttribute('attr-name'); - expect(methodName, 'removeAttribute'); - expect(methodArguments, { + expect(_platform.methodName, 'removeAttribute'); + expect(_platform.methodArguments, { 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', 'attributeName': 'attr-name', @@ -287,8 +252,8 @@ void main() { SplitClient client = _getClient(); client.clearAttributes(); - expect(methodName, 'clearAttributes'); - expect(methodArguments, { + expect(_platform.methodName, 'clearAttributes'); + expect(_platform.methodArguments, { 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', }); @@ -298,122 +263,19 @@ void main() { SplitClient client = _getClient(); client.flush(); - expect(methodName, 'flush'); - expect(methodArguments, { + expect(_platform.methodName, 'flush'); + expect(_platform.methodArguments, { 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', }); }); test('destroy', () async { - var splitEventsListenerStub = SplitEventsListenerStub(); - SplitClient client = _getClient(splitEventsListenerStub); - - client.destroy(); - - expect(splitEventsListenerStub.calledMethods['destroy'], 1); - - client.destroy(); - expect(methodName, 'destroy'); - expect(methodArguments, { - 'matchingKey': 'matching-key', - 'bucketingKey': 'bucketing-key', - }); + // TODO }); }); group('events', () { - test('onReady is returned from events listener', () { - var splitEventsListenerStub = SplitEventsListenerStub(); - SplitClient client = _getClient(splitEventsListenerStub); - splitEventsListenerStub.attachClient(client); - - var future = client.whenReady().then((value) => client == value); - expect(splitEventsListenerStub.calledMethods['onReady'], 1); - expect(splitEventsListenerStub.calledMethods['onReadyFromCache'], null); - expect(splitEventsListenerStub.calledMethods['onTimeout'], null); - expect(splitEventsListenerStub.calledMethods['onUpdated'], null); - expect(future, completion(equals(true))); - }); - - test('onReadyFromCache is returned from events listener', () { - var splitEventsListenerStub = SplitEventsListenerStub(); - SplitClient client = _getClient(splitEventsListenerStub); - splitEventsListenerStub.attachClient(client); - - var future = client.whenReadyFromCache().then((value) => client == value); - expect(splitEventsListenerStub.calledMethods['onReady'], null); - expect(splitEventsListenerStub.calledMethods['onReadyFromCache'], 1); - expect(splitEventsListenerStub.calledMethods['onTimeout'], null); - expect(splitEventsListenerStub.calledMethods['onUpdated'], null); - expect(future, completion(equals(true))); - }); - - test('onTimeout is returned from events listener', () { - var splitEventsListenerStub = SplitEventsListenerStub(); - SplitClient client = _getClient(splitEventsListenerStub); - splitEventsListenerStub.attachClient(client); - - var future = client.whenTimeout().then((value) => client == value); - expect(splitEventsListenerStub.calledMethods['onReady'], null); - expect(splitEventsListenerStub.calledMethods['onReadyFromCache'], null); - expect(splitEventsListenerStub.calledMethods['onTimeout'], 1); - expect(splitEventsListenerStub.calledMethods['onUpdated'], null); - expect(future, completion(equals(true))); - }); - - test('onUpdated is returned from events listener', () { - var splitEventsListenerStub = SplitEventsListenerStub(); - SplitClient client = _getClient(splitEventsListenerStub); - splitEventsListenerStub.attachClient(client); - - var future = - client.whenUpdated().first.then(((value) => client == value)); - expect(splitEventsListenerStub.calledMethods['onReady'], null); - expect(splitEventsListenerStub.calledMethods['onReadyFromCache'], null); - expect(splitEventsListenerStub.calledMethods['onTimeout'], null); - expect(splitEventsListenerStub.calledMethods['onUpdated'], 1); - expect(future, completion(equals(true))); - }); + // TODO }); } - -class SplitEventsListenerStub extends SplitEventsListener { - Map calledMethods = {}; - - late final Future _clientFuture; - - void attachClient(SplitClient splitClient) { - _clientFuture = Future.value(splitClient); - } - - @override - Future onReady() { - calledMethods.update('onReady', (value) => value + 1, ifAbsent: () => 1); - return _clientFuture; - } - - @override - Future onReadyFromCache() { - calledMethods.update('onReadyFromCache', (value) => value + 1, - ifAbsent: () => 1); - return _clientFuture; - } - - @override - Future onTimeout() { - calledMethods.update('onTimeout', (value) => value + 1, ifAbsent: () => 1); - return _clientFuture; - } - - @override - Stream onUpdated() { - calledMethods.update('onUpdated', (value) => value + 1, ifAbsent: () => 1); - return Stream.fromFuture(_clientFuture); - } - - @override - void destroy() { - calledMethods.update('destroy', (value) => value + 1, ifAbsent: () => 1); - } -} diff --git a/splitio/test/splitio_platform_stub.dart b/splitio/test/splitio_platform_stub.dart new file mode 100644 index 0000000..13e27f7 --- /dev/null +++ b/splitio/test/splitio_platform_stub.dart @@ -0,0 +1,346 @@ +import 'package:plugin_platform_interface/plugin_platform_interface.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'; + +class SplitioPlatformStub + with MockPlatformInterfaceMixin + implements SplitioPlatform { + String methodName = ''; + Map methodArguments = {}; + + @override + Future clearAttributes( + {required String matchingKey, required String? bucketingKey}) { + methodName = 'clearAttributes'; + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey + }; + return Future.value(true); + } + + @override + Future destroy( + {required String matchingKey, required String? bucketingKey}) { + methodName = 'destroy'; + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey + }; + + return Future.value(); + } + + @override + Future flush( + {required String matchingKey, required String? bucketingKey}) { + methodName = 'flush'; + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey + }; + return Future.value(); + } + + @override + Future> getAllAttributes( + {required String matchingKey, required String? bucketingKey}) { + methodName = 'getAllAttributes'; + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey + }; + + return Future.value({}); + } + + @override + Future getAttribute( + {required String matchingKey, + required String? bucketingKey, + required String attributeName}) { + methodName = 'getAttribute'; + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey, + 'attributeName': attributeName + }; + + return Future.value(); + } + + @override + Future getClient( + {required String matchingKey, required String? bucketingKey}) { + methodName = 'getClient'; + + methodArguments = { + 'matchingKey': matchingKey, + }; + + if (bucketingKey != null) { + methodArguments['bucketingKey'] = bucketingKey; + } + + return Future.value(); + } + + @override + Future getTreatment( + {required String matchingKey, + required String? bucketingKey, + required String splitName, + Map attributes = const {}}) { + methodName = 'getTreatment'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey, + 'splitName': splitName, + 'attributes': attributes + }; + + return Future.value(''); + } + + @override + Future getTreatmentWithConfig( + {required String matchingKey, + required String? bucketingKey, + required String splitName, + Map attributes = const {}}) { + methodName = 'getTreatmentWithConfig'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey, + 'splitName': splitName, + 'attributes': attributes, + }; + + return Future.value(const SplitResult('on', null)); + } + + @override + Future> getTreatments( + {required String matchingKey, + required String? bucketingKey, + required List splitNames, + Map attributes = const {}}) { + methodName = 'getTreatments'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey, + 'splitName': splitNames, + 'attributes': attributes, + }; + + return Future.value({}); + } + + @override + Future> getTreatmentsWithConfig( + {required String matchingKey, + required String? bucketingKey, + required List splitNames, + Map attributes = const {}}) { + methodName = 'getTreatmentsWithConfig'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey, + 'splitName': splitNames, + 'attributes': attributes, + }; + + return Future.value({}); + } + + @override + Stream impressionsStream() { + methodName = 'impressionsStream'; + + return const Stream.empty(); + } + + @override + Future init( + {required String apiKey, + required String matchingKey, + required String? bucketingKey, + SplitConfiguration? sdkConfiguration}) { + methodName = 'init'; + + methodArguments = { + 'matchingKey': matchingKey, + 'apiKey': apiKey, + 'sdkConfiguration': sdkConfiguration?.configurationMap ?? {}, + }; + + if (bucketingKey != null) { + methodArguments['bucketingKey'] = bucketingKey; + } + + return Future.value(); + } + + @override + Future? onReady( + {required String matchingKey, required String? bucketingKey}) { + methodName = 'onReady'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey + }; + + return Future.value(); + } + + @override + Future? onReadyFromCache( + {required String matchingKey, required String? bucketingKey}) { + methodName = 'onReadyFromCache'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey + }; + + return Future.value(); + } + + @override + Future? onTimeout( + {required String matchingKey, required String? bucketingKey}) { + methodName = 'onTimeout'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey + }; + + return Future.value(); + } + + @override + Stream? onUpdated( + {required String matchingKey, required String? bucketingKey}) { + methodName = 'onUpdated'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey + }; + + return const Stream.empty(); + } + + @override + Future removeAttribute( + {required String matchingKey, + required String? bucketingKey, + required String attributeName}) { + methodName = 'removeAttribute'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey, + 'attributeName': attributeName + }; + + return Future.value(true); + } + + @override + Future setAttribute( + {required String matchingKey, + required String? bucketingKey, + required String attributeName, + required value}) { + methodName = 'setAttribute'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey, + 'attributeName': attributeName, + 'value': value, + }; + + return Future.value(true); + } + + @override + Future setAttributes( + {required String matchingKey, + required String? bucketingKey, + required Map attributes}) { + methodName = 'setAttributes'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey, + 'attributes': attributes + }; + + return Future.value(true); + } + + @override + Future split({required String splitName}) { + methodName = 'split'; + + methodArguments = {'splitName': splitName}; + + return Future.value(null); + } + + @override + Future> splitNames() { + methodName = 'splitNames'; + + return Future.value([]); + } + + @override + Future> splits() { + methodName = 'splits'; + + return Future.value([]); + } + + @override + Future track( + {required String matchingKey, + required String? bucketingKey, + required String eventType, + String? trafficType, + double? value, + Map properties = const {}}) { + methodName = 'track'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey, + 'eventType': eventType + }; + + if (properties.isNotEmpty) { + methodArguments['properties'] = properties; + } + + if (trafficType != null) { + methodArguments['trafficType'] = trafficType; + } + + if (value != null) { + methodArguments['value'] = value; + } + + return Future.value(true); + } +} diff --git a/splitio/test/splitio_test.dart b/splitio/test/splitio_test.dart index 6d46e08..37cd1c5 100644 --- a/splitio/test/splitio_test.dart +++ b/splitio/test/splitio_test.dart @@ -1,37 +1,20 @@ -import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:splitio/splitio.dart'; +import 'package:splitio_platform_interface/splitio_platform_interface.dart'; -void main() { - const MethodChannel channel = MethodChannel('splitio'); - - var methodArguments; - var methodName; +import 'splitio_platform_stub.dart'; +void main() { TestWidgetsFlutterBinding.ensureInitialized(); - setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - methodName = methodCall.method; - methodArguments = methodCall.arguments; - - if (methodCall.method == 'splitNames') { - List emptyList = []; - return Future.value(emptyList); - } - }); - }); - - tearDown(() async { - methodName = null; - methodArguments = null; - }); + SplitioPlatformStub _platform = SplitioPlatformStub(); + SplitioPlatform.instance = _platform; group('initialization', () { test('init with matching key only', () { Splitio('api-key', 'matching-key'); - expect(methodName, 'init'); - expect(methodArguments, { + expect(_platform.methodName, 'init'); + expect(_platform.methodArguments, { 'apiKey': 'api-key', 'matchingKey': 'matching-key', 'sdkConfiguration': {} @@ -40,8 +23,8 @@ void main() { test('init with bucketing key', () { Splitio('api-key', 'matching-key', bucketingKey: 'bucketing-key'); - expect(methodName, 'init'); - expect(methodArguments, { + expect(_platform.methodName, 'init'); + expect(_platform.methodArguments, { 'apiKey': 'api-key', 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', @@ -54,8 +37,8 @@ void main() { bucketingKey: 'bucketing-key', configuration: SplitConfiguration(enableDebug: true, streamingEnabled: false)); - expect(methodName, 'init'); - expect(methodArguments, { + expect(_platform.methodName, 'init'); + expect(_platform.methodArguments, { 'apiKey': 'api-key', 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', @@ -70,8 +53,8 @@ void main() { splitio.client(); - expect(methodName, 'getClient'); - expect(methodArguments, {'matchingKey': 'matching-key'}); + expect(_platform.methodName, 'getClient'); + expect(_platform.methodArguments, {'matchingKey': 'matching-key'}); }); test('get client with new matching key', () { @@ -79,8 +62,8 @@ void main() { splitio.client(matchingKey: 'new-matching-key'); - expect(methodName, 'getClient'); - expect(methodArguments, {'matchingKey': 'new-matching-key'}); + expect(_platform.methodName, 'getClient'); + expect(_platform.methodArguments, {'matchingKey': 'new-matching-key'}); }); test('get client with new matching key and bucketing key', () { @@ -89,8 +72,8 @@ void main() { splitio.client( matchingKey: 'new-matching-key', bucketingKey: 'bucketing-key'); - expect(methodName, 'getClient'); - expect(methodArguments, + expect(_platform.methodName, 'getClient'); + expect(_platform.methodArguments, {'matchingKey': 'new-matching-key', 'bucketingKey': 'bucketing-key'}); }); }); @@ -100,22 +83,22 @@ void main() { var splitio = Splitio('api-key', 'matching-key'); splitio.splitNames(); - expect(methodName, 'splitNames'); + expect(_platform.methodName, 'splitNames'); }); test('get splits', () { var splitio = Splitio('api-key', 'matching-key'); splitio.splits(); - expect(methodName, 'splits'); + expect(_platform.methodName, 'splits'); }); test('get split', () { var splitio = Splitio('api-key', 'matching-key'); splitio.split('my_split'); - expect(methodName, 'split'); - expect(methodArguments, {'splitName': 'my_split'}); + expect(_platform.methodName, 'split'); + expect(_platform.methodArguments, {'splitName': 'my_split'}); }); }); } diff --git a/splitio_platform_interface/lib/method_channel_platform.dart b/splitio_platform_interface/lib/method_channel_platform.dart index 95ff186..5a33fd3 100644 --- a/splitio_platform_interface/lib/method_channel_platform.dart +++ b/splitio_platform_interface/lib/method_channel_platform.dart @@ -1,5 +1,4 @@ -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'; @@ -15,18 +14,17 @@ const SplitResult _controlResult = SplitResult(_controlTreatment, null); class MethodChannelPlatform extends SplitioPlatform { - final MethodChannel _methodChannel = const MethodChannel('splitio'); + late MethodChannel _methodChannel = const MethodChannel('splitio'); final Map _handlers = {}; final ImpressionsMethodCallHandler _impressionsMethodCallHandler = ImpressionsMethodCallHandler(); MethodChannelPlatform() { - _methodChannel.setMethodCallHandler((call) => handle(call)); + _methodChannel.setMethodCallHandler((call) => _handle(call)); } - @visibleForTesting - Future handle(MethodCall call) async { + Future _handle(MethodCall call) async { _impressionsMethodCallHandler.handle(call.method, call.arguments); for (MethodCallHandler handler in _handlers.values) { handler.handle(call.method, call.arguments); @@ -210,8 +208,7 @@ class MethodChannelPlatform extends SplitioPlatform { } @override - Future split({required String matchingKey, - required String? bucketingKey, + Future split({ required String splitName}) async { Map? mapResult = await _methodChannel.invokeMapMethod('split', {'splitName': splitName}); @@ -224,8 +221,7 @@ class MethodChannelPlatform extends SplitioPlatform { } @override - Future> splitNames( - {required String matchingKey, required String? bucketingKey}) async { + Future> splitNames() async { List splitNames = await _methodChannel.invokeListMethod('splitNames') ?? []; @@ -233,8 +229,7 @@ class MethodChannelPlatform extends SplitioPlatform { } @override - Future> splits( - {required String matchingKey, required String? bucketingKey}) async { + Future> splits() async { List callResult = (await _methodChannel .invokeListMethod>('splits') ?? []); @@ -269,7 +264,7 @@ class MethodChannelPlatform extends SplitioPlatform { } try { - return await _methodChannel.invokeMethod("track", parameters) + return await _methodChannel.invokeMethod('track', parameters) as bool; } on Exception catch (_) { return false; diff --git a/splitio_platform_interface/lib/split_sync_config.dart b/splitio_platform_interface/lib/split_sync_config.dart index 8e59b20..6eec23d 100644 --- a/splitio_platform_interface/lib/split_sync_config.dart +++ b/splitio_platform_interface/lib/split_sync_config.dart @@ -1,12 +1,19 @@ class SyncConfig { - late final Set _names; - late final Set _prefixes; + late Set _names; + late Set _prefixes; Set get names => _names; Set get prefixes => _prefixes; - SyncConfig({Set names = const {}, Set prefixes = const {}}) { + SyncConfig( + {List names = const [], List prefixes = const []}) { + _names = names.toSet(); + _prefixes = prefixes.toSet(); + } + + SyncConfig.fromSet( + {Set names = const {}, Set prefixes = const {}}) { _names = names; _prefixes = prefixes; } diff --git a/splitio_platform_interface/lib/splitio_platform_interface.dart b/splitio_platform_interface/lib/splitio_platform_interface.dart index e368763..71c97ea 100644 --- a/splitio_platform_interface/lib/splitio_platform_interface.dart +++ b/splitio_platform_interface/lib/splitio_platform_interface.dart @@ -22,20 +22,16 @@ abstract class _FactoryPlatform { throw UnimplementedError(); } - Future split( - {required String matchingKey, - required String? bucketingKey, + Future split({ required String splitName}) { throw UnimplementedError(); } - Future> splitNames( - {required String matchingKey, required String? bucketingKey}) { + Future> splitNames() { throw UnimplementedError(); } - Future> splits( - {required String matchingKey, required String? bucketingKey}) { + Future> splits() { throw UnimplementedError(); } 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..8f00cc9 100644 --- a/splitio_platform_interface/test/impressions_method_call_handler_test.dart +++ b/splitio_platform_interface/test/impressions_method_call_handler_test.dart @@ -1,6 +1,6 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:splitio/impressions/split_impression.dart'; +import 'package:splitio_platform_interface/split_impression.dart'; import '../lib/impressions/impressions_method_call_handler.dart'; diff --git a/splitio_platform_interface/test/method_channel_platform_interface_test.dart b/splitio_platform_interface/test/method_channel_platform_interface_test.dart new file mode 100644 index 0000000..ff29df2 --- /dev/null +++ b/splitio_platform_interface/test/method_channel_platform_interface_test.dart @@ -0,0 +1,438 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:splitio_platform_interface/method_channel_platform.dart'; +import 'package:splitio_platform_interface/split_configuration.dart'; + +void main() { + const MethodChannel _channel = MethodChannel('splitio'); + + String methodName = ''; + dynamic methodArguments; + + TestWidgetsFlutterBinding.ensureInitialized(); + + MethodChannelPlatform _platform = MethodChannelPlatform(); + + setUp(() { + _channel.setMockMethodCallHandler((MethodCall methodCall) async { + methodName = methodCall.method; + methodArguments = methodCall.arguments; + + switch (methodCall.method) { + case 'getTreatment': + return ''; + case 'getTreatments': + return {'split1': 'on', 'split2': 'off'}; + case 'getTreatmentsWithConfig': + return { + 'split1': {'treatment': 'on', 'config': null}, + 'split2': {'treatment': 'off', 'config': null} + }; + case 'track': + return true; + case 'getAttribute': + return true; + case 'getAllAttributes': + return { + 'attr_1': true, + 'attr_2': ['list-element'], + 'attr_3': 28.20 + }; + case 'setAttribute': + case 'setAttributes': + case 'removeAttribute': + case 'clearAttributes': + return true; + } + }); + }); + + group('evaluation', () { + test('getTreatment without attributes', () async { + _platform.getTreatment( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitName: 'split'); + + expect(methodName, 'getTreatment'); + expect(methodArguments, { + 'splitName': 'split', + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {} + }); + }); + + test('getTreatment with attributes', () async { + _platform.getTreatment( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitName: 'split', + attributes: {'attr1': true}); + + expect(methodName, 'getTreatment'); + expect(methodArguments, { + 'splitName': 'split', + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {'attr1': true} + }); + }); + + test('getTreatments without attributes', () async { + _platform.getTreatments( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitNames: ['split1', 'split2']); + + expect(methodName, 'getTreatments'); + expect(methodArguments, { + 'splitName': ['split1', 'split2'], + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {} + }); + }); + + test('getTreatments with attributes', () async { + _platform.getTreatments( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitNames: ['split1', 'split2'], + attributes: {'attr1': true}); + + expect(methodName, 'getTreatments'); + expect(methodArguments, { + 'splitName': ['split1', 'split2'], + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {'attr1': true} + }); + }); + + test('getTreatmentWithConfig with attributes', () async { + _platform.getTreatmentWithConfig( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitName: 'split1', + attributes: {'attr1': true}); + + expect(methodName, 'getTreatmentWithConfig'); + expect(methodArguments, { + 'splitName': 'split1', + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {'attr1': true} + }); + }); + + test('getTreatmentWithConfig without attributes', () async { + _platform.getTreatmentWithConfig( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitName: 'split1'); + + expect(methodName, 'getTreatmentWithConfig'); + expect(methodArguments, { + 'splitName': 'split1', + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {} + }); + }); + + test('getTreatmentsWithConfig without attributes', () async { + _platform.getTreatmentsWithConfig( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitNames: ['split1', 'split2']); + + expect(methodName, 'getTreatmentsWithConfig'); + expect(methodArguments, { + 'splitName': ['split1', 'split2'], + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {} + }); + }); + + test('getTreatmentsWithConfig with attributes', () async { + _platform.getTreatmentsWithConfig( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitNames: ['split1', 'split2'], + attributes: {'attr1': true}); + + expect(methodName, 'getTreatmentsWithConfig'); + expect(methodArguments, { + 'splitName': ['split1', 'split2'], + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {'attr1': true} + }); + }); + }); + + group('track', () { + test('track with traffic type & value', () async { + _platform.track( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + eventType: 'my_event', + trafficType: 'my_traffic_type', + value: 25.10); + expect(methodName, 'track'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'eventType': 'my_event', + 'trafficType': 'my_traffic_type', + 'value': 25.10 + }); + }); + + test('track with value', () async { + _platform.track( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + eventType: 'my_event', + value: 25.10); + expect(methodName, 'track'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'eventType': 'my_event', + 'value': 25.10 + }); + }); + + test('track with traffic type', () async { + _platform.track( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + eventType: 'my_event', + trafficType: 'my_traffic_type'); + expect(methodName, 'track'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'eventType': 'my_event', + 'trafficType': 'my_traffic_type', + }); + }); + }); + + group('attributes', () { + test('get single attribute', () async { + _platform.getAttribute( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + attributeName: 'attribute-name'); + expect(methodName, 'getAttribute'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributeName': 'attribute-name', + }); + }); + + test('get all attributes', () async { + _platform.getAllAttributes( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + expect(methodName, 'getAllAttributes'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + }); + }); + + test('set attribute', () async { + _platform.setAttribute( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + attributeName: 'my_attr', + value: 'attr_value'); + expect(methodName, 'setAttribute'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributeName': 'my_attr', + 'value': 'attr_value', + }); + }); + + test('set multiple attributes', () async { + _platform.setAttributes( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + attributes: { + 'bool_attr': true, + 'number_attr': 25.56, + 'string_attr': 'attr-value', + 'list_attr': ['one', 'two'], + }); + expect(methodName, 'setAttributes'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': { + 'bool_attr': true, + 'number_attr': 25.56, + 'string_attr': 'attr-value', + 'list_attr': ['one', 'two'], + } + }); + }); + + test('remove attribute', () async { + _platform.removeAttribute( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + attributeName: 'attr-name'); + expect(methodName, 'removeAttribute'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributeName': 'attr-name', + }); + }); + + test('clear attributes', () async { + _platform.clearAttributes( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + expect(methodName, 'clearAttributes'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + }); + }); + + test('flush', () async { + _platform.flush( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + expect(methodName, 'flush'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + }); + }); + + test('destroy', () async { + _platform.destroy( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + expect(methodName, 'destroy'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + }); + }); + }); + + group('initialization', () { + test('init with matching key only', () { + _platform.init( + apiKey: 'api-key', matchingKey: 'matching-key', bucketingKey: null); + + expect(methodName, 'init'); + expect(methodArguments, { + 'apiKey': 'api-key', + 'matchingKey': 'matching-key', + 'sdkConfiguration': {} + }); + }); + + test('init with bucketing key', () { + _platform.init( + apiKey: 'api-key', + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key'); + expect(methodName, 'init'); + expect(methodArguments, { + 'apiKey': 'api-key', + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'sdkConfiguration': {} + }); + }); + + test('init with config', () { + _platform.init( + apiKey: 'api-key', + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + sdkConfiguration: + SplitConfiguration(enableDebug: true, streamingEnabled: false)); + expect(methodName, 'init'); + expect(methodArguments, { + 'apiKey': 'api-key', + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'sdkConfiguration': {'enableDebug': true, 'streamingEnabled': false}, + }); + }); + }); + + group('client', () { + test('get client with no keys', () { + _platform.getClient(matchingKey: 'matching-key', bucketingKey: null); + + expect(methodName, 'getClient'); + expect(methodArguments, {'matchingKey': 'matching-key'}); + }); + + test('get client with new matching key', () { + _platform.getClient(matchingKey: 'new-matching-key', bucketingKey: null); + + expect(methodName, 'getClient'); + expect(methodArguments, {'matchingKey': 'new-matching-key'}); + }); + + test('get client with new matching key and bucketing key', () { + _platform.getClient( + matchingKey: 'new-matching-key', bucketingKey: 'bucketing-key'); + + expect(methodName, 'getClient'); + expect(methodArguments, + {'matchingKey': 'new-matching-key', 'bucketingKey': 'bucketing-key'}); + }); + }); + + group('manager', () { + test('get split names', () { + _platform.splitNames(); + + expect(methodName, 'splitNames'); + }); + + test('get splits', () { + _platform.splits(); + + expect(methodName, 'splits'); + }); + + test('get split', () { + _platform.split(splitName: 'my_split'); + + expect(methodName, 'split'); + expect(methodArguments, {'splitName': 'my_split'}); + }); + }); + + group('events', () { + test('onReady is returned from events listener', () { + // TODO + }); + + test('onReadyFromCache is returned from events listener', () { + // TODO + }); + + test('onTimeout is returned from events listener', () { + // TODO + }); + + test('onUpdated is returned from events listener', () { + // TODO + }); + }); +} diff --git a/splitio_platform_interface/test/split_event_listener_test.dart b/splitio_platform_interface/test/split_event_listener_test.dart index f06c463..2388fe3 100644 --- a/splitio_platform_interface/test/split_event_listener_test.dart +++ b/splitio_platform_interface/test/split_event_listener_test.dart @@ -1,22 +1,14 @@ 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/events/split_method_call_handler.dart'; -import 'package:splitio/split_result.dart'; - -import '../lib/split_client.dart'; +import 'package:splitio_platform_interface/events/split_method_call_handler.dart'; void main() { const MethodChannel _channel = MethodChannel('splitio'); TestWidgetsFlutterBinding.ensureInitialized(); - SplitClientMock splitClientMock = SplitClientMock(); SplitEventMethodCallHandler splitEventMethodCallHandler = - SplitEventMethodCallHandler('key', 'bucketing', splitClientMock); - late final MethodChannelManager _methodChannelWrapper = - MethodChannelManager(_channel); + SplitEventMethodCallHandler('key', 'bucketing'); void _simulateMethodInvocation(String methodName, {String key = 'key', String bucketingKey = 'bucketing'}) { @@ -33,18 +25,16 @@ void main() { group('client events', () { test('test client ready', () async { - SplitEventsListener eventListener = DefaultEventsListener( - _methodChannelWrapper, splitEventMethodCallHandler); - Future future = eventListener.onReady().then((value) => true); + Future future = + splitEventMethodCallHandler.onReady().then((value) => true); _simulateMethodInvocation('clientReady'); expect(future, completion(equals(true))); }); test('test multiple client ready', () async { - SplitEventsListener eventListener = DefaultEventsListener( - _methodChannelWrapper, splitEventMethodCallHandler); - Future future = eventListener.onReady().then((value) => true); + Future future = + splitEventMethodCallHandler.onReady().then((value) => true); _simulateMethodInvocation('clientReady'); expect(future, completion(equals(true))); @@ -54,19 +44,16 @@ void main() { }); test('test client ready from cache', () async { - SplitEventsListener eventListener = DefaultEventsListener( - _methodChannelWrapper, splitEventMethodCallHandler); Future future = - eventListener.onReadyFromCache().then((value) => true); + splitEventMethodCallHandler.onReadyFromCache().then((value) => true); _simulateMethodInvocation('clientReadyFromCache'); expect(future, completion(equals(true))); }); test('test multiple client ready from cache', () async { - SplitEventsListener eventListener = DefaultEventsListener( - _methodChannelWrapper, splitEventMethodCallHandler); - Future future = eventListener.onReady().then((value) => true); + Future future = + splitEventMethodCallHandler.onReady().then((value) => true); _simulateMethodInvocation('clientReadyFromCache'); expect(future, completion(equals(true))); @@ -76,18 +63,16 @@ void main() { }); test('test client timeout', () async { - SplitEventsListener eventListener = DefaultEventsListener( - _methodChannelWrapper, splitEventMethodCallHandler); - Future future = eventListener.onTimeout().then((value) => true); + Future future = + splitEventMethodCallHandler.onTimeout().then((value) => true); _simulateMethodInvocation('clientTimeout'); expect(future, completion(equals(true))); }); test('test multiple client timeout', () async { - SplitEventsListener eventListener = DefaultEventsListener( - _methodChannelWrapper, splitEventMethodCallHandler); - Future future = eventListener.onReady().then((value) => true); + Future future = + splitEventMethodCallHandler.onReady().then((value) => true); _simulateMethodInvocation('clientTimeout'); expect(future, completion(equals(true))); @@ -97,10 +82,8 @@ void main() { }); test('test client updated', () async { - SplitEventsListener eventListener = DefaultEventsListener( - _methodChannelWrapper, splitEventMethodCallHandler); var count = 0; - eventListener + splitEventMethodCallHandler .onUpdated() .map((event) => ++count) .take(3) @@ -112,128 +95,3 @@ void main() { }); }); } - -class SplitClientMock extends SplitClient { - Map calledMethods = {}; - - @override - Future clearAttributes() { - calledMethods.update('clearAttributes', (value) => value + 1, - ifAbsent: () => 1); - return Future.value(true); - } - - @override - Future destroy() { - calledMethods.update('destroy', (value) => value + 1, ifAbsent: () => 1); - return Future.value(null); - } - - @override - Future flush() { - calledMethods.update('flush', (value) => value + 1, ifAbsent: () => 1); - return Future.value(null); - } - - @override - Future getAttribute(String attributeName) { - calledMethods.update('getAttribute', (value) => value + 1, - ifAbsent: () => 1); - return Future.value('value'); - } - - @override - Future> getAttributes() { - calledMethods.update('getAttributes', (value) => value + 1, - ifAbsent: () => 1); - return Future.value({'attr1': 'value1'}); - } - - @override - Future getTreatment(String splitName, - [Map attributes = const {}]) { - calledMethods.update('getTreatment', (value) => value + 1, - ifAbsent: () => 1); - return Future.value('treatment'); - } - - @override - Future getTreatmentWithConfig(String splitName, - [Map attributes = const {}]) { - calledMethods.update('getTreatmentWithConfig', (value) => value + 1, - ifAbsent: () => 1); - return Future.value(const SplitResult('treatment', null)); - } - - @override - Future> getTreatments(List splitNames, - [Map attributes = const {}]) { - calledMethods.update('getTreatments', (value) => value + 1, - ifAbsent: () => 1); - return Future.value({'split1': 'treatment'}); - } - - @override - Future> getTreatmentsWithConfig( - List splitNames, - [Map attributes = const {}]) { - calledMethods.update('getTreatmentsWithConfig', (value) => value + 1, - ifAbsent: () => 1); - return Future.value({'split1': const SplitResult('treatment', null)}); - } - - @override - Future whenReady() { - calledMethods.update('onReady', (value) => value + 1, ifAbsent: () => 1); - return Future.value(this); - } - - @override - Future whenReadyFromCache() { - calledMethods.update('onReadyFromCache', (value) => value + 1, - ifAbsent: () => 1); - return Future.value(this); - } - - @override - Future whenTimeout() { - calledMethods.update('onTimeout', (value) => value + 1, ifAbsent: () => 1); - return Future.value(this); - } - - @override - Stream whenUpdated() { - calledMethods.update('onUpdated', (value) => value + 1, ifAbsent: () => 1); - return Stream.value(this); - } - - @override - Future removeAttribute(String attributeName) { - calledMethods.update('removeAttribute', (value) => value + 1, - ifAbsent: () => 1); - return Future.value(true); - } - - @override - Future setAttribute(String attributeName, value) { - calledMethods.update('setAttribute', (value) => value + 1, - ifAbsent: () => 1); - return Future.value(true); - } - - @override - Future setAttributes(Map attributes) { - calledMethods.update('setAttributes', (value) => value + 1, - ifAbsent: () => 1); - return Future.value(true); - } - - @override - Future track(String eventType, - {String? trafficType, - double? value, - Map properties = const {}}) { - calledMethods.update('track', (value) => value + 1, ifAbsent: () => 1); - return Future.value(true); - } -} diff --git a/splitio_platform_interface/test/split_impression_test.dart b/splitio_platform_interface/test/split_impression_test.dart index e792441..b993d2a 100644 --- a/splitio_platform_interface/test/split_impression_test.dart +++ b/splitio_platform_interface/test/split_impression_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:splitio/impressions/split_impression.dart'; +import 'package:splitio_platform_interface/split_impression.dart'; void main() { test('from map creates correct Impression', () { diff --git a/splitio_platform_interface/test/split_view_test.dart b/splitio_platform_interface/test/split_view_test.dart index af0578a..0268b42 100644 --- a/splitio_platform_interface/test/split_view_test.dart +++ b/splitio_platform_interface/test/split_view_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:splitio/split_view.dart'; +import 'package:splitio_platform_interface/split_view.dart'; void main() { test('split view from empty map results in null object', () { diff --git a/splitio_platform_interface/test/splitio_configuration_test.dart b/splitio_platform_interface/test/splitio_configuration_test.dart index c04ed72..d1c44f5 100644 --- a/splitio_platform_interface/test/splitio_configuration_test.dart +++ b/splitio_platform_interface/test/splitio_configuration_test.dart @@ -1,5 +1,6 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:splitio/split_configuration.dart'; +import 'package:splitio_platform_interface/split_configuration.dart'; +import 'package:splitio_platform_interface/split_sync_config.dart'; void main() { test('valuesAreMappedCorrectly', () async { @@ -21,7 +22,9 @@ void main() { eventsEndpoint: 'eventsEndpoint.split.io', authServiceEndpoint: 'authServiceEndpoint.split.io', streamingServiceEndpoint: 'streamingServiceEndpoint.split.io', - telemetryServiceEndpoint: 'telemetryServiceEndpoint.split.io'); + telemetryServiceEndpoint: 'telemetryServiceEndpoint.split.io', + syncConfig: + SyncConfig(names: ['one', 'two', 'three'], prefixes: ['pre1'])); expect(config.configurationMap['eventFlushInterval'], 2000); expect(config.configurationMap['eventsPerPush'], 300); @@ -44,6 +47,10 @@ void main() { expect(config.configurationMap['telemetryServiceEndpoint'], 'telemetryServiceEndpoint.split.io'); expect(config.configurationMap['impressionListener'], true); + expect(config.configurationMap['syncConfig']['syncConfigNames'], + ['one', 'two', 'three']); + expect( + config.configurationMap['syncConfig']['syncConfigPrefixes'], ['pre1']); }); test('noSpecialValuesLeavesMapEmpty', () async { From d3e6315738f938845be633ad76f5b3a0bbb3c081 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Mon, 5 Sep 2022 18:05:02 -0300 Subject: [PATCH 19/23] Fix --- .../lib/method_channel_platform.dart | 157 +++++++++--------- 1 file changed, 83 insertions(+), 74 deletions(-) diff --git a/splitio_platform_interface/lib/method_channel_platform.dart b/splitio_platform_interface/lib/method_channel_platform.dart index 5a33fd3..3833636 100644 --- a/splitio_platform_interface/lib/method_channel_platform.dart +++ b/splitio_platform_interface/lib/method_channel_platform.dart @@ -1,4 +1,3 @@ - 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'; @@ -13,12 +12,12 @@ const String _controlTreatment = 'control'; const SplitResult _controlResult = SplitResult(_controlTreatment, null); class MethodChannelPlatform extends SplitioPlatform { - late MethodChannel _methodChannel = const MethodChannel('splitio'); final Map _handlers = {}; - final ImpressionsMethodCallHandler _impressionsMethodCallHandler = ImpressionsMethodCallHandler(); + final ImpressionsMethodCallHandler _impressionsMethodCallHandler = + ImpressionsMethodCallHandler(); MethodChannelPlatform() { _methodChannel.setMethodCallHandler((call) => _handle(call)); @@ -32,10 +31,11 @@ class MethodChannelPlatform extends SplitioPlatform { } @override - Future init({required String apiKey, - required String matchingKey, - required String? bucketingKey, - SplitConfiguration? sdkConfiguration}) { + Future init( + {required String apiKey, + required String matchingKey, + required String? bucketingKey, + SplitConfiguration? sdkConfiguration}) { Map arguments = { 'apiKey': apiKey, 'matchingKey': matchingKey, @@ -52,11 +52,10 @@ class MethodChannelPlatform extends SplitioPlatform { @override Future getClient( {required String matchingKey, required String? bucketingKey}) { - _handlers.addAll( - { - _buildMapKey(matchingKey, bucketingKey): SplitEventMethodCallHandler( - matchingKey, bucketingKey) - }); + _handlers.addAll({ + _buildMapKey(matchingKey, bucketingKey): + SplitEventMethodCallHandler(matchingKey, bucketingKey) + }); return _methodChannel.invokeMethod( 'getClient', _buildParameters(matchingKey, bucketingKey)); @@ -90,42 +89,46 @@ class MethodChannelPlatform extends SplitioPlatform { @override Future> getAllAttributes( {required String matchingKey, required String? bucketingKey}) async { - return (await _methodChannel.invokeMapMethod( - 'getAllAttributes', _buildParameters(matchingKey, bucketingKey))) - ?.map((key, value) => MapEntry(key, value)) ?? + 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}) { + Future getAttribute( + {required String matchingKey, + required String? bucketingKey, + required String attributeName}) { return _methodChannel.invokeMethod( - 'getAttribute', _buildParameters( - matchingKey, bucketingKey, {'attributeName': attributeName})); + 'getAttribute', + _buildParameters( + matchingKey, bucketingKey, {'attributeName': attributeName})); } @override - Future getTreatment({required String matchingKey, - required String? bucketingKey, - required String splitName, - Map attributes = const {}}) async { + 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})) ?? + '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 { + 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}))) + 'getTreatmentWithConfig', + _buildParameters(matchingKey, bucketingKey, + {'splitName': splitName, 'attributes': attributes}))) ?.entries .first .value; @@ -137,51 +140,56 @@ class MethodChannelPlatform extends SplitioPlatform { } @override - Future> getTreatments({required String matchingKey, - required String? bucketingKey, - required List splitNames, - Map attributes = const {}}) async { + 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)) ?? + ?.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 { + 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']))) ?? + 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 { + Future removeAttribute( + {required String matchingKey, + required String? bucketingKey, + required String attributeName}) async { return await _methodChannel.invokeMethod( - 'removeAttribute', _buildParameters( - matchingKey, bucketingKey, {'attributeName': attributeName})); + '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', + 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})); @@ -193,12 +201,14 @@ class MethodChannelPlatform extends SplitioPlatform { } @override - Future setAttributes({required String matchingKey, - required String? bucketingKey, - required Map attributes}) async { + Future setAttributes( + {required String matchingKey, + required String? bucketingKey, + required Map attributes}) async { var result = await _methodChannel.invokeMethod( - 'setAttributes', _buildParameters( - matchingKey, bucketingKey, {'attributes': attributes})); + 'setAttributes', + _buildParameters( + matchingKey, bucketingKey, {'attributes': attributes})); if (result is bool) { return result; @@ -208,10 +218,9 @@ class MethodChannelPlatform extends SplitioPlatform { } @override - Future split({ - required String splitName}) async { + Future split({required String splitName}) async { Map? mapResult = - await _methodChannel.invokeMapMethod('split', {'splitName': splitName}); + await _methodChannel.invokeMapMethod('split', {'splitName': splitName}); if (mapResult == null) { return null; @@ -231,7 +240,7 @@ class MethodChannelPlatform extends SplitioPlatform { @override Future> splits() async { List callResult = (await _methodChannel - .invokeListMethod>('splits') ?? + .invokeListMethod>('splits') ?? []); List splits = []; @@ -246,14 +255,15 @@ class MethodChannelPlatform extends SplitioPlatform { } @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}); + 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; @@ -264,15 +274,14 @@ class MethodChannelPlatform extends SplitioPlatform { } try { - return await _methodChannel.invokeMethod('track', parameters) - as bool; + return await _methodChannel.invokeMethod('track', parameters) as bool; } on Exception catch (_) { return false; } } - Map _buildParameters(String matchingKey, - String? bucketingKey, + Map _buildParameters( + String matchingKey, String? bucketingKey, [Map parameters = const {}]) { Map result = {}; result.addAll(parameters); From 0c9e120862d1763857dd13a7f4f8dd52bcfbd347 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Mon, 5 Sep 2022 18:37:03 -0300 Subject: [PATCH 20/23] Renamed test class --- .../test/impressions_method_call_handler_test.dart | 3 +-- ...ner_test.dart => split_event_method_call_handler_test.dart} | 0 2 files changed, 1 insertion(+), 2 deletions(-) rename splitio_platform_interface/test/{split_event_listener_test.dart => split_event_method_call_handler_test.dart} (100%) 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 8f00cc9..f6836bd 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_platform_interface/impressions/impressions_method_call_handler.dart'; import 'package:splitio_platform_interface/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_platform_interface/test/split_event_listener_test.dart b/splitio_platform_interface/test/split_event_method_call_handler_test.dart similarity index 100% rename from splitio_platform_interface/test/split_event_listener_test.dart rename to splitio_platform_interface/test/split_event_method_call_handler_test.dart From a96dbe18320ac6b180c4e8bd019b10e992096030 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Tue, 6 Sep 2022 11:31:07 -0300 Subject: [PATCH 21/23] Add missing tests --- splitio_platform_interface/coverage/lcov.info | 315 ++++++++++++++++++ .../lib/method_channel_platform.dart | 9 +- ...dart => method_channel_platform_test.dart} | 97 +++++- 3 files changed, 410 insertions(+), 11 deletions(-) create mode 100644 splitio_platform_interface/coverage/lcov.info rename splitio_platform_interface/test/{method_channel_platform_interface_test.dart => method_channel_platform_test.dart} (80%) diff --git a/splitio_platform_interface/coverage/lcov.info b/splitio_platform_interface/coverage/lcov.info new file mode 100644 index 0000000..816bcfd --- /dev/null +++ b/splitio_platform_interface/coverage/lcov.info @@ -0,0 +1,315 @@ +SF:lib/split_impression.dart +DA:11,3 +DA:14,3 +DA:15,3 +DA:16,3 +DA:17,3 +DA:18,3 +DA:19,3 +DA:20,3 +DA:21,3 +DA:22,3 +DA:23,6 +DA:26,0 +DA:28,0 +LF:13 +LH:11 +end_of_record +SF:lib/split_configuration.dart +DA:35,2 +DA:57,2 +DA:61,2 +DA:65,2 +DA:69,2 +DA:73,2 +DA:77,2 +DA:81,2 +DA:85,2 +DA:89,2 +DA:93,4 +DA:97,4 +DA:101,2 +DA:106,2 +DA:110,2 +DA:114,2 +DA:118,2 +DA:122,2 +DA:126,2 +DA:130,3 +DA:131,2 +DA:132,2 +LF:22 +LH:22 +end_of_record +SF:lib/split_sync_config.dart +DA:5,2 +DA:7,2 +DA:9,1 +DA:11,2 +DA:12,2 +DA:15,0 +DA:17,0 +DA:18,0 +LF:8 +LH:5 +end_of_record +SF:lib/impressions/impressions_method_call_handler.dart +DA:10,2 +DA:11,2 +DA:12,4 +DA:13,4 +DA:14,4 +DA:15,6 +DA:16,2 +DA:21,2 +DA:23,4 +LF:9 +LH:9 +end_of_record +SF:lib/split_view.dart +DA:11,1 +DA:14,1 +DA:15,1 +DA:19,1 +DA:20,4 +DA:21,6 +DA:24,1 +DA:25,1 +DA:26,1 +DA:27,1 +DA:28,4 +DA:29,1 +DA:33,0 +DA:36,0 +DA:37,0 +DA:38,0 +DA:39,0 +DA:40,0 +DA:41,0 +DA:42,0 +LF:20 +LH:12 +end_of_record +SF:lib/events/split_method_call_handler.dart +DA:27,2 +DA:30,2 +DA:31,4 +DA:32,6 +DA:33,6 +DA:34,4 +DA:35,2 +DA:36,2 +DA:39,4 +DA:40,4 +DA:43,2 +DA:44,4 +DA:45,4 +DA:46,4 +DA:47,4 +DA:51,2 +DA:52,2 +DA:55,2 +DA:56,2 +DA:59,2 +DA:60,4 +DA:63,2 +DA:64,2 +DA:67,0 +DA:68,0 +DA:71,2 +DA:72,4 +DA:73,6 +DA:74,1 +DA:77,6 +LF:30 +LH:28 +end_of_record +SF:lib/method_channel_platform.dart +DA:16,1 +DA:23,0 +DA:24,0 +DA:27,1 +DA:30,1 +DA:31,4 +DA:32,3 +DA:33,3 +DA:37,1 +DA:43,1 +DA:46,2 +DA:50,2 +DA:53,2 +DA:56,1 +DA:59,3 +DA:60,1 +DA:61,1 +DA:64,2 +DA:65,1 +DA:69,1 +DA:71,3 +DA:72,1 +DA:76,1 +DA:78,1 +DA:79,2 +DA:80,2 +DA:82,3 +DA:83,1 +DA:87,1 +DA:89,3 +DA:90,1 +DA:94,1 +DA:96,3 +DA:97,1 +DA:98,3 +DA:99,0 +DA:102,1 +DA:107,2 +DA:109,1 +DA:110,1 +DA:114,1 +DA:119,3 +DA:121,1 +DA:122,1 +DA:127,1 +DA:132,3 +DA:134,1 +DA:135,1 +DA:136,0 +DA:137,0 +DA:138,0 +DA:143,0 +DA:147,1 +DA:152,3 +DA:154,1 +DA:155,1 +DA:158,3 +DA:159,0 +DA:163,1 +DA:168,3 +DA:170,1 +DA:171,1 +DA:173,2 +DA:174,4 +DA:175,0 +DA:179,1 +DA:183,3 +DA:185,1 +DA:186,1 +DA:190,1 +DA:195,3 +DA:197,1 +DA:198,1 +DA:200,1 +DA:208,1 +DA:212,3 +DA:214,1 +DA:215,1 +DA:217,1 +DA:225,1 +DA:227,4 +DA:233,0 +DA:237,1 +DA:239,4 +DA:245,1 +DA:246,2 +DA:247,1 +DA:248,1 +DA:250,1 +DA:251,1 +DA:252,0 +DA:254,0 +DA:258,1 +DA:262,1 +DA:270,2 +DA:273,1 +DA:277,1 +DA:281,3 +DA:282,0 +DA:287,1 +DA:290,1 +DA:291,1 +DA:292,2 +DA:297,1 +DA:298,1 +DA:301,2 +DA:307,1 +DA:310,4 +DA:313,1 +DA:316,3 +DA:317,1 +DA:320,1 +DA:323,4 +DA:326,1 +DA:329,4 +DA:332,1 +DA:334,2 +DA:337,1 +DA:338,1 +LF:119 +LH:106 +end_of_record +SF:lib/split_result.dart +DA:10,3 +DA:12,0 +DA:14,0 +DA:15,0 +DA:16,0 +DA:17,0 +LF:6 +LH:1 +end_of_record +SF:lib/splitio_platform_interface.dart +DA:11,0 +DA:13,0 +DA:16,0 +DA:22,0 +DA:25,0 +DA:27,0 +DA:30,0 +DA:31,0 +DA:34,0 +DA:35,0 +DA:38,0 +DA:39,0 +DA:44,0 +DA:49,0 +DA:52,0 +DA:57,0 +DA:60,0 +DA:65,0 +DA:68,0 +DA:73,0 +DA:76,0 +DA:78,0 +DA:81,0 +DA:86,0 +DA:89,0 +DA:93,0 +DA:96,0 +DA:100,0 +DA:103,0 +DA:107,0 +DA:110,0 +DA:112,0 +DA:115,0 +DA:117,0 +DA:120,0 +DA:122,0 +DA:125,0 +DA:132,0 +DA:135,0 +DA:137,0 +DA:140,0 +DA:142,0 +DA:145,0 +DA:147,0 +DA:150,0 +DA:152,0 +DA:165,3 +DA:167,0 +DA:169,3 +DA:171,0 +DA:175,0 +DA:176,0 +LF:52 +LH:2 +end_of_record diff --git a/splitio_platform_interface/lib/method_channel_platform.dart b/splitio_platform_interface/lib/method_channel_platform.dart index 3833636..cbd45d1 100644 --- a/splitio_platform_interface/lib/method_channel_platform.dart +++ b/splitio_platform_interface/lib/method_channel_platform.dart @@ -1,3 +1,4 @@ +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'; @@ -20,10 +21,14 @@ class MethodChannelPlatform extends SplitioPlatform { ImpressionsMethodCallHandler(); MethodChannelPlatform() { - _methodChannel.setMethodCallHandler((call) => _handle(call)); + _methodChannel.setMethodCallHandler((call) => handle(call)); } - Future _handle(MethodCall call) async { + @visibleForTesting + MethodChannelPlatform.withoutHandler(); + + @visibleForTesting + Future handle(MethodCall call) async { _impressionsMethodCallHandler.handle(call.method, call.arguments); for (MethodCallHandler handler in _handlers.values) { handler.handle(call.method, call.arguments); diff --git a/splitio_platform_interface/test/method_channel_platform_interface_test.dart b/splitio_platform_interface/test/method_channel_platform_test.dart similarity index 80% rename from splitio_platform_interface/test/method_channel_platform_interface_test.dart rename to splitio_platform_interface/test/method_channel_platform_test.dart index ff29df2..ee51457 100644 --- a/splitio_platform_interface/test/method_channel_platform_interface_test.dart +++ b/splitio_platform_interface/test/method_channel_platform_test.dart @@ -11,13 +11,25 @@ void main() { TestWidgetsFlutterBinding.ensureInitialized(); - MethodChannelPlatform _platform = MethodChannelPlatform(); + MethodChannelPlatform _platform = MethodChannelPlatform.withoutHandler(); + + void _simulateMethodInvocation(String methodName, + {String key = 'key', + String? bucketingKey, + Map? arguments}) { + if (arguments == null) { + arguments = {}; + } + arguments.addAll({'matchingKey': key, 'bucketingKey': bucketingKey}); + _channel.invokeMethod(methodName, arguments); + } setUp(() { _channel.setMockMethodCallHandler((MethodCall methodCall) async { methodName = methodCall.method; methodArguments = methodCall.arguments; + _platform.handle(methodCall); switch (methodCall.method) { case 'getTreatment': return ''; @@ -419,20 +431,87 @@ void main() { }); group('events', () { - test('onReady is returned from events listener', () { - // TODO + test('onReady', () { + _platform.getClient( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + + Future? onReady = _platform + .onReady(matchingKey: 'matching-key', bucketingKey: 'bucketing-key') + ?.then((value) => true); + + _simulateMethodInvocation('clientReady', + key: 'matching-key', bucketingKey: 'bucketing-key'); + + expect(onReady, completion(equals(true))); }); - test('onReadyFromCache is returned from events listener', () { - // TODO + test('onReadyFromCache', () { + _platform.getClient( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + + Future? onReadyFromCache = _platform + .onReadyFromCache( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key') + ?.then((value) => true); + + _simulateMethodInvocation('clientReadyFromCache', + key: 'matching-key', bucketingKey: 'bucketing-key'); + + expect(onReadyFromCache, completion(equals(true))); }); - test('onTimeout is returned from events listener', () { - // TODO + test('onTimeout', () { + _platform.getClient( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + + Future? onTimeout = _platform + .onTimeout(matchingKey: 'matching-key', bucketingKey: 'bucketing-key') + ?.then((value) => true); + + _simulateMethodInvocation('clientTimeout', + key: 'matching-key', bucketingKey: 'bucketing-key'); + + expect(onTimeout, completion(equals(true))); }); - test('onUpdated is returned from events listener', () { - // TODO + test('onUpdated', () { + _platform.getClient( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + + Future? onUpdated = _platform + .onUpdated(matchingKey: 'matching-key', bucketingKey: 'bucketing-key') + ?.first + .then((value) => true); + + _simulateMethodInvocation('clientUpdated', + key: 'matching-key', bucketingKey: 'bucketing-key'); + + expect(onUpdated, completion(equals(true))); + }); + }); + + test('impressions', () { + _platform.impressionsStream().listen( + expectAsync1((impression) { + expect(impression.key, 'key'); + expect(impression.bucketingKey, null); + expect(impression.split, 'split'); + expect(impression.treatment, 'treatment'); + expect(impression.time, 3000); + expect(impression.appliedRule, 'appliedRule'); + expect(impression.changeNumber, 200); + expect(impression.attributes, {}); + }), + ); + _simulateMethodInvocation('impressionLog', key: 'matching-key', arguments: { + 'key': 'key', + 'bucketingKey': 'bucketingKey', + 'split': 'split', + 'treatment': 'treatment', + 'time': 3000, + 'appliedRule': 'appliedRule', + 'changeNumber': 200, + 'attributes': {} }); }); } From f285bce0c37184e1e633af2f02e6581ea3b2ae4f Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Tue, 6 Sep 2022 11:58:24 -0300 Subject: [PATCH 22/23] Add missing client tests --- splitio/test/splitio_client_test.dart | 45 +++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/splitio/test/splitio_client_test.dart b/splitio/test/splitio_client_test.dart index 1e2e144..25e638f 100644 --- a/splitio/test/splitio_client_test.dart +++ b/splitio/test/splitio_client_test.dart @@ -270,12 +270,51 @@ void main() { }); }); - test('destroy', () async { - // TODO + test('destroy', () { + _platform.destroy( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + + expect(_platform.methodName, 'destroy'); + expect(_platform.methodArguments, + {'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key'}); }); }); group('events', () { - // TODO + test('onReady', () { + _platform.onReady( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + + expect(_platform.methodName, 'onReady'); + expect(_platform.methodArguments, + {'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key'}); + }); + + test('onReadyFromCache', () { + _platform.onReadyFromCache( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + + expect(_platform.methodName, 'onReadyFromCache'); + expect(_platform.methodArguments, + {'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key'}); + }); + + test('onTimeout', () { + _platform.onTimeout( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + + expect(_platform.methodName, 'onTimeout'); + expect(_platform.methodArguments, + {'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key'}); + }); + + test('onUpdated', () { + _platform.onUpdated( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + + expect(_platform.methodName, 'onUpdated'); + expect(_platform.methodArguments, + {'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key'}); + }); }); } From 456d4348e1579d63e7db8ccf738d804f800509be Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Tue, 6 Sep 2022 18:05:19 -0300 Subject: [PATCH 23/23] Remove coverage file --- splitio_platform_interface/coverage/lcov.info | 315 ------------------ 1 file changed, 315 deletions(-) delete mode 100644 splitio_platform_interface/coverage/lcov.info diff --git a/splitio_platform_interface/coverage/lcov.info b/splitio_platform_interface/coverage/lcov.info deleted file mode 100644 index 816bcfd..0000000 --- a/splitio_platform_interface/coverage/lcov.info +++ /dev/null @@ -1,315 +0,0 @@ -SF:lib/split_impression.dart -DA:11,3 -DA:14,3 -DA:15,3 -DA:16,3 -DA:17,3 -DA:18,3 -DA:19,3 -DA:20,3 -DA:21,3 -DA:22,3 -DA:23,6 -DA:26,0 -DA:28,0 -LF:13 -LH:11 -end_of_record -SF:lib/split_configuration.dart -DA:35,2 -DA:57,2 -DA:61,2 -DA:65,2 -DA:69,2 -DA:73,2 -DA:77,2 -DA:81,2 -DA:85,2 -DA:89,2 -DA:93,4 -DA:97,4 -DA:101,2 -DA:106,2 -DA:110,2 -DA:114,2 -DA:118,2 -DA:122,2 -DA:126,2 -DA:130,3 -DA:131,2 -DA:132,2 -LF:22 -LH:22 -end_of_record -SF:lib/split_sync_config.dart -DA:5,2 -DA:7,2 -DA:9,1 -DA:11,2 -DA:12,2 -DA:15,0 -DA:17,0 -DA:18,0 -LF:8 -LH:5 -end_of_record -SF:lib/impressions/impressions_method_call_handler.dart -DA:10,2 -DA:11,2 -DA:12,4 -DA:13,4 -DA:14,4 -DA:15,6 -DA:16,2 -DA:21,2 -DA:23,4 -LF:9 -LH:9 -end_of_record -SF:lib/split_view.dart -DA:11,1 -DA:14,1 -DA:15,1 -DA:19,1 -DA:20,4 -DA:21,6 -DA:24,1 -DA:25,1 -DA:26,1 -DA:27,1 -DA:28,4 -DA:29,1 -DA:33,0 -DA:36,0 -DA:37,0 -DA:38,0 -DA:39,0 -DA:40,0 -DA:41,0 -DA:42,0 -LF:20 -LH:12 -end_of_record -SF:lib/events/split_method_call_handler.dart -DA:27,2 -DA:30,2 -DA:31,4 -DA:32,6 -DA:33,6 -DA:34,4 -DA:35,2 -DA:36,2 -DA:39,4 -DA:40,4 -DA:43,2 -DA:44,4 -DA:45,4 -DA:46,4 -DA:47,4 -DA:51,2 -DA:52,2 -DA:55,2 -DA:56,2 -DA:59,2 -DA:60,4 -DA:63,2 -DA:64,2 -DA:67,0 -DA:68,0 -DA:71,2 -DA:72,4 -DA:73,6 -DA:74,1 -DA:77,6 -LF:30 -LH:28 -end_of_record -SF:lib/method_channel_platform.dart -DA:16,1 -DA:23,0 -DA:24,0 -DA:27,1 -DA:30,1 -DA:31,4 -DA:32,3 -DA:33,3 -DA:37,1 -DA:43,1 -DA:46,2 -DA:50,2 -DA:53,2 -DA:56,1 -DA:59,3 -DA:60,1 -DA:61,1 -DA:64,2 -DA:65,1 -DA:69,1 -DA:71,3 -DA:72,1 -DA:76,1 -DA:78,1 -DA:79,2 -DA:80,2 -DA:82,3 -DA:83,1 -DA:87,1 -DA:89,3 -DA:90,1 -DA:94,1 -DA:96,3 -DA:97,1 -DA:98,3 -DA:99,0 -DA:102,1 -DA:107,2 -DA:109,1 -DA:110,1 -DA:114,1 -DA:119,3 -DA:121,1 -DA:122,1 -DA:127,1 -DA:132,3 -DA:134,1 -DA:135,1 -DA:136,0 -DA:137,0 -DA:138,0 -DA:143,0 -DA:147,1 -DA:152,3 -DA:154,1 -DA:155,1 -DA:158,3 -DA:159,0 -DA:163,1 -DA:168,3 -DA:170,1 -DA:171,1 -DA:173,2 -DA:174,4 -DA:175,0 -DA:179,1 -DA:183,3 -DA:185,1 -DA:186,1 -DA:190,1 -DA:195,3 -DA:197,1 -DA:198,1 -DA:200,1 -DA:208,1 -DA:212,3 -DA:214,1 -DA:215,1 -DA:217,1 -DA:225,1 -DA:227,4 -DA:233,0 -DA:237,1 -DA:239,4 -DA:245,1 -DA:246,2 -DA:247,1 -DA:248,1 -DA:250,1 -DA:251,1 -DA:252,0 -DA:254,0 -DA:258,1 -DA:262,1 -DA:270,2 -DA:273,1 -DA:277,1 -DA:281,3 -DA:282,0 -DA:287,1 -DA:290,1 -DA:291,1 -DA:292,2 -DA:297,1 -DA:298,1 -DA:301,2 -DA:307,1 -DA:310,4 -DA:313,1 -DA:316,3 -DA:317,1 -DA:320,1 -DA:323,4 -DA:326,1 -DA:329,4 -DA:332,1 -DA:334,2 -DA:337,1 -DA:338,1 -LF:119 -LH:106 -end_of_record -SF:lib/split_result.dart -DA:10,3 -DA:12,0 -DA:14,0 -DA:15,0 -DA:16,0 -DA:17,0 -LF:6 -LH:1 -end_of_record -SF:lib/splitio_platform_interface.dart -DA:11,0 -DA:13,0 -DA:16,0 -DA:22,0 -DA:25,0 -DA:27,0 -DA:30,0 -DA:31,0 -DA:34,0 -DA:35,0 -DA:38,0 -DA:39,0 -DA:44,0 -DA:49,0 -DA:52,0 -DA:57,0 -DA:60,0 -DA:65,0 -DA:68,0 -DA:73,0 -DA:76,0 -DA:78,0 -DA:81,0 -DA:86,0 -DA:89,0 -DA:93,0 -DA:96,0 -DA:100,0 -DA:103,0 -DA:107,0 -DA:110,0 -DA:112,0 -DA:115,0 -DA:117,0 -DA:120,0 -DA:122,0 -DA:125,0 -DA:132,0 -DA:135,0 -DA:137,0 -DA:140,0 -DA:142,0 -DA:145,0 -DA:147,0 -DA:150,0 -DA:152,0 -DA:165,3 -DA:167,0 -DA:169,3 -DA:171,0 -DA:175,0 -DA:176,0 -LF:52 -LH:2 -end_of_record