From 2cedd13a60939e7563c105c0b27b74e79c284d47 Mon Sep 17 00:00:00 2001 From: Ben Butterworth <24711048+ben-xD@users.noreply.github.com> Date: Wed, 7 Jul 2021 16:48:33 +0100 Subject: [PATCH 01/16] - Rename codegen files to be consistent with their output - Add underscore to separate words in file names --- bin/codegen.dart | 12 ++++++------ bin/{codegencontext.dart => codegen_context.dart} | 0 ...stants.dart.dart => platform_constants.dart.dart} | 0 3 files changed, 6 insertions(+), 6 deletions(-) rename bin/{codegencontext.dart => codegen_context.dart} (100%) rename bin/templates/{platformconstants.dart.dart => platform_constants.dart.dart} (100%) diff --git a/bin/codegen.dart b/bin/codegen.dart index 1324934d5..c1d9021f3 100644 --- a/bin/codegen.dart +++ b/bin/codegen.dart @@ -1,10 +1,10 @@ import 'dart:io'; -import 'codegencontext.dart' show context; -import 'templates/platformconstants.dart.dart' as dart_template; -import 'templates/platformconstants.h.dart' as objc_header_template; -import 'templates/platformconstants.java.dart' as java_template; -import 'templates/platformconstants.m.dart' as objc_impl_template; +import 'codegen_context.dart' show context; +import 'templates/PlatformConstants.h.dart' as objc_header_template; +import 'templates/PlatformConstants.java.dart' as java_template; +import 'templates/PlatformConstants.m.dart' as objc_impl_template; +import 'templates/platform_constants.dart.dart' as dart_template; typedef Template = String Function(Map context); @@ -12,7 +12,7 @@ const String projectRoot = '../'; Map toGenerate = { // input template method vs output file path - dart_template.$: '${projectRoot}lib/src/generated/platformconstants.dart', + dart_template.$: '${projectRoot}lib/src/generated/platform_constants.dart', java_template.$: '${projectRoot}android/src/main/java/io/ably/flutter/plugin/generated/PlatformConstants.java', objc_header_template.$: diff --git a/bin/codegencontext.dart b/bin/codegen_context.dart similarity index 100% rename from bin/codegencontext.dart rename to bin/codegen_context.dart diff --git a/bin/templates/platformconstants.dart.dart b/bin/templates/platform_constants.dart.dart similarity index 100% rename from bin/templates/platformconstants.dart.dart rename to bin/templates/platform_constants.dart.dart From 246354789a5a5fcd8e9bb3ac9135ebc28c966385 Mon Sep 17 00:00:00 2001 From: Ben Butterworth <24711048+ben-xD@users.noreply.github.com> Date: Wed, 7 Jul 2021 16:48:42 +0100 Subject: [PATCH 02/16] Remove unused import --- android/src/main/java/io/ably/flutter/plugin/AblyLibrary.java | 1 - 1 file changed, 1 deletion(-) diff --git a/android/src/main/java/io/ably/flutter/plugin/AblyLibrary.java b/android/src/main/java/io/ably/flutter/plugin/AblyLibrary.java index 7729a2ab9..1d7f99509 100644 --- a/android/src/main/java/io/ably/flutter/plugin/AblyLibrary.java +++ b/android/src/main/java/io/ably/flutter/plugin/AblyLibrary.java @@ -7,7 +7,6 @@ import io.ably.lib.types.AblyException; import io.ably.lib.types.AsyncPaginatedResult; import io.ably.lib.types.ClientOptions; -import io.ably.lib.types.PaginatedResult; class AblyLibrary { From a029304062f9bbbdf32c9c5e3db7dc6170351e8c Mon Sep 17 00:00:00 2001 From: Ben Butterworth <24711048+ben-xD@users.noreply.github.com> Date: Wed, 7 Jul 2021 16:49:21 +0100 Subject: [PATCH 03/16] Rename test file to the class it contains (mock_method_call_manager) --- test/{utils.dart => mock_method_call_manager.dart} | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) rename test/{utils.dart => mock_method_call_manager.dart} (93%) diff --git a/test/utils.dart b/test/mock_method_call_manager.dart similarity index 93% rename from test/utils.dart rename to test/mock_method_call_manager.dart index 913384139..daa1ce655 100644 --- a/test/utils.dart +++ b/test/mock_method_call_manager.dart @@ -1,7 +1,5 @@ import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/impl/message.dart'; -import 'package:ably_flutter/src/method_call_handler.dart'; -import 'package:ably_flutter/src/platform.dart' as platform; +import 'package:ably_flutter/src/generated/platform_constants.dart'; import 'package:flutter/services.dart'; typedef MethodCallHandler = Future Function(MethodCall); @@ -11,7 +9,6 @@ class MockMethodCallManager { bool isAuthenticated = false; final channels = {}; final publishedMessages = []; - final methodChannel = platform.methodChannel; MockMethodCallManager() { methodChannel.setMockMethodCallHandler(handler); From a5877dad49530fc3f48a5b810caa35239cdf8a3c Mon Sep 17 00:00:00 2001 From: Ben Butterworth <24711048+ben-xD@users.noreply.github.com> Date: Wed, 7 Jul 2021 17:01:38 +0100 Subject: [PATCH 04/16] Tear down tests without digging into methodChannel directly (law of demeter) --- test/models/client_options.dart | 2 +- test/realtime/channel_test.dart | 8 ++++---- test/rest/channel_test.dart | 7 +++---- test/rest/channels_test.dart | 8 ++++---- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/test/models/client_options.dart b/test/models/client_options.dart index b87cf0f36..592d36241 100644 --- a/test/models/client_options.dart +++ b/test/models/client_options.dart @@ -1,5 +1,5 @@ -import 'package:test/test.dart'; import 'package:ably_flutter/ably_flutter.dart'; +import 'package:test/test.dart'; void main() { /// We are leaving it up to the platform client library SDK to supply defaults diff --git a/test/realtime/channel_test.dart b/test/realtime/channel_test.dart index 4bc20e357..403f0209a 100644 --- a/test/realtime/channel_test.dart +++ b/test/realtime/channel_test.dart @@ -1,12 +1,13 @@ import 'dart:async'; import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/impl/message.dart'; +import 'package:ably_flutter/src/generated/platform_constants.dart'; import 'package:fake_async/fake_async.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pedantic/pedantic.dart'; -import '../utils.dart'; +import '../mock_method_call_manager.dart'; + void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -15,11 +16,10 @@ void main() { setUp(() { manager = MockMethodCallManager(); - manager.methodChannel.setMockMethodCallHandler(manager.handler); }); tearDown(() { - manager.methodChannel.setMockMethodCallHandler(null); + manager.reset(); }); group('realtime#channels#channel', () { diff --git a/test/rest/channel_test.dart b/test/rest/channel_test.dart index eed7d01cd..01b191825 100644 --- a/test/rest/channel_test.dart +++ b/test/rest/channel_test.dart @@ -1,12 +1,12 @@ import 'dart:async'; import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/impl/message.dart'; +import 'package:ably_flutter/src/generated/platform_constants.dart'; import 'package:fake_async/fake_async.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pedantic/pedantic.dart'; -import '../utils.dart'; +import '../mock_method_call_manager.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -15,11 +15,10 @@ void main() { setUp(() { manager = MockMethodCallManager(); - manager.methodChannel.setMockMethodCallHandler(manager.handler); }); tearDown(() { - manager.methodChannel.setMockMethodCallHandler(null); + manager.reset(); }); group('rest#channels#channel', () { diff --git a/test/rest/channels_test.dart b/test/rest/channels_test.dart index 0aa4b527f..ecfead855 100644 --- a/test/rest/channels_test.dart +++ b/test/rest/channels_test.dart @@ -1,13 +1,13 @@ import 'package:ably_flutter/ably_flutter.dart'; import 'package:flutter_test/flutter_test.dart'; -import '../utils.dart'; +import '../mock_method_call_manager.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); late MockMethodCallManager manager; - late RestPlatformChannels channels; + late RestChannels channels; setUp(() { manager = MockMethodCallManager(); @@ -38,8 +38,8 @@ void main() { test('creates/returns channel with list accessor #[]', () { final channel2 = channels.get('channel-2'); - final chanel2WithSyntacticSugar = channels['channel-2']; - expect(chanel2WithSyntacticSugar, channel2); + final channel2WithSyntacticSugar = channels['channel-2']; + expect(channel2WithSyntacticSugar, channel2); final channel3 = channels['channel-3']; expect(channel3.name, 'channel-3'); From af9325ea3286d4caca0a85639f873b70f6c006b5 Mon Sep 17 00:00:00 2001 From: Ben Butterworth <24711048+ben-xD@users.noreply.github.com> Date: Wed, 7 Jul 2021 17:04:26 +0100 Subject: [PATCH 05/16] Move file scoped variables to a class, instead of importing a file `as platform`. --- lib/src/codec.dart | 889 ----------------------------- lib/src/info.dart | 10 - lib/src/method_call_handler.dart | 55 -- lib/src/platform.dart | 61 -- test/ably_flutter_plugin_test.dart | 6 +- test/mock_method_call_manager.dart | 8 +- 6 files changed, 6 insertions(+), 1023 deletions(-) delete mode 100644 lib/src/codec.dart delete mode 100644 lib/src/info.dart delete mode 100644 lib/src/method_call_handler.dart delete mode 100644 lib/src/platform.dart diff --git a/lib/src/codec.dart b/lib/src/codec.dart deleted file mode 100644 index 5a2ce87ca..000000000 --- a/lib/src/codec.dart +++ /dev/null @@ -1,889 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/services.dart'; - -import '../ably_flutter.dart'; -import '../src/generated/platformconstants.dart'; -import '../src/impl/message.dart'; -import '../src/spec/realtime/channels.dart'; -import '../src/spec/rest/channels.dart'; - -/// a [_Encoder] encodes custom type and converts it to a Map which will -/// be passed on to platform side -typedef _Encoder = Map? Function(T value); - -/// a [_Decoder] decodes Map received from platform side and converts to -/// to respective dart types -typedef _Decoder = T Function(Map jsonMap); - -/// A class to manage encoding/decoding by provided encoder/decoder functions. -class _CodecPair { - final _Encoder? _encoder; - final _Decoder? _decoder; - - _CodecPair(this._encoder, this._decoder); - - /// Convert properties from an ably library object instance (dart) to Map. - /// if passed [value] is null, encoder will not be called. - /// This method will throw an [AblyException] if encoder is null. - Map? encode(final Object? value) { - if (_encoder == null) throw AblyException('Codec encoder is null'); - if (value == null) return null; - return _encoder!(value as T); - } - - /// Convert Map entries to an ably library object instance (dart). - /// if passed [jsonMap] is null, decoder will not be called. - /// This method will throw an [AblyException] if decoder is null. - T? decode(Map? jsonMap) { - if (_decoder == null) throw AblyException('Codec decoder is null'); - if (jsonMap == null) return null; - return _decoder!(jsonMap); - } -} - -/// Custom message codec for encoding objects to send to platform -/// or decoding objects received from platform. -class Codec extends StandardMessageCodec { - /// Map of codec type (a value from [CodecTypes]) vs encoder/decoder pair. - /// Encoder/decoder can be null. - /// For example, [ErrorInfo] only needs a decoder but not an encoder. - late Map codecMap; - - /// initializes codec with codec map linking codec type to codec pair - Codec() : super() { - codecMap = { - // Ably flutter plugin protocol message - CodecTypes.ablyMessage: - _CodecPair(_encodeAblyMessage, _decodeAblyMessage), - CodecTypes.ablyEventMessage: - _CodecPair(_encodeAblyEventMessage, null), - - // Other ably objects - CodecTypes.clientOptions: - _CodecPair(_encodeClientOptions, _decodeClientOptions), - CodecTypes.tokenParams: - _CodecPair(_encodeTokenParams, _decodeTokenParams), - CodecTypes.tokenDetails: - _CodecPair(_encodeTokenDetails, _decodeTokenDetails), - CodecTypes.tokenRequest: - _CodecPair(_encodeTokenRequest, null), - CodecTypes.restChannelOptions: - _CodecPair(_encodeRestChannelOptions, null), - CodecTypes.realtimeChannelOptions: _CodecPair( - _encodeRealtimeChannelOptions, - null, - ), - CodecTypes.paginatedResult: - _CodecPair(null, _decodePaginatedResult), - CodecTypes.realtimeHistoryParams: - _CodecPair(_encodeRealtimeHistoryParams, null), - CodecTypes.restHistoryParams: - _CodecPair(_encodeRestHistoryParams, null), - CodecTypes.restPresenceParams: - _CodecPair(_encodeRestPresenceParams, null), - CodecTypes.realtimePresenceParams: _CodecPair( - _encodeRealtimePresenceParams, - null, - ), - - CodecTypes.errorInfo: - _CodecPair(_encodeErrorInfo, _decodeErrorInfo), - CodecTypes.messageData: _CodecPair( - _encodeChannelMessageData, - _decodeChannelMessageData, - ), - CodecTypes.messageExtras: _CodecPair( - _encodeChannelMessageExtras, - _decodeChannelMessageExtras, - ), - CodecTypes.message: - _CodecPair(_encodeChannelMessage, _decodeChannelMessage), - CodecTypes.presenceMessage: - _CodecPair(null, _decodePresenceMessage), - - // Events - Connection - CodecTypes.connectionStateChange: - _CodecPair(null, _decodeConnectionStateChange), - - // Events - Channel - CodecTypes.channelStateChange: - _CodecPair(null, _decodeChannelStateChange), - }; - } - - /// Converts a Map with dynamic keys and values to - /// a Map with String keys and dynamic values. - /// Returns null of [value] is null. - Map? toJsonMap(Map? value) { - if (value == null) return null; - return Map.castFrom(value); - } - - /// Returns a value from [CodecTypes] based of the [Type] of [value] - int? getCodecType(final Object? value) { - if (value is ClientOptions) { - return CodecTypes.clientOptions; - } else if (value is TokenDetails) { - return CodecTypes.tokenDetails; - } else if (value is TokenParams) { - return CodecTypes.tokenParams; - } else if (value is TokenRequest) { - return CodecTypes.tokenRequest; - } else if (value is MessageData) { - return CodecTypes.messageData; - } else if (value is MessageExtras) { - return CodecTypes.messageExtras; - } else if (value is Message) { - return CodecTypes.message; - } else if (value is RealtimeHistoryParams) { - return CodecTypes.realtimeHistoryParams; - } else if (value is RestHistoryParams) { - return CodecTypes.restHistoryParams; - } else if (value is RestPresenceParams) { - return CodecTypes.restPresenceParams; - } else if (value is RealtimePresenceParams) { - return CodecTypes.realtimePresenceParams; - } else if (value is ErrorInfo) { - return CodecTypes.errorInfo; - } else if (value is AblyMessage) { - return CodecTypes.ablyMessage; - } else if (value is AblyEventMessage) { - return CodecTypes.ablyEventMessage; - } - // ignore: avoid_returning_null - return null; - } - - /// Encodes values from [_CodecPair._encoder] available in [_CodecPair] - /// obtained from [codecMap] against codecType obtained from [value]. - /// If decoder is not found, [StandardMessageCodec] is used to read value - @override - void writeValue(final WriteBuffer buffer, final Object? value) { - final type = getCodecType(value); - if (type == null) { - super.writeValue(buffer, value); - } else { - buffer.putUint8(type); - writeValue(buffer, codecMap[type]!.encode(value)); - } - } - - /// Decodes values from [_CodecPair._decoder] available in codec pair, - /// obtained from [codecMap] against [type]. - /// If decoder is not found, [StandardMessageCodec] is used to read value - @override - dynamic readValueOfType(int type, ReadBuffer buffer) { - final pair = codecMap[type]; - if (pair == null) { - return super.readValueOfType(type, buffer); - } else { - final map = toJsonMap(readValue(buffer) as Map?); - return pair.decode(map); - } - } - - // =========== ENCODERS =========== - /// Writes [value] for [key] in [map] if [value] is not null - void _writeToJson(Map map, String key, Object? value) { - assert(value is! DateTime, '`DateTime` objects cannot be encoded'); - if (value == null) return; - map[key] = value; - } - - /// Encodes [ClientOptions] to a Map - /// returns null of [v] is null - Map _encodeClientOptions(final ClientOptions v) { - final jsonMap = {}; - // AuthOptions (super class of ClientOptions) - _writeToJson(jsonMap, TxClientOptions.authUrl, v.authUrl); - _writeToJson(jsonMap, TxClientOptions.authMethod, v.authMethod); - _writeToJson(jsonMap, TxClientOptions.key, v.key); - _writeToJson(jsonMap, TxClientOptions.tokenDetails, - _encodeTokenDetails(v.tokenDetails)); - _writeToJson(jsonMap, TxClientOptions.authHeaders, v.authHeaders); - _writeToJson(jsonMap, TxClientOptions.authParams, v.authParams); - _writeToJson(jsonMap, TxClientOptions.queryTime, v.queryTime); - _writeToJson(jsonMap, TxClientOptions.useTokenAuth, v.useTokenAuth); - _writeToJson( - jsonMap, TxClientOptions.hasAuthCallback, v.authCallback != null); - - // ClientOptions - _writeToJson(jsonMap, TxClientOptions.clientId, v.clientId); - _writeToJson(jsonMap, TxClientOptions.logLevel, v.logLevel); - //TODO handle logHandler - _writeToJson(jsonMap, TxClientOptions.tls, v.tls); - _writeToJson(jsonMap, TxClientOptions.restHost, v.restHost); - _writeToJson(jsonMap, TxClientOptions.realtimeHost, v.realtimeHost); - _writeToJson(jsonMap, TxClientOptions.port, v.port); - _writeToJson(jsonMap, TxClientOptions.tlsPort, v.tlsPort); - _writeToJson(jsonMap, TxClientOptions.autoConnect, v.autoConnect); - _writeToJson( - jsonMap, TxClientOptions.useBinaryProtocol, v.useBinaryProtocol); - _writeToJson(jsonMap, TxClientOptions.queueMessages, v.queueMessages); - _writeToJson(jsonMap, TxClientOptions.echoMessages, v.echoMessages); - _writeToJson(jsonMap, TxClientOptions.recover, v.recover); - _writeToJson(jsonMap, TxClientOptions.environment, v.environment); - _writeToJson(jsonMap, TxClientOptions.idempotentRestPublishing, - v.idempotentRestPublishing); - _writeToJson(jsonMap, TxClientOptions.httpOpenTimeout, v.httpOpenTimeout); - _writeToJson( - jsonMap, TxClientOptions.httpRequestTimeout, v.httpRequestTimeout); - _writeToJson( - jsonMap, TxClientOptions.httpMaxRetryCount, v.httpMaxRetryCount); - _writeToJson(jsonMap, TxClientOptions.realtimeRequestTimeout, - v.realtimeRequestTimeout); - _writeToJson(jsonMap, TxClientOptions.fallbackHosts, v.fallbackHosts); - _writeToJson(jsonMap, TxClientOptions.fallbackHostsUseDefault, - v.fallbackHostsUseDefault); - _writeToJson( - jsonMap, TxClientOptions.fallbackRetryTimeout, v.fallbackRetryTimeout); - _writeToJson(jsonMap, TxClientOptions.defaultTokenParams, - _encodeTokenParams(v.defaultTokenParams)); - _writeToJson( - jsonMap, TxClientOptions.channelRetryTimeout, v.channelRetryTimeout); - _writeToJson(jsonMap, TxClientOptions.transportParams, v.transportParams); - return jsonMap; - } - - /// Encodes [TokenDetails] to a Map - /// returns null if [v] is null - Map? _encodeTokenDetails(final TokenDetails? v) { - if (v == null) return null; - return { - TxTokenDetails.token: v.token, - TxTokenDetails.expires: v.expires, - TxTokenDetails.issued: v.issued, - TxTokenDetails.capability: v.capability, - TxTokenDetails.clientId: v.clientId, - }; - } - - /// Encodes [TokenParams] to a Map - /// returns null if [v] is null - Map? _encodeTokenParams(final TokenParams? v) { - if (v == null) return null; - final jsonMap = {}; - _writeToJson(jsonMap, TxTokenParams.capability, v.capability); - _writeToJson(jsonMap, TxTokenParams.clientId, v.clientId); - _writeToJson(jsonMap, TxTokenParams.nonce, v.nonce); - _writeToJson(jsonMap, TxTokenParams.timestamp, v.timestamp); - _writeToJson(jsonMap, TxTokenParams.ttl, v.ttl); - return jsonMap; - } - - /// Encodes [TokenRequest] to a Map - /// returns null if [v] is null - Map _encodeTokenRequest(final TokenRequest v) { - final jsonMap = {}; - _writeToJson(jsonMap, TxTokenRequest.capability, v.capability); - _writeToJson(jsonMap, TxTokenRequest.clientId, v.clientId); - _writeToJson(jsonMap, TxTokenRequest.keyName, v.keyName); - _writeToJson(jsonMap, TxTokenRequest.mac, v.mac); - _writeToJson(jsonMap, TxTokenRequest.nonce, v.nonce); - _writeToJson( - jsonMap, TxTokenRequest.timestamp, v.timestamp?.millisecondsSinceEpoch); - _writeToJson(jsonMap, TxTokenRequest.ttl, v.ttl); - return jsonMap; - } - - /// Encodes [ChannelOptions] to a Map - /// returns null if [v] is null - Map _encodeRestChannelOptions(final ChannelOptions v) { - final jsonMap = {}; - _writeToJson(jsonMap, TxRestChannelOptions.cipher, v.cipher); - return jsonMap; - } - - /// Encodes [ChannelMode] to a string constant - String _encodeChannelMode(ChannelMode mode) { - switch (mode) { - case ChannelMode.presence: - return TxEnumConstants.presence; - case ChannelMode.publish: - return TxEnumConstants.publish; - case ChannelMode.subscribe: - return TxEnumConstants.subscribe; - case ChannelMode.presenceSubscribe: - return TxEnumConstants.presenceSubscribe; - } - } - - /// Encodes [RealtimeChannelOptions] to a Map - /// returns null if [v] is null - Map _encodeRealtimeChannelOptions( - final RealtimeChannelOptions v) { - final jsonMap = {}; - _writeToJson(jsonMap, TxRealtimeChannelOptions.cipher, v.cipher); - _writeToJson(jsonMap, TxRealtimeChannelOptions.params, v.params); - _writeToJson( - jsonMap, - TxRealtimeChannelOptions.modes, - v.modes?.map(_encodeChannelMode).toList(), - ); - return jsonMap; - } - - /// Encodes [RestHistoryParams] to a Map - /// returns null if [v] is null - Map _encodeRestHistoryParams(final RestHistoryParams v) { - final jsonMap = {}; - _writeToJson( - jsonMap, TxRestHistoryParams.start, v.start.millisecondsSinceEpoch); - _writeToJson( - jsonMap, TxRestHistoryParams.end, v.end.millisecondsSinceEpoch); - _writeToJson(jsonMap, TxRestHistoryParams.direction, v.direction); - _writeToJson(jsonMap, TxRestHistoryParams.limit, v.limit); - return jsonMap; - } - - /// Encodes [RestPresenceParams] to a Map - /// returns null if [v] is null - Map _encodeRestPresenceParams(final RestPresenceParams v) { - final jsonMap = {}; - _writeToJson(jsonMap, TxRestPresenceParams.limit, v.limit); - _writeToJson(jsonMap, TxRestPresenceParams.clientId, v.clientId); - _writeToJson(jsonMap, TxRestPresenceParams.connectionId, v.connectionId); - return jsonMap; - } - - Map _encodeRealtimePresenceParams( - final RealtimePresenceParams v) { - final jsonMap = {}; - _writeToJson(jsonMap, TxRealtimePresenceParams.waitForSync, v.waitForSync); - _writeToJson(jsonMap, TxRealtimePresenceParams.clientId, v.clientId); - _writeToJson( - jsonMap, TxRealtimePresenceParams.connectionId, v.connectionId); - return jsonMap; - } - - /// Encodes [RealtimeHistoryParams] to a Map - /// returns null of [v] is null - Map _encodeRealtimeHistoryParams( - final RealtimeHistoryParams v, - ) { - final jsonMap = {}; - _writeToJson( - jsonMap, - TxRealtimeHistoryParams.start, - v.start.millisecondsSinceEpoch, - ); - _writeToJson( - jsonMap, - TxRealtimeHistoryParams.end, - v.end.millisecondsSinceEpoch, - ); - _writeToJson(jsonMap, TxRealtimeHistoryParams.direction, v.direction); - _writeToJson(jsonMap, TxRealtimeHistoryParams.limit, v.limit); - _writeToJson(jsonMap, TxRealtimeHistoryParams.untilAttach, v.untilAttach); - return jsonMap; - } - - /// Encodes [AblyMessage] to a Map - /// returns null of [v] is null - Map _encodeAblyMessage(final AblyMessage v) { - final codecType = getCodecType(v.message); - final message = (codecType == null) - ? v.message - : codecMap[codecType]!.encode(v.message); - final jsonMap = {}; - _writeToJson(jsonMap, TxAblyMessage.registrationHandle, v.handle); - _writeToJson(jsonMap, TxAblyMessage.type, codecType); - _writeToJson(jsonMap, TxAblyMessage.message, message); - return jsonMap; - } - - /// Encodes [AblyEventMessage] to a Map - /// returns null of [v] is null - Map _encodeAblyEventMessage(final AblyEventMessage v) { - final codecType = getCodecType(v.message); - final message = (v.message == null) - ? null - : (codecType == null) - ? v.message - : codecMap[codecType]!.encode(v.message); - final jsonMap = {}; - _writeToJson(jsonMap, TxAblyEventMessage.eventName, v.eventName); - _writeToJson(jsonMap, TxAblyEventMessage.type, codecType); - _writeToJson(jsonMap, TxAblyEventMessage.message, message); - return jsonMap; - } - - /// Encodes [ErrorInfo] to a Map - /// returns null of [v] is null - Map? _encodeErrorInfo(final ErrorInfo? v) { - if (v == null) return null; - final jsonMap = {}; - _writeToJson(jsonMap, TxErrorInfo.code, v.code); - _writeToJson(jsonMap, TxErrorInfo.message, v.message); - _writeToJson(jsonMap, TxErrorInfo.statusCode, v.statusCode); - _writeToJson(jsonMap, TxErrorInfo.href, v.href); - _writeToJson(jsonMap, TxErrorInfo.requestId, v.requestId); - _writeToJson(jsonMap, TxErrorInfo.cause, v.cause); - return jsonMap; - } - - /// Encodes [MessageData] to a Map - /// returns null of [v] is null - Map? _encodeChannelMessageData(final MessageData? v) { - if (v == null) return null; - final jsonMap = {}; - _writeToJson(jsonMap, TxMessageData.data, v.data); - return jsonMap; - } - - /// Encodes [MessageExtras] to a Map - /// returns null of [v] is null - Map? _encodeChannelMessageExtras(final MessageExtras? v) { - if (v == null) return null; - final jsonMap = {}; - // Not encoding `delta`, as it is a readonly extension - _writeToJson(jsonMap, TxMessageExtras.extras, v.map); - return jsonMap; - } - - /// Encodes [Message] to a Map - /// returns null of [v] is null - Map _encodeChannelMessage(final Message v) { - final jsonMap = {}; - // Note: connectionId and timestamp are automatically set by platform - // So they are suppressed on dart side - _writeToJson(jsonMap, TxMessage.name, v.name); - _writeToJson(jsonMap, TxMessage.clientId, v.clientId); - _writeToJson(jsonMap, TxMessage.data, v.data); - _writeToJson(jsonMap, TxMessage.id, v.id); - _writeToJson(jsonMap, TxMessage.encoding, v.encoding); - _writeToJson(jsonMap, TxMessage.extras, v.extras); - return jsonMap; - } - - // =========== DECODERS =========== - /// Reads [key] value from [jsonMap] - /// Casts it to [T] if the value is not null - T? _readFromJson(Map jsonMap, String key) { - final value = jsonMap[key]; - if (value == null) return null; - return value as T; - } - - /// Decodes value [jsonMap] to [ClientOptions] - /// returns null if [jsonMap] is null - ClientOptions _decodeClientOptions(Map jsonMap) { - final tokenDetails = toJsonMap(_readFromJson( - jsonMap, - TxClientOptions.tokenDetails, - )); - final tokenParams = toJsonMap(_readFromJson( - jsonMap, - TxClientOptions.defaultTokenParams, - )); - return ClientOptions() - // AuthOptions (super class of ClientOptions) - ..authUrl = _readFromJson( - jsonMap, - TxClientOptions.authUrl, - ) - ..authMethod = _readFromJson( - jsonMap, - TxClientOptions.authMethod, - ) - ..key = _readFromJson( - jsonMap, - TxClientOptions.key, - ) - ..tokenDetails = - (tokenDetails == null) ? null : _decodeTokenDetails(tokenDetails) - ..authHeaders = _readFromJson>( - jsonMap, - TxClientOptions.authHeaders, - ) - ..authParams = _readFromJson>( - jsonMap, - TxClientOptions.authParams, - ) - ..queryTime = _readFromJson( - jsonMap, - TxClientOptions.queryTime, - ) - ..useTokenAuth = _readFromJson( - jsonMap, - TxClientOptions.useTokenAuth, - ) - - // ClientOptions - ..clientId = _readFromJson( - jsonMap, - TxClientOptions.clientId, - ) - ..logLevel = _readFromJson( - jsonMap, - TxClientOptions.logLevel, - ) - //TODO handle logHandler - ..tls = _readFromJson( - jsonMap, - TxClientOptions.tls, - ) - ..restHost = _readFromJson( - jsonMap, - TxClientOptions.restHost, - ) - ..realtimeHost = _readFromJson( - jsonMap, - TxClientOptions.realtimeHost, - ) - ..port = _readFromJson( - jsonMap, - TxClientOptions.port, - ) - ..tlsPort = _readFromJson( - jsonMap, - TxClientOptions.tlsPort, - ) - ..autoConnect = _readFromJson( - jsonMap, - TxClientOptions.autoConnect, - ) - ..useBinaryProtocol = _readFromJson( - jsonMap, - TxClientOptions.useBinaryProtocol, - ) - ..queueMessages = _readFromJson( - jsonMap, - TxClientOptions.queueMessages, - ) - ..echoMessages = _readFromJson( - jsonMap, - TxClientOptions.echoMessages, - ) - ..recover = _readFromJson( - jsonMap, - TxClientOptions.recover, - ) - ..environment = _readFromJson( - jsonMap, - TxClientOptions.environment, - ) - ..idempotentRestPublishing = _readFromJson( - jsonMap, - TxClientOptions.idempotentRestPublishing, - ) - ..httpOpenTimeout = _readFromJson( - jsonMap, - TxClientOptions.httpOpenTimeout, - ) - ..httpRequestTimeout = _readFromJson( - jsonMap, - TxClientOptions.httpRequestTimeout, - ) - ..httpMaxRetryCount = _readFromJson( - jsonMap, - TxClientOptions.httpMaxRetryCount, - ) - ..realtimeRequestTimeout = _readFromJson( - jsonMap, - TxClientOptions.realtimeRequestTimeout, - ) - ..fallbackHosts = _readFromJson>( - jsonMap, - TxClientOptions.fallbackHosts, - ) - ..fallbackHostsUseDefault = _readFromJson( - jsonMap, - TxClientOptions.fallbackHostsUseDefault, - ) - ..fallbackRetryTimeout = _readFromJson( - jsonMap, - TxClientOptions.fallbackRetryTimeout, - ) - ..defaultTokenParams = - (tokenParams == null) ? null : _decodeTokenParams(tokenParams) - ..channelRetryTimeout = _readFromJson( - jsonMap, - TxClientOptions.channelRetryTimeout, - ) - ..transportParams = _readFromJson>( - jsonMap, - TxClientOptions.transportParams, - ); - } - - /// Decodes value [jsonMap] to [TokenDetails] - /// returns null if [jsonMap] is null - TokenDetails _decodeTokenDetails(Map jsonMap) => - TokenDetails(_readFromJson(jsonMap, TxTokenDetails.token)) - ..expires = _readFromJson(jsonMap, TxTokenDetails.expires) - ..issued = _readFromJson(jsonMap, TxTokenDetails.issued) - ..capability = _readFromJson(jsonMap, TxTokenDetails.capability) - ..clientId = _readFromJson(jsonMap, TxTokenDetails.clientId); - - /// Decodes value [jsonMap] to [TokenParams] - /// returns null if [jsonMap] is null - TokenParams _decodeTokenParams(Map jsonMap) => TokenParams() - ..capability = _readFromJson(jsonMap, TxTokenParams.capability) - ..clientId = _readFromJson(jsonMap, TxTokenParams.clientId) - ..nonce = _readFromJson(jsonMap, TxTokenParams.nonce) - ..timestamp = DateTime.fromMillisecondsSinceEpoch( - _readFromJson(jsonMap, TxTokenParams.timestamp)!) - ..ttl = _readFromJson(jsonMap, TxTokenParams.ttl); - - /// Decodes value [jsonMap] to [AblyMessage] - /// returns null if [jsonMap] is null - AblyMessage _decodeAblyMessage(Map jsonMap) { - final type = _readFromJson(jsonMap, TxAblyMessage.type); - var message = jsonMap[TxAblyMessage.message] as Object; - if (type != null) { - message = codecMap[type]!.decode( - toJsonMap(_readFromJson(jsonMap, TxAblyMessage.message)), - ) as Object; - } - return AblyMessage( - message, - handle: jsonMap[TxAblyMessage.registrationHandle] as int?, - type: type, - ); - } - - /// Decodes value [jsonMap] to [ErrorInfo] - /// returns null if [jsonMap] is null - ErrorInfo _decodeErrorInfo(Map jsonMap) => ErrorInfo( - code: jsonMap[TxErrorInfo.code] as int?, - message: jsonMap[TxErrorInfo.message] as String?, - statusCode: jsonMap[TxErrorInfo.statusCode] as int?, - href: jsonMap[TxErrorInfo.href] as String?, - requestId: jsonMap[TxErrorInfo.requestId] as String?, - cause: jsonMap[TxErrorInfo.cause] as ErrorInfo?, - ); - - /// Decodes [eventName] to [ConnectionEvent] enum if not null - ConnectionEvent _decodeConnectionEvent(String? eventName) { - switch (eventName) { - case TxEnumConstants.initialized: - return ConnectionEvent.initialized; - case TxEnumConstants.connecting: - return ConnectionEvent.connecting; - case TxEnumConstants.connected: - return ConnectionEvent.connected; - case TxEnumConstants.disconnected: - return ConnectionEvent.disconnected; - case TxEnumConstants.suspended: - return ConnectionEvent.suspended; - case TxEnumConstants.closing: - return ConnectionEvent.closing; - case TxEnumConstants.closed: - return ConnectionEvent.closed; - case TxEnumConstants.failed: - return ConnectionEvent.failed; - case TxEnumConstants.update: - return ConnectionEvent.update; - } - throw AblyException( - 'Platform communication error. Connection event is invalid: $eventName', - ); - } - - /// Decodes [state] to [ConnectionState] enum if not null - ConnectionState _decodeConnectionState(String? state) { - switch (state) { - case TxEnumConstants.initialized: - return ConnectionState.initialized; - case TxEnumConstants.connecting: - return ConnectionState.connecting; - case TxEnumConstants.connected: - return ConnectionState.connected; - case TxEnumConstants.disconnected: - return ConnectionState.disconnected; - case TxEnumConstants.suspended: - return ConnectionState.suspended; - case TxEnumConstants.closing: - return ConnectionState.closing; - case TxEnumConstants.closed: - return ConnectionState.closed; - case TxEnumConstants.failed: - return ConnectionState.failed; - } - throw AblyException( - 'Platform communication error. Connection state is invalid: $state', - ); - } - - /// Decodes [eventName] to [ChannelEvent] enum if not null - ChannelEvent _decodeChannelEvent(String? eventName) { - switch (eventName) { - case TxEnumConstants.initialized: - return ChannelEvent.initialized; - case TxEnumConstants.attaching: - return ChannelEvent.attaching; - case TxEnumConstants.attached: - return ChannelEvent.attached; - case TxEnumConstants.detaching: - return ChannelEvent.detaching; - case TxEnumConstants.detached: - return ChannelEvent.detached; - case TxEnumConstants.suspended: - return ChannelEvent.suspended; - case TxEnumConstants.failed: - return ChannelEvent.failed; - case TxEnumConstants.update: - return ChannelEvent.update; - } - throw AblyException( - 'Platform communication error. Channel event is invalid: $eventName', - ); - } - - /// Decodes [state] to [ChannelState] enum if not null - ChannelState _decodeChannelState(String? state) { - switch (state) { - case TxEnumConstants.initialized: - return ChannelState.initialized; - case TxEnumConstants.attaching: - return ChannelState.attaching; - case TxEnumConstants.attached: - return ChannelState.attached; - case TxEnumConstants.detaching: - return ChannelState.detaching; - case TxEnumConstants.detached: - return ChannelState.detached; - case TxEnumConstants.suspended: - return ChannelState.suspended; - case TxEnumConstants.failed: - return ChannelState.failed; - } - throw AblyException( - 'Platform communication error. Channel state is invalid: $state', - ); - } - - /// Decodes value [jsonMap] to [ConnectionStateChange] - /// returns null if [jsonMap] is null - ConnectionStateChange _decodeConnectionStateChange( - Map jsonMap, - ) { - final current = _decodeConnectionState( - _readFromJson(jsonMap, TxConnectionStateChange.current)); - final previous = _decodeConnectionState( - _readFromJson(jsonMap, TxConnectionStateChange.previous)); - final event = _decodeConnectionEvent( - _readFromJson(jsonMap, TxConnectionStateChange.event)); - final retryIn = - _readFromJson(jsonMap, TxConnectionStateChange.retryIn); - final errorInfo = - toJsonMap(_readFromJson(jsonMap, TxConnectionStateChange.reason)); - final reason = (errorInfo == null) ? null : _decodeErrorInfo(errorInfo); - return ConnectionStateChange( - current, - previous, - event, - retryIn: retryIn, - reason: reason, - ); - } - - /// Decodes value [jsonMap] to [ChannelStateChange] - /// returns null if [jsonMap] is null - ChannelStateChange _decodeChannelStateChange(Map jsonMap) { - final current = _decodeChannelState( - _readFromJson(jsonMap, TxChannelStateChange.current)); - final previous = _decodeChannelState( - _readFromJson(jsonMap, TxChannelStateChange.previous)); - final event = _decodeChannelEvent( - _readFromJson(jsonMap, TxChannelStateChange.event)); - final resumed = _readFromJson(jsonMap, TxChannelStateChange.resumed); - final errorInfo = - toJsonMap(_readFromJson(jsonMap, TxChannelStateChange.reason)); - final reason = (errorInfo == null) ? null : _decodeErrorInfo(errorInfo); - return ChannelStateChange(current, previous, event, - resumed: resumed, reason: reason); - } - - /// Decodes value [jsonMap] to [MessageData] - /// returns null if [jsonMap] is null - MessageData? _decodeChannelMessageData(Map jsonMap) => - MessageData.fromValue(_readFromJson(jsonMap, TxMessageData.data)); - - /// Decodes value [jsonMap] to [MessageExtras] - /// returns null if [jsonMap] is null - MessageExtras? _decodeChannelMessageExtras(Map jsonMap) => - MessageExtras.fromMap(jsonMap); - - /// Decodes value [jsonMap] to [Message] - /// returns null if [jsonMap] is null - Message _decodeChannelMessage(Map jsonMap) { - final timestamp = _readFromJson(jsonMap, TxMessage.timestamp); - // here extras may be a Map or a MessageExtras instance as - // cocoa side doesn't have dedicated models to handle MessageExtras - var extras = _readFromJson(jsonMap, TxMessage.extras); - if (extras is! MessageExtras) { - extras = MessageExtras.fromMap(toJsonMap(extras as Map?)); - } - return Message( - name: _readFromJson(jsonMap, TxMessage.name), - clientId: _readFromJson(jsonMap, TxMessage.clientId), - data: _readFromJson(jsonMap, TxMessage.data), - id: _readFromJson(jsonMap, TxMessage.id), - connectionId: _readFromJson(jsonMap, TxMessage.connectionId), - encoding: _readFromJson(jsonMap, TxMessage.encoding), - extras: extras as MessageExtras?, - timestamp: (timestamp == null) - ? null - : DateTime.fromMillisecondsSinceEpoch(timestamp), - ); - } - - /// Decodes [action] to [PresenceAction] enum if not null - PresenceAction? _decodePresenceAction(String? action) { - switch (action) { - case TxEnumConstants.present: - return PresenceAction.present; - case TxEnumConstants.absent: - return PresenceAction.absent; - case TxEnumConstants.enter: - return PresenceAction.enter; - case TxEnumConstants.leave: - return PresenceAction.leave; - case TxEnumConstants.update: - return PresenceAction.update; - default: - return null; - } - } - - /// Decodes value [jsonMap] to [PresenceMessage] - /// returns null if [jsonMap] is null - PresenceMessage _decodePresenceMessage(Map jsonMap) { - final timestamp = _readFromJson(jsonMap, TxPresenceMessage.timestamp); - return PresenceMessage( - id: _readFromJson(jsonMap, TxPresenceMessage.id), - action: _decodePresenceAction(_readFromJson( - jsonMap, - TxPresenceMessage.action, - )), - clientId: _readFromJson(jsonMap, TxPresenceMessage.clientId), - data: _readFromJson(jsonMap, TxPresenceMessage.data), - connectionId: - _readFromJson(jsonMap, TxPresenceMessage.connectionId), - encoding: _readFromJson(jsonMap, TxPresenceMessage.encoding), - extras: _readFromJson(jsonMap, TxPresenceMessage.extras), - timestamp: (timestamp == null) - ? null - : DateTime.fromMillisecondsSinceEpoch(timestamp), - ); - } - - /// Decodes value [jsonMap] to [PaginatedResult] - /// returns null if [jsonMap] is null - PaginatedResult _decodePaginatedResult(Map jsonMap) { - final type = _readFromJson(jsonMap, TxPaginatedResult.type); - final items = _readFromJson(jsonMap, TxPaginatedResult.items) - ?.map((e) => codecMap[type]?.decode(toJsonMap(e as Map)) as Object) - .toList() ?? - []; - final hasNext = _readFromJson(jsonMap, TxPaginatedResult.hasNext); - if (hasNext == null) { - throw AblyException( - 'Platform communication error. PaginatedResult.hasNext is null', - ); - } - return PaginatedResult(items, hasNext: hasNext); - } -} diff --git a/lib/src/info.dart b/lib/src/info.dart deleted file mode 100644 index 87e65d415..000000000 --- a/lib/src/info.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'generated/platformconstants.dart' show PlatformMethod; -import 'platform.dart' show invokePlatformMethod; - -/// Get android/iOS platform version -Future platformVersion() async => - (await invokePlatformMethod(PlatformMethod.getPlatformVersion))!; - -/// Get ably library version -Future version() async => - (await invokePlatformMethod(PlatformMethod.getVersion))!; diff --git a/lib/src/method_call_handler.dart b/lib/src/method_call_handler.dart deleted file mode 100644 index 6bec286fb..000000000 --- a/lib/src/method_call_handler.dart +++ /dev/null @@ -1,55 +0,0 @@ -import 'package:flutter/services.dart'; - -import '../ably_flutter.dart' as ably; -import 'generated/platformconstants.dart'; -import 'impl/message.dart'; - -/// Handles method calls invoked from platform side to dart side -class AblyMethodCallHandler { - /// creates instance with method channel and forwards calls respective - /// instance methods: [onAuthCallback], [onRealtimeAuthCallback], etc - AblyMethodCallHandler(MethodChannel channel) { - channel.setMethodCallHandler((call) async { - switch (call.method) { - case PlatformMethod.authCallback: - return onAuthCallback(call.arguments as AblyMessage); - case PlatformMethod.realtimeAuthCallback: - return onRealtimeAuthCallback(call.arguments as AblyMessage?); - default: - throw PlatformException( - code: 'invalid_method', message: 'No such method ${call.method}'); - } - }); - } - - /// handles auth callback for rest instances - Future onAuthCallback(AblyMessage message) async { - final tokenParams = message.message as ably.TokenParams; - final rest = ably.restInstances[message.handle]; - if (rest == null) { - throw ably.AblyException('invalid message handle ${message.handle}'); - } - final callbackResponse = await rest.options.authCallback!(tokenParams); - Future.delayed(Duration.zero, rest.authUpdateComplete); - return callbackResponse; - } - - bool _realtimeAuthInProgress = false; - - /// handles auth callback for realtime instances - Future onRealtimeAuthCallback(AblyMessage? message) async { - if (_realtimeAuthInProgress) { - return null; - } - _realtimeAuthInProgress = true; - final tokenParams = message!.message as ably.TokenParams; - final realtime = ably.realtimeInstances[message.handle]; - if (realtime == null) { - throw ably.AblyException('invalid message handle ${message.handle}'); - } - final callbackResponse = await realtime.options.authCallback!(tokenParams); - Future.delayed(Duration.zero, realtime.authUpdateComplete); - _realtimeAuthInProgress = false; - return callbackResponse; - } -} diff --git a/lib/src/platform.dart b/lib/src/platform.dart deleted file mode 100644 index caddf2bdf..000000000 --- a/lib/src/platform.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/services.dart'; - -import 'codec.dart'; -import 'generated/platformconstants.dart' show PlatformMethod; -import 'impl/streams_channel.dart'; -import 'method_call_handler.dart'; -import 'spec/common.dart' show ErrorInfo, AblyException; -import 'spec/constants.dart'; - -/// instance of [StandardMethodCodec] with custom [MessageCodec] for -/// exchanging Ably types with platform via platform channels -/// viz., [MethodChannel] and [StreamsChannel] -StandardMethodCodec codec = StandardMethodCodec(Codec()); - -/// instance of method channel to interact with android/ios code -final MethodChannel methodChannel = - MethodChannel('io.ably.flutter.plugin', codec); - -/// instance of method channel to listen to android/ios events -final StreamsChannel streamsChannel = - StreamsChannel('io.ably.flutter.stream', codec); - -/// Initializing ably on platform side by invoking `register` platform method. -/// Register will clear any stale instances on platform. -Future? _initializer; - -Future _initialize() async { - if (_initializer == null) { - AblyMethodCallHandler(methodChannel); - _initializer = methodChannel - .invokeMethod(PlatformMethod.registerAbly) - .timeout(Timeouts.initializeTimeout, onTimeout: () { - _initializer = null; - throw TimeoutException( - 'Initialization timed out.', - Timeouts.initializeTimeout, - ); - }); - } - return _initializer; -} - -/// invokes a platform [method] with [arguments] -/// -/// calls an [_initialize] method before invoking any method so as to handle -/// any cleanup tasks that are especially required while performing hot-restart -/// (as hot-restart is known to not clear any objects on platform side) -Future invokePlatformMethod(String method, [Object? arguments]) async { - await _initialize(); - try { - return await methodChannel.invokeMethod(method, arguments); - } on PlatformException catch (pe) { - if (pe.details is ErrorInfo) { - throw AblyException.fromPlatformException(pe); - } else { - rethrow; - } - } -} diff --git a/test/ably_flutter_plugin_test.dart b/test/ably_flutter_plugin_test.dart index 73f1a00ca..8a21a8713 100644 --- a/test/ably_flutter_plugin_test.dart +++ b/test/ably_flutter_plugin_test.dart @@ -1,11 +1,9 @@ import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/impl/rest/rest.dart'; -import 'package:ably_flutter/src/info.dart'; -import 'package:ably_flutter/src/platform.dart' as platform; +import 'package:ably_flutter/src/generated/platform_constants.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { - final channel = platform.methodChannel; + final channel = Platform.methodChannel; TestWidgetsFlutterBinding.ensureInitialized(); var counter = 0; diff --git a/test/mock_method_call_manager.dart b/test/mock_method_call_manager.dart index daa1ce655..14c758040 100644 --- a/test/mock_method_call_manager.dart +++ b/test/mock_method_call_manager.dart @@ -11,14 +11,14 @@ class MockMethodCallManager { final publishedMessages = []; MockMethodCallManager() { - methodChannel.setMockMethodCallHandler(handler); + Platform.methodChannel.setMockMethodCallHandler(handler); } void reset() { channels.clear(); publishedMessages.clear(); handleCounter = 0; - methodChannel.setMockMethodCallHandler(null); + Platform.methodChannel.setMockMethodCallHandler(null); } Future handler(MethodCall methodCall) async { @@ -42,7 +42,7 @@ class MockMethodCallManager { // because function references (in `authCallback`) get dropped by the // PlatformChannel. if (!isAuthenticated && clientOptions.authUrl == 'hasAuthCallback') { - await AblyMethodCallHandler(methodChannel).onAuthCallback( + await AblyMethodCallHandler(Platform.methodChannel).onAuthCallback( AblyMessage( TokenParams(timestamp: DateTime.now()), handle: handle, @@ -67,7 +67,7 @@ class MockMethodCallManager { // because function references (in `authCallback`) get dropped by the // PlatformChannel. if (!isAuthenticated && clientOptions.authUrl == 'hasAuthCallback') { - await AblyMethodCallHandler(methodChannel).onRealtimeAuthCallback( + await AblyMethodCallHandler(Platform.methodChannel).onRealtimeAuthCallback( AblyMessage(TokenParams(timestamp: DateTime.now()), handle: handle)); isAuthenticated = true; From 0e07b1d6bbdc7b2721ada135fc229250fc2f5bcd Mon Sep 17 00:00:00 2001 From: Ben Butterworth <24711048+ben-xD@users.noreply.github.com> Date: Wed, 7 Jul 2021 17:10:39 +0100 Subject: [PATCH 06/16] =?UTF-8?q?-=20Use=20mini=20libraries=20and=20export?= =?UTF-8?q?=20them=20instead=20of=20adding=20each=20file=20to=20a=20main?= =?UTF-8?q?=20ably=5Fflutter.dart=20file=20-=20Separate=20out=20files=20fr?= =?UTF-8?q?om=20common.dart,=20enums.dart=20(and=20other=20god=20files=20?= =?UTF-8?q?=F0=9F=98=85)=20into=20their=20classes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CONTRIBUTING.md | 4 +- lib/ably_flutter.dart | 21 +- lib/src/authentication/authentication.dart | 8 + .../{spec => authentication/src}/auth.dart | 3 +- lib/src/authentication/src/auth_options.dart | 94 ++ lib/src/authentication/src/cipher_params.dart | 26 + .../src/client_options.dart} | 104 +- .../authentication/src/http_auth_type.dart | 11 + lib/src/authentication/src/token_details.dart | 53 + lib/src/authentication/src/token_params.dart | 61 ++ lib/src/authentication/src/token_request.dart | 71 ++ lib/src/common/common.dart | 6 + .../{spec/rest => common/src}/ably_base.dart | 10 +- lib/src/common/src/channels.dart | 67 ++ lib/src/common/src/event_emitter.dart | 13 + lib/src/common/src/event_listener.dart | 12 + .../common/src/http_paginated_response.dart | 51 + lib/src/common/src/paginated_result.dart | 33 + lib/src/error/error.dart | 4 + lib/src/error/src/ably_exception.dart | 40 + lib/src/error/src/error_codes.dart | 9 + lib/src/error/src/error_info.dart | 43 + lib/src/error/src/timeouts.dart | 16 + ...constants.dart => platform_constants.dart} | 0 lib/src/logging/logging.dart | 2 + lib/src/logging/src/log_handler.dart | 9 + lib/src/logging/src/log_level.dart | 27 + lib/src/message/message.dart | 6 + lib/src/message/src/delta_extras.dart | 26 + lib/src/message/src/message.dart | 133 +++ lib/src/message/src/message_data.dart | 45 + lib/src/message/src/message_extras.dart | 52 + lib/src/message/src/presence_action.dart | 22 + lib/src/message/src/presence_message.dart | 136 +++ lib/src/platform/platform.dart | 16 + lib/src/platform/src/ably_event_message.dart | 15 + .../src/ably_message.dart} | 19 +- lib/src/platform/src/codec.dart | 891 ++++++++++++++++ lib/src/platform/src/info.dart | 10 + lib/src/platform/src/method_call_handler.dart | 57 + .../src}/paginated_result.dart | 11 +- lib/src/platform/src/platform.dart | 60 ++ .../src}/platform_object.dart | 14 +- .../src}/realtime/connection.dart | 10 +- .../src}/realtime/presence.dart | 13 +- .../src}/realtime/realtime.dart | 16 +- .../src/realtime/realtime_channel.dart} | 18 +- lib/src/{impl => platform/src}/rest/rest.dart | 18 +- .../src/rest/rest_channels.dart} | 14 +- .../src/rest/rest_presence.dart} | 10 +- .../src}/streams_channel.dart | 2 +- .../push_notifications.dart | 14 + .../src/admin/push_admin.dart | 21 + .../src/admin/push_device_registrations.dart | 40 + .../src/device_details.dart | 44 + .../src/device_platform.dart | 12 + .../src/device_push_details.dart | 24 + .../src/device_push_state.dart | 12 + .../src/device_registration_params.dart | 19 + .../push_notifications/src/form_factor.dart | 28 + lib/src/push_notifications/src/push.dart | 27 + .../src/push_channel.dart} | 3 +- .../src/push_channel_params.dart | 7 + .../src/push_channel_subscription.dart | 20 + .../src/push_channel_subscription_params.dart | 17 + .../src/push_channel_subscriptions.dart | 34 + lib/src/realtime/realtime.dart | 13 + lib/src/realtime/src/channel_event.dart | 27 + lib/src/realtime/src/channel_mode.dart | 18 + lib/src/realtime/src/channel_state.dart | 25 + lib/src/realtime/src/channel_state_event.dart | 46 + .../realtime => realtime/src}/channels.dart | 21 +- .../{spec => realtime/src}/connection.dart | 5 +- lib/src/realtime/src/connection_event.dart | 32 + lib/src/realtime/src/connection_state.dart | 27 + .../realtime/src/connection_state_change.dart | 47 + .../realtime => realtime/src}/presence.dart | 11 +- .../realtime => realtime/src}/realtime.dart | 9 +- .../realtime/src/realtime_history_params.dart | 28 + .../src/realtime_presence_params.dart | 27 + lib/src/rest/rest.dart | 7 + lib/src/rest/src/channel_options.dart | 10 + lib/src/{spec/rest => rest/src}/channels.dart | 25 +- lib/src/rest/src/options.dart | 0 lib/src/{spec/rest => rest/src}/presence.dart | 9 +- lib/src/{spec/rest => rest/src}/rest.dart | 10 +- lib/src/rest/src/rest_history_params.dart | 49 + lib/src/rest/src/rest_presence_params.dart | 26 + lib/src/spec/common.dart | 975 ------------------ lib/src/spec/constants.dart | 54 - lib/src/spec/enums.dart | 246 ----- lib/src/spec/message.dart | 376 ------- lib/src/spec/push/push.dart | 113 -- lib/src/spec/spec.dart | 16 - lib/src/stats/stats.dart | 57 + lib/src/stats/stats_connection_types.dart | 16 + lib/src/stats/stats_internal_granularity.dart | 14 + lib/src/stats/stats_message_count.dart | 10 + lib/src/stats/stats_message_traffic.dart | 20 + lib/src/stats/stats_message_types.dart | 16 + lib/src/stats/stats_request_count.dart | 14 + lib/src/stats/stats_resource_count.dart | 20 + 102 files changed, 3125 insertions(+), 2028 deletions(-) create mode 100644 lib/src/authentication/authentication.dart rename lib/src/{spec => authentication/src}/auth.dart (96%) create mode 100644 lib/src/authentication/src/auth_options.dart create mode 100644 lib/src/authentication/src/cipher_params.dart rename lib/src/{spec/rest/options.dart => authentication/src/client_options.dart} (65%) create mode 100644 lib/src/authentication/src/http_auth_type.dart create mode 100644 lib/src/authentication/src/token_details.dart create mode 100644 lib/src/authentication/src/token_params.dart create mode 100644 lib/src/authentication/src/token_request.dart create mode 100644 lib/src/common/common.dart rename lib/src/{spec/rest => common/src}/ably_base.dart (85%) create mode 100644 lib/src/common/src/channels.dart create mode 100644 lib/src/common/src/event_emitter.dart create mode 100644 lib/src/common/src/event_listener.dart create mode 100644 lib/src/common/src/http_paginated_response.dart create mode 100644 lib/src/common/src/paginated_result.dart create mode 100644 lib/src/error/error.dart create mode 100644 lib/src/error/src/ably_exception.dart create mode 100644 lib/src/error/src/error_codes.dart create mode 100644 lib/src/error/src/error_info.dart create mode 100644 lib/src/error/src/timeouts.dart rename lib/src/generated/{platformconstants.dart => platform_constants.dart} (100%) create mode 100644 lib/src/logging/logging.dart create mode 100644 lib/src/logging/src/log_handler.dart create mode 100644 lib/src/logging/src/log_level.dart create mode 100644 lib/src/message/message.dart create mode 100644 lib/src/message/src/delta_extras.dart create mode 100644 lib/src/message/src/message.dart create mode 100644 lib/src/message/src/message_data.dart create mode 100644 lib/src/message/src/message_extras.dart create mode 100644 lib/src/message/src/presence_action.dart create mode 100644 lib/src/message/src/presence_message.dart create mode 100644 lib/src/platform/platform.dart create mode 100644 lib/src/platform/src/ably_event_message.dart rename lib/src/{impl/message.dart => platform/src/ably_message.dart} (60%) create mode 100644 lib/src/platform/src/codec.dart create mode 100644 lib/src/platform/src/info.dart create mode 100644 lib/src/platform/src/method_call_handler.dart rename lib/src/{impl => platform/src}/paginated_result.dart (90%) create mode 100644 lib/src/platform/src/platform.dart rename lib/src/{impl => platform/src}/platform_object.dart (90%) rename lib/src/{impl => platform/src}/realtime/connection.dart (87%) rename lib/src/{impl => platform/src}/realtime/presence.dart (92%) rename lib/src/{impl => platform/src}/realtime/realtime.dart (92%) rename lib/src/{impl/realtime/channels.dart => platform/src/realtime/realtime_channel.dart} (94%) rename lib/src/{impl => platform/src}/rest/rest.dart (82%) rename lib/src/{impl/rest/channels.dart => platform/src/rest/rest_channels.dart} (93%) rename lib/src/{impl/rest/presence.dart => platform/src/rest/rest_presence.dart} (87%) rename lib/src/{impl => platform/src}/streams_channel.dart (98%) create mode 100644 lib/src/push_notifications/push_notifications.dart create mode 100644 lib/src/push_notifications/src/admin/push_admin.dart create mode 100644 lib/src/push_notifications/src/admin/push_device_registrations.dart create mode 100644 lib/src/push_notifications/src/device_details.dart create mode 100644 lib/src/push_notifications/src/device_platform.dart create mode 100644 lib/src/push_notifications/src/device_push_details.dart create mode 100644 lib/src/push_notifications/src/device_push_state.dart create mode 100644 lib/src/push_notifications/src/device_registration_params.dart create mode 100644 lib/src/push_notifications/src/form_factor.dart create mode 100644 lib/src/push_notifications/src/push.dart rename lib/src/{spec/push/channels.dart => push_notifications/src/push_channel.dart} (94%) create mode 100644 lib/src/push_notifications/src/push_channel_params.dart create mode 100644 lib/src/push_notifications/src/push_channel_subscription.dart create mode 100644 lib/src/push_notifications/src/push_channel_subscription_params.dart create mode 100644 lib/src/push_notifications/src/push_channel_subscriptions.dart create mode 100644 lib/src/realtime/realtime.dart create mode 100644 lib/src/realtime/src/channel_event.dart create mode 100644 lib/src/realtime/src/channel_mode.dart create mode 100644 lib/src/realtime/src/channel_state.dart create mode 100644 lib/src/realtime/src/channel_state_event.dart rename lib/src/{spec/realtime => realtime/src}/channels.dart (87%) rename lib/src/{spec => realtime/src}/connection.dart (95%) create mode 100644 lib/src/realtime/src/connection_event.dart create mode 100644 lib/src/realtime/src/connection_state.dart create mode 100644 lib/src/realtime/src/connection_state_change.dart rename lib/src/{spec/realtime => realtime/src}/presence.dart (90%) rename lib/src/{spec/realtime => realtime/src}/realtime.dart (78%) create mode 100644 lib/src/realtime/src/realtime_history_params.dart create mode 100644 lib/src/realtime/src/realtime_presence_params.dart create mode 100644 lib/src/rest/rest.dart create mode 100644 lib/src/rest/src/channel_options.dart rename lib/src/{spec/rest => rest/src}/channels.dart (75%) create mode 100644 lib/src/rest/src/options.dart rename lib/src/{spec/rest => rest/src}/presence.dart (74%) rename lib/src/{spec/rest => rest/src}/rest.dart (70%) create mode 100644 lib/src/rest/src/rest_history_params.dart create mode 100644 lib/src/rest/src/rest_presence_params.dart delete mode 100644 lib/src/spec/common.dart delete mode 100644 lib/src/spec/constants.dart delete mode 100644 lib/src/spec/enums.dart delete mode 100644 lib/src/spec/message.dart delete mode 100644 lib/src/spec/push/push.dart delete mode 100644 lib/src/spec/spec.dart create mode 100644 lib/src/stats/stats.dart create mode 100644 lib/src/stats/stats_connection_types.dart create mode 100644 lib/src/stats/stats_internal_granularity.dart create mode 100644 lib/src/stats/stats_message_count.dart create mode 100644 lib/src/stats/stats_message_traffic.dart create mode 100644 lib/src/stats/stats_message_types.dart create mode 100644 lib/src/stats/stats_request_count.dart create mode 100644 lib/src/stats/stats_resource_count.dart diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 17dae29cc..3d1268f61 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -81,8 +81,8 @@ Some files in the project are generated to maintain sync between Generate platform constants and continue -3. update `getCodecType` in [lib.src.codec.Codec](lib/src/codec.dart) so new codec type is returned based on runtime type -4. update `codecPair` in [lib.src.codec.Codec](lib/src/codec.dart) so new encoder/decoder is assigned for new type +3. update `getCodecType` in [lib.src.codec.Codec](lib/src/native/platform_utilities/codec.dart) so new codec type is returned based on runtime type +4. update `codecPair` in [lib.src.codec.Codec](lib/src/native/platform_utilities/codec.dart) so new encoder/decoder is assigned for new type 5. update `writeValue` in [android.src.main.java.io.ably.flutter.plugin.AblyMessageCodec](android/src/main/java/io/ably/flutter/plugin/AblyMessageCodec.java) so new codec type is obtained from runtime type 6. update `codecMap` in [android.src.main.java.io.ably.flutter.plugin.AblyMessageCodec](android/src/main/java/io/ably/flutter/plugin/AblyMessageCodec.java) diff --git a/lib/ably_flutter.dart b/lib/ably_flutter.dart index 235ef6b4c..e8cce200d 100644 --- a/lib/ably_flutter.dart +++ b/lib/ably_flutter.dart @@ -1,9 +1,12 @@ -export 'src/generated/platformconstants.dart'; -export 'src/impl/paginated_result.dart'; -export 'src/impl/realtime/channels.dart'; -export 'src/impl/realtime/connection.dart'; -export 'src/impl/realtime/realtime.dart'; -export 'src/impl/rest/channels.dart'; -export 'src/impl/rest/rest.dart'; -export 'src/info.dart'; -export 'src/spec/spec.dart'; +library ably_flutter; + +export 'src/authentication/authentication.dart'; +export 'src/common/common.dart'; +export 'src/error/error.dart'; +export 'src/logging/logging.dart'; +export 'src/message/message.dart'; +export 'src/platform/platform.dart'; +export 'src/push_notifications/push_notifications.dart'; +export 'src/realtime/realtime.dart'; +export 'src/rest/rest.dart'; +export 'src/stats/stats.dart'; \ No newline at end of file diff --git a/lib/src/authentication/authentication.dart b/lib/src/authentication/authentication.dart new file mode 100644 index 000000000..f1ee187f1 --- /dev/null +++ b/lib/src/authentication/authentication.dart @@ -0,0 +1,8 @@ +export 'src/auth.dart'; +export 'src/auth_options.dart'; +export 'src/cipher_params.dart'; +export 'src/client_options.dart'; +export 'src/http_auth_type.dart'; +export 'src/token_details.dart'; +export 'src/token_params.dart'; +export 'src/token_request.dart'; \ No newline at end of file diff --git a/lib/src/spec/auth.dart b/lib/src/authentication/src/auth.dart similarity index 96% rename from lib/src/spec/auth.dart rename to lib/src/authentication/src/auth.dart index d0f516173..7ba5ab098 100644 --- a/lib/src/spec/auth.dart +++ b/lib/src/authentication/src/auth.dart @@ -1,5 +1,4 @@ -import 'common.dart'; -import 'rest/options.dart'; +import '../authentication.dart'; /// [Auth] object provides a way to create [TokenRequest] objects /// with [createTokenRequest] method or create Ably Tokens with diff --git a/lib/src/authentication/src/auth_options.dart b/lib/src/authentication/src/auth_options.dart new file mode 100644 index 000000000..bf07ea9fe --- /dev/null +++ b/lib/src/authentication/src/auth_options.dart @@ -0,0 +1,94 @@ +import '../authentication.dart'; + +/// A class providing configurable authentication options used when +/// authenticating or issuing tokens explicitly. +/// +/// These options are used when invoking Auth#authorize, Auth#requestToken, +/// Auth#createTokenRequest and Auth#authorize. +/// +/// https://docs.ably.com/client-lib-development-guide/features/#AO1 +abstract class AuthOptions { + /// initializes an instance without any defaults + AuthOptions(); + + /// Convenience constructor, to create an AuthOptions based + /// on the key string obtained from the application dashboard. + /// param [key]: the full key string as obtained from the dashboard + AuthOptions.fromKey(String key) { + if (key.contains(':')) { + this.key = key; + } else { + tokenDetails = TokenDetails(key); + } + } + + /// A function which is called when a new token is required. + /// + /// The role of the callback is to either generate a signed [TokenRequest] + /// which may then be submitted automatically by the library to + /// the Ably REST API requestToken; or to provide a valid token + /// as a [TokenDetails] object. + /// https://docs.ably.com/client-lib-development-guide/features/#AO2b + AuthCallback? authCallback; + + /// A URL that the library may use to obtain + /// a token String (in plain text format), + /// or a signed [TokenRequest] or [TokenDetails] (in JSON format). + /// + /// https://docs.ably.com/client-lib-development-guide/features/#AO2c + String? authUrl; + + /// HTTP Method used when a request is made using authURL + /// + /// defaults to 'GET', supports 'GET' and 'POST' + /// https://docs.ably.com/client-lib-development-guide/features/#AO2d + String? authMethod; + + /// Full Ably key string, as obtained from dashboard, + /// used when signing token requests locally + /// + /// https://docs.ably.com/client-lib-development-guide/features/#AO2a + String? key; + + /// An authentication token issued for this application + /// + /// https://docs.ably.com/client-lib-development-guide/features/#AO2i + TokenDetails? tokenDetails; + + /// Headers to be included in any request made to the [authUrl] + /// + /// https://docs.ably.com/client-lib-development-guide/features/#AO2e + Map? authHeaders; + + /// Additional params to be included in any request made to the [authUrl] + /// + /// As query params in the case of GET + /// and as form-encoded in the body in the case of POST + /// https://docs.ably.com/client-lib-development-guide/features/#AO2f + Map? authParams; + + /// If true, the library will when issuing a token request query + /// the Ably system for the current time instead of relying on a + /// locally-available time of day. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#AO2g + bool? queryTime; + + /// Token Auth is used if useTokenAuth is set to true + /// + /// or if useTokenAuth is unspecified and any one of + /// [authUrl], [authCallback], token, or [TokenDetails] is provided + /// https://docs.ably.com/client-lib-development-guide/features/#RSA4 + bool? useTokenAuth; + +// TODO(tiholic) missing token attribute here +// see: https://docs.ably.com/client-lib-development-guide/features/#AO2h +} + +/// Function-type alias implemented by a function that provides either tokens, +/// or signed token requests, in response to a request with given token params. +/// +/// Java: io.ably.lib.rest.Auth.TokenCallback.getTokenRequest(TokenParams) +/// returns either a [String] token or [TokenDetails] or [TokenRequest] +typedef AuthCallback = Future Function(TokenParams params); + diff --git a/lib/src/authentication/src/cipher_params.dart b/lib/src/authentication/src/cipher_params.dart new file mode 100644 index 000000000..da3bb60fe --- /dev/null +++ b/lib/src/authentication/src/cipher_params.dart @@ -0,0 +1,26 @@ +/// params to configure encryption for a channel +/// +/// https://docs.ably.com/client-lib-development-guide/features/#TZ1 +abstract class CipherParams { + /// Specifies the algorithm to use for encryption + /// + /// Default is AES. Currently only AES is supported. + /// https://docs.ably.com/client-lib-development-guide/features/#TZ2a + String? algorithm; + + /// private key used to encrypt and decrypt payloads + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TZ2d + dynamic key; + + /// the length in bits of the key + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TZ2b + int? keyLength; + + /// Specify cipher mode + /// + /// Default is CBC. Currently only CBC is supported + /// https://docs.ably.com/client-lib-development-guide/features/#TZ2c + String? mode; +} diff --git a/lib/src/spec/rest/options.dart b/lib/src/authentication/src/client_options.dart similarity index 65% rename from lib/src/spec/rest/options.dart rename to lib/src/authentication/src/client_options.dart index 6af7ab943..e54e329df 100644 --- a/lib/src/spec/rest/options.dart +++ b/lib/src/authentication/src/client_options.dart @@ -1,105 +1,5 @@ -import '../../../ably_flutter.dart'; -import '../common.dart'; - -/// Function-type alias implemented by a function that provides either tokens, -/// or signed token requests, in response to a request with given token params. -/// -/// Java: io.ably.lib.rest.Auth.TokenCallback.getTokenRequest(TokenParams) -/// returns either a [String] token or [TokenDetails] or [TokenRequest] -typedef AuthCallback = Future Function(TokenParams params); - -/// A class providing configurable authentication options used when -/// authenticating or issuing tokens explicitly. -/// -/// These options are used when invoking Auth#authorize, Auth#requestToken, -/// Auth#createTokenRequest and Auth#authorize. -/// -/// https://docs.ably.com/client-lib-development-guide/features/#AO1 -abstract class AuthOptions { - /// initializes an instance without any defaults - AuthOptions(); - - /// Convenience constructor, to create an AuthOptions based - /// on the key string obtained from the application dashboard. - /// param [key]: the full key string as obtained from the dashboard - AuthOptions.fromKey(String key) { - if (key.contains(':')) { - this.key = key; - } else { - tokenDetails = TokenDetails(key); - } - } - - /// A function which is called when a new token is required. - /// - /// The role of the callback is to either generate a signed [TokenRequest] - /// which may then be submitted automatically by the library to - /// the Ably REST API requestToken; or to provide a valid token - /// as a [TokenDetails] object. - /// https://docs.ably.com/client-lib-development-guide/features/#AO2b - AuthCallback? authCallback; - - /// A URL that the library may use to obtain - /// a token String (in plain text format), - /// or a signed [TokenRequest] or [TokenDetails] (in JSON format). - /// - /// https://docs.ably.com/client-lib-development-guide/features/#AO2c - String? authUrl; - - /// HTTP Method used when a request is made using authURL - /// - /// defaults to 'GET', supports 'GET' and 'POST' - /// https://docs.ably.com/client-lib-development-guide/features/#AO2d - String? authMethod; - - /// Full Ably key string, as obtained from dashboard, - /// used when signing token requests locally - /// - /// https://docs.ably.com/client-lib-development-guide/features/#AO2a - String? key; - - /// An authentication token issued for this application - /// - /// https://docs.ably.com/client-lib-development-guide/features/#AO2i - TokenDetails? tokenDetails; - - /// Headers to be included in any request made to the [authUrl] - /// - /// https://docs.ably.com/client-lib-development-guide/features/#AO2e - Map? authHeaders; - - /// Additional params to be included in any request made to the [authUrl] - /// - /// As query params in the case of GET - /// and as form-encoded in the body in the case of POST - /// https://docs.ably.com/client-lib-development-guide/features/#AO2f - Map? authParams; - - /// If true, the library will when issuing a token request query - /// the Ably system for the current time instead of relying on a - /// locally-available time of day. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#AO2g - bool? queryTime; - - /// Token Auth is used if useTokenAuth is set to true - /// - /// or if useTokenAuth is unspecified and any one of - /// [authUrl], [authCallback], token, or [TokenDetails] is provided - /// https://docs.ably.com/client-lib-development-guide/features/#RSA4 - bool? useTokenAuth; - -// TODO(tiholic) missing token attribute here -// see: https://docs.ably.com/client-lib-development-guide/features/#AO2h -} - -/// Custom handler to handle SDK log messages -/// -/// https://docs.ably.com/client-lib-development-guide/features/#TO3c -typedef LogHandler = void Function({ - String? msg, - AblyException? exception, -}); +import '../../logging/logging.dart'; +import '../authentication.dart'; /// Ably library options used when instancing a REST or Realtime client library /// diff --git a/lib/src/authentication/src/http_auth_type.dart b/lib/src/authentication/src/http_auth_type.dart new file mode 100644 index 000000000..803c012f4 --- /dev/null +++ b/lib/src/authentication/src/http_auth_type.dart @@ -0,0 +1,11 @@ +/// Java: io.ably.lib.http.HttpAuth.Type +enum HttpAuthType { + /// indicates basic authentication + basic, + + /// digest authentication + digest, + + /// Token auth + xAblyToken, +} \ No newline at end of file diff --git a/lib/src/authentication/src/token_details.dart b/lib/src/authentication/src/token_details.dart new file mode 100644 index 000000000..19fe69ef9 --- /dev/null +++ b/lib/src/authentication/src/token_details.dart @@ -0,0 +1,53 @@ +/// Response to a `requestToken` request +/// +/// https://docs.ably.com/client-lib-development-guide/features/#TD1 +class TokenDetails { + /// https://docs.ably.com/client-lib-development-guide/features/#TD2 + String? token; + + /// Token expiry time in milliseconds + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TD3 + int? expires; + + /// the time the token was issued in milliseconds + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TD4 + int? issued; + + /// stringified capabilities JSON + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TD5 + String? capability; + + /// Client ID assigned to the token. + /// + /// If [clientId] is not set (i.e. null), then the token is prohibited + /// from assuming a clientId in any operations, however if clientId + /// is a wildcard string '*', then the token is permitted to assume + /// any clientId. Any other string value for clientId implies that the + /// clientId is both enforced and assumed for all operations for this token + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TD6 + String? clientId; + + /// instantiates a [TokenDetails] with provided values + TokenDetails( + this.token, { + this.expires, + this.issued, + this.capability, + this.clientId, + }); + + /// Creates an instance from the map + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TD7 + TokenDetails.fromMap(Map map) { + token = map['token'] as String?; + expires = map['expires'] as int?; + issued = map['issued'] as int?; + capability = map['capability'] as String?; + clientId = map['clientId'] as String?; + } +} diff --git a/lib/src/authentication/src/token_params.dart b/lib/src/authentication/src/token_params.dart new file mode 100644 index 000000000..da6009a15 --- /dev/null +++ b/lib/src/authentication/src/token_params.dart @@ -0,0 +1,61 @@ +/// A class providing parameters of a token request. +/// +/// Parameters for a token request +/// +/// [Auth.authorize], [Auth.requestToken] and [Auth.createTokenRequest] +/// accept an instance of TokenParams as a parameter +/// +/// https://docs.ably.com/client-lib-development-guide/features/#TK1 +class TokenParams { + /// Capability of the token. + /// + /// If the token request is successful, the capability of the + /// returned token will be the intersection of this [capability] + /// with the capability of the issuing key. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TK2b + String? capability; + + /// A clientId to associate with this token. + /// + /// The generated token may be used to authenticate as this clientId. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TK2c + String? clientId; + + /// An opaque nonce string of at least 16 characters to ensure uniqueness. + /// + /// Timestamps, in conjunction with the nonce, + /// are used to prevent requests from being replayed + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TK2d + String? nonce; + + /// The timestamp (in millis since the epoch) of this request. + /// + /// Timestamps, in conjunction with the nonce, are used to prevent + /// token requests from being replayed. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TK2d + DateTime? timestamp; + + /// Requested time to live for the token. + /// + /// If the token request is successful, the TTL of the returned + /// token will be less than or equal to this value depending on + /// application settings and the attributes of the issuing key. + /// + /// 0 means Ably will set it to the default value + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TK2a + int? ttl; + + /// instantiates a [TokenParams] with provided values + TokenParams({ + this.capability, + this.clientId, + this.nonce, + this.timestamp, + this.ttl, + }); +} diff --git a/lib/src/authentication/src/token_request.dart b/lib/src/authentication/src/token_request.dart new file mode 100644 index 000000000..f87c5bece --- /dev/null +++ b/lib/src/authentication/src/token_request.dart @@ -0,0 +1,71 @@ +/// spec: https://docs.ably.com/client-lib-development-guide/features/#TE1 +class TokenRequest { + /// [keyName] is the first part of Ably API Key. + /// + /// provided keyName will be used to authorize requests made to Ably. + /// spec: https://docs.ably.com/client-lib-development-guide/features/#TE2 + /// + /// More details about Ably API Key: + /// https://docs.ably.com/client-lib-development-guide/features/#RSA11 + String? keyName; + + /// An opaque nonce string of at least 16 characters to ensure + /// uniqueness of this request. Any subsequent request using the + /// same nonce will be rejected. + /// + /// spec: + /// https://docs.ably.com/client-lib-development-guide/features/#TE2 + /// https://docs.ably.com/client-lib-development-guide/features/#TE5 + String? nonce; + + /// The "Message Authentication Code" for this request. + /// + /// See the Ably Authentication documentation for more details. + /// spec: https://docs.ably.com/client-lib-development-guide/features/#TE2 + String? mac; + + /// stringified capabilities JSON + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TE3 + String? capability; + + /// Client ID assigned to the tokenRequest. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TE2 + String? clientId; + + /// timestamp long – The timestamp (in milliseconds since the epoch) + /// of this request. Timestamps, in conjunction with the nonce, + /// are used to prevent requests from being replayed + /// + /// spec: https://docs.ably.com/client-lib-development-guide/features/#TE5 + DateTime? timestamp; + + /// ttl attribute represents time to live (expiry) + /// of this token in milliseconds + /// + /// spec: https://docs.ably.com/client-lib-development-guide/features/#TE4 + int? ttl; + + /// instantiates a [TokenRequest] with provided values + TokenRequest({ + this.keyName, + this.nonce, + this.clientId, + this.mac, + this.capability, + this.timestamp, + this.ttl, + }); + + /// spec: https://docs.ably.com/client-lib-development-guide/features/#TE7 + TokenRequest.fromMap(Map map) { + keyName = map['keyName'] as String?; + nonce = map['nonce'] as String?; + mac = map['mac'] as String?; + capability = map['capability'] as String?; + clientId = map['clientId'] as String?; + timestamp = DateTime.fromMillisecondsSinceEpoch(map['timestamp'] as int); + ttl = map['ttl'] as int?; + } +} diff --git a/lib/src/common/common.dart b/lib/src/common/common.dart new file mode 100644 index 000000000..509a5c504 --- /dev/null +++ b/lib/src/common/common.dart @@ -0,0 +1,6 @@ +export 'src/ably_base.dart'; +export 'src/channels.dart'; +export 'src/event_emitter.dart'; +export 'src/event_listener.dart'; +export 'src/http_paginated_response.dart'; +export 'src/paginated_result.dart'; \ No newline at end of file diff --git a/lib/src/spec/rest/ably_base.dart b/lib/src/common/src/ably_base.dart similarity index 85% rename from lib/src/spec/rest/ably_base.dart rename to lib/src/common/src/ably_base.dart index d8dc85486..11460334d 100644 --- a/lib/src/spec/rest/ably_base.dart +++ b/lib/src/common/src/ably_base.dart @@ -1,8 +1,8 @@ -import '../../../ably_flutter.dart'; -import '../auth.dart'; +import '../../authentication/authentication.dart'; +import '../../authentication/src/auth.dart'; +import '../../push_notifications/push_notifications.dart'; +import '../../stats/stats.dart'; import '../common.dart'; -import '../push/push.dart'; -import 'options.dart'; /// A base class for [Rest] and [Realtime] abstract class AblyBase { @@ -27,7 +27,7 @@ abstract class AblyBase { /// viz., subscribing for push notifications, etc Push? push; - /// gets stats based on params as a [PaginatedResult] + /// gets stats based on params as a [PaginatedResultNative] /// /// https://docs.ably.com/client-lib-development-guide/features/#RSC6 Future> stats([ diff --git a/lib/src/common/src/channels.dart b/lib/src/common/src/channels.dart new file mode 100644 index 000000000..9af729619 --- /dev/null +++ b/lib/src/common/src/channels.dart @@ -0,0 +1,67 @@ +import 'package:meta/meta.dart'; + +/// A collection of Channel objects accessible +/// through [Rest.channels] or [Realtime.channels] +abstract class Channels extends Iterable { + /// stores channel name vs instance of [ChannelType] + final _channels = {}; + + /// creates a channel with provided name and options + /// + /// This is a private method to be overridden by implementation classes + @protected + ChannelType createChannel(String name); + + /// creates a channel with [name]. + /// + /// Doesn't create a channel instance on platform side yet. + ChannelType get(String name) { + if (_channels[name] == null) { + _channels[name] = createChannel(name); + } + return _channels[name]!; + } + + /// returns true if a channel exists [name] + bool exists(String name) => _channels[name] != null; + + /// Same as [get]. + ChannelType operator [](String name) => get(name); + + @override + Iterator get iterator => + _ChannelIterator(_channels.values.toList()); + + /// releases channel with [name] + void release(String name) { + _channels.remove(name); + } +} + +/// Iterator class for [Channels.iterator] +class _ChannelIterator implements Iterator { + _ChannelIterator(this._channels); + + final List _channels; + + int _currentIndex = 0; + + T? _currentChannel; + + @override + T get current { + if (_currentChannel == null) { + throw StateError('Not iterating'); + } + return _currentChannel!; + } + + @override + bool moveNext() { + if (_currentIndex == _channels.length) { + return false; + } + _currentChannel = _channels[_currentIndex++]; + return true; + } +} \ No newline at end of file diff --git a/lib/src/common/src/event_emitter.dart b/lib/src/common/src/event_emitter.dart new file mode 100644 index 000000000..b21a02408 --- /dev/null +++ b/lib/src/common/src/event_emitter.dart @@ -0,0 +1,13 @@ +/// Interface implemented by Ably classes that can emit events, +/// offering the capability to create listeners for those events. +/// [E] is type of event to listen for +/// [G] is the instance which will be passed back in streams. +/// +/// +/// There is no `off` API as in other Ably client libraries as on returns a +/// [Stream] which can be subscribed for, and that subscription can be cancelled +/// using [StreamSubscription.cancel] API +abstract class EventEmitter { + /// Create a listener, with which registrations may be made. + Stream on([E? event]); +} \ No newline at end of file diff --git a/lib/src/common/src/event_listener.dart b/lib/src/common/src/event_listener.dart new file mode 100644 index 000000000..f26a56e15 --- /dev/null +++ b/lib/src/common/src/event_listener.dart @@ -0,0 +1,12 @@ +/// Interface implemented by event listeners, returned by event emitters. +abstract class EventListener { + /// Register for all events (no parameter), or a specific event. + Stream on([E? event]); + + /// Register for a single occurrence of any event (no parameter), + /// or a specific event. + Future once([E? event]); + + /// Remove registrations for this listener, irrespective of type. + Future off(); +} \ No newline at end of file diff --git a/lib/src/common/src/http_paginated_response.dart b/lib/src/common/src/http_paginated_response.dart new file mode 100644 index 000000000..af8fdfa7b --- /dev/null +++ b/lib/src/common/src/http_paginated_response.dart @@ -0,0 +1,51 @@ + +import '../common.dart'; + +/// The response from an HTTP request containing an empty or +/// JSON-encodable object response +/// +/// [T] can be a [Map] or [List] +/// +/// https://docs.ably.com/client-lib-development-guide/features/#HP1 +abstract class HttpPaginatedResponse extends PaginatedResultInterface { + /// HTTP status code for the response + /// + /// https://docs.ably.com/client-lib-development-guide/features/#HP4 + int? statusCode; + + /// indicates whether the request is successful + /// + /// true when 200 <= [statusCode] < 300 + /// + /// https://docs.ably.com/client-lib-development-guide/features/#HP5 + bool? success; + + /// Value from X-Ably-Errorcode HTTP header, if available in response + /// + /// https://docs.ably.com/client-lib-development-guide/features/#HP6 + int? errorCode; + + /// Value from X-Ably-Errormessage HTTP header, if available in response + /// + /// https://docs.ably.com/client-lib-development-guide/features/#HP7 + String? errorMessage; + + /// Array of key value pairs of each response header + /// + /// https://docs.ably.com/client-lib-development-guide/features/#HP8 + List>? headers; + + /// returns a new HttpPaginatedResponse loaded with the next page of results. + /// + /// If there are no further pages, then null is returned. + /// https://docs.ably.com/client-lib-development-guide/features/#HP2 + @override + Future> next(); + + /// returns a new HttpPaginatedResponse with the first page of results + /// + /// If there are no further pages, then null is returned. + /// https://docs.ably.com/client-lib-development-guide/features/#HP2 + @override + Future> first(); +} diff --git a/lib/src/common/src/paginated_result.dart b/lib/src/common/src/paginated_result.dart new file mode 100644 index 000000000..5525555a8 --- /dev/null +++ b/lib/src/common/src/paginated_result.dart @@ -0,0 +1,33 @@ +/// PaginatedResult [TG1](https://docs.ably.com/client-lib-development-guide/features/#TG1) +/// +/// A type that represents page results from a paginated query. +/// The response is accompanied by metadata that indicates the +/// relative queries available. +abstract class PaginatedResultInterface { + /// items contain page of results + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TG3 + List get items; + + /// returns a new PaginatedResult loaded with the next page of results. + /// + /// If there are no further pages, then null is returned. + /// https://docs.ably.com/client-lib-development-guide/features/#TG4 + Future> next(); + + /// returns a new PaginatedResult with the first page of results + /// + /// If there are no further pages, then null is returned. + /// https://docs.ably.com/client-lib-development-guide/features/#TG5 + Future> first(); + + /// returns true if there are further pages + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TG6 + bool hasNext(); + + /// returns true if this page is the last page + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TG7 + bool isLast(); +} \ No newline at end of file diff --git a/lib/src/error/error.dart b/lib/src/error/error.dart new file mode 100644 index 000000000..06bce19c8 --- /dev/null +++ b/lib/src/error/error.dart @@ -0,0 +1,4 @@ +export 'src/ably_exception.dart'; +export 'src/error_codes.dart'; +export 'src/error_info.dart'; +export 'src/timeouts.dart'; \ No newline at end of file diff --git a/lib/src/error/src/ably_exception.dart b/lib/src/error/src/ably_exception.dart new file mode 100644 index 000000000..3cf0be95f --- /dev/null +++ b/lib/src/error/src/ably_exception.dart @@ -0,0 +1,40 @@ +import 'package:flutter/services.dart'; + +import 'error_info.dart'; + +/// An exception generated by the native client library called by this plugin +class AblyException implements Exception { + /// platform error code + /// + /// Mostly used for storing [PlatformException.code] + final String? code; + + /// platform error message + /// + /// Mostly used for storing [PlatformException.message] + final String? message; + + /// error message from ably native sdk + final ErrorInfo? errorInfo; + + /// initializes with no defaults + AblyException([ + this.code, + this.message, + this.errorInfo, + ]); + + /// create AblyException from [PlatformException] + AblyException.fromPlatformException(PlatformException exception) + : code = exception.code, + message = exception.message, + errorInfo = exception.details as ErrorInfo?; + + @override + String toString() { + if (message == null) { + return 'AblyException (${(code == null) ? "" : '$code '})'; + } + return 'AblyException: $message (${(code == null) ? "" : '$code '})'; + } +} diff --git a/lib/src/error/src/error_codes.dart b/lib/src/error/src/error_codes.dart new file mode 100644 index 000000000..d6515fbc8 --- /dev/null +++ b/lib/src/error/src/error_codes.dart @@ -0,0 +1,9 @@ +/// Static error codes used inside the SDK +class ErrorCodes { + /// error code sent from platform in case if response from + /// authCallback is not of valid type. + /// When this is encountered, flutter side will re-try the + /// method call after responding to authCallback method channel + /// call triggered from platform side + static const int authCallbackFailure = 80019; +} \ No newline at end of file diff --git a/lib/src/error/src/error_info.dart b/lib/src/error/src/error_info.dart new file mode 100644 index 000000000..d90ac76c5 --- /dev/null +++ b/lib/src/error/src/error_info.dart @@ -0,0 +1,43 @@ +/// An [AblyException] encapsulates [ErrorInfo] which carries details +/// about information related to Ably-specific error [code], +/// generic [statusCode], error [message], +/// link to error related documentation as [href], +/// [requestId] and [cause] of this exception +/// +/// https://docs.ably.com/client-lib-development-guide/features/#TI1 +class ErrorInfo { + /// ably specific error code + final int? code; + + /// link to error related documentation as + final String? href; + + /// error message + final String? message; + + /// cause for the error + final ErrorInfo? cause; + + /// generic status code + final int? statusCode; + + /// request id which triggered this exception + final String? requestId; + + /// instantiates a [ErrorInfo] with provided values + ErrorInfo({ + this.code, + this.href, + this.message, + this.cause, + this.statusCode, + this.requestId, + }); + + @override + String toString() => 'ErrorInfo' + ' message=$message' + ' code=$code' + ' statusCode=$statusCode' + ' href=$href'; +} \ No newline at end of file diff --git a/lib/src/error/src/timeouts.dart b/lib/src/error/src/timeouts.dart new file mode 100644 index 000000000..876cb37e4 --- /dev/null +++ b/lib/src/error/src/timeouts.dart @@ -0,0 +1,16 @@ + + +/// Static timeouts used inside the SDK +class Timeouts { + /// max time allowed for retrying an operation for auth failure + /// in case of usage of authCallback + static const retryOperationOnAuthFailure = Duration(seconds: 30); + + /// max time dart side will wait for platform side to respond with a + /// platform handle + static const acquireHandleTimeout = Duration(seconds: 5); + + /// max time dart side will wait for platform side to respond after + /// initializing an Ably instance on platform side + static const initializeTimeout = Duration(seconds: 5); +} diff --git a/lib/src/generated/platformconstants.dart b/lib/src/generated/platform_constants.dart similarity index 100% rename from lib/src/generated/platformconstants.dart rename to lib/src/generated/platform_constants.dart diff --git a/lib/src/logging/logging.dart b/lib/src/logging/logging.dart new file mode 100644 index 000000000..2c3fa4bf6 --- /dev/null +++ b/lib/src/logging/logging.dart @@ -0,0 +1,2 @@ +export 'src/log_level.dart'; +export 'src/log_handler.dart'; \ No newline at end of file diff --git a/lib/src/logging/src/log_handler.dart b/lib/src/logging/src/log_handler.dart new file mode 100644 index 000000000..a4b7b1ca4 --- /dev/null +++ b/lib/src/logging/src/log_handler.dart @@ -0,0 +1,9 @@ +import '../../error/src/ably_exception.dart'; + +/// Custom handler to handle SDK log messages +/// +/// https://docs.ably.com/client-lib-development-guide/features/#TO3c +typedef LogHandler = void Function({ +String? msg, +AblyException? exception, +}); \ No newline at end of file diff --git a/lib/src/logging/src/log_level.dart b/lib/src/logging/src/log_level.dart new file mode 100644 index 000000000..415fa4a2e --- /dev/null +++ b/lib/src/logging/src/log_level.dart @@ -0,0 +1,27 @@ +/// Log levels - control verbosity of log messages +/// +/// Can be used for [ClientOptions.logLevel] +/// +/// https://docs.ably.com/client-lib-development-guide/features/#TO3b +/// +/// TODO(tiholic) convert [LogLevel] to enum and update encoder to pass +/// right numeric values to platform methods +class LogLevel { + /// No logging + static const int none = 99; + + /// Verbose logs + static const int verbose = 2; + + /// debug logs + static const int debug = 3; + + /// info logs + static const int info = 4; + + /// warning logs + static const int warn = 5; + + /// error logs + static const int error = 6; +} diff --git a/lib/src/message/message.dart b/lib/src/message/message.dart new file mode 100644 index 000000000..17d9150be --- /dev/null +++ b/lib/src/message/message.dart @@ -0,0 +1,6 @@ +export 'src/delta_extras.dart'; +export 'src/message.dart'; +export 'src/message_data.dart'; +export 'src/message_extras.dart'; +export 'src/presence_action.dart'; +export 'src/presence_message.dart'; \ No newline at end of file diff --git a/lib/src/message/src/delta_extras.dart b/lib/src/message/src/delta_extras.dart new file mode 100644 index 000000000..0f858191d --- /dev/null +++ b/lib/src/message/src/delta_extras.dart @@ -0,0 +1,26 @@ +import 'package:meta/meta.dart'; + +import '../../generated/platform_constants.dart'; + +/// Delta extension configuration for [MessageExtras] +@immutable +class DeltaExtras { + /// the id of the message the delta was generated from + final String? from; + + /// the delta format. Only "vcdiff" is supported currently + final String? format; + + /// create instance from a map + @protected + DeltaExtras.fromMap(Map value) + : from = value[TxDeltaExtras.from] as String?, + format = value[TxDeltaExtras.format] as String?; + + @override + bool operator ==(Object other) => + other is DeltaExtras && other.from == from && other.format == format; + + @override + int get hashCode => '$from:$format'.hashCode; +} \ No newline at end of file diff --git a/lib/src/message/src/message.dart b/lib/src/message/src/message.dart new file mode 100644 index 000000000..e28e3aa2c --- /dev/null +++ b/lib/src/message/src/message.dart @@ -0,0 +1,133 @@ + + +import 'package:meta/meta.dart'; + +import '../../rest/src/channel_options.dart'; +import 'message_data.dart'; +import 'message_extras.dart'; + +/// An individual message to be sent/received by Ably +/// +/// https://docs.ably.com/client-lib-development-guide/features/#TM1 +@immutable +class Message { + /// A unique ID for this message + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TM2a + final String? id; + + /// The timestamp for this message + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TM2f + final DateTime? timestamp; + + /// The id of the publisher of this message + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TM2b + final String? clientId; + + /// The connection id of the publisher of this message + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TM2c + final String? connectionId; + + /// Any transformation applied to the data for this message + final String? encoding; + + final MessageData? _data; + + /// Message payload + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TM2d + Object? get data => _data?.data; + + /// Name of the message + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TM2g + final String? name; + + /// Message extras that may contain message metadata + /// and/or ancillary payloads + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TM2i + final MessageExtras? extras; + + /// Creates a message instance with [name], [data] and [clientId] + Message({ + this.id, + this.name, + Object? data, + this.clientId, + this.connectionId, + this.timestamp, + this.encoding, + this.extras, + }) : _data = MessageData.fromValue(data); + + @override + bool operator ==(Object other) => + other is Message && + other.id == id && + other.name == name && + other.data == data && + other.extras == extras && + other.encoding == encoding && + other.clientId == clientId && + other.timestamp == timestamp && + other.connectionId == connectionId; + + @override + int get hashCode => '$id:' + '$name:' + '$encoding:' + '$clientId:' + '$timestamp:' + '$connectionId:' + '${data?.hashCode}:' + '${extras?.hashCode}:' + .hashCode; + + /// https://docs.ably.com/client-lib-development-guide/features/#TM3 + /// + /// TODO(tiholic): decoding and decryption is not implemented as per + /// RSL6 and RLS6b as mentioned in TM3 + Message.fromEncoded( + Map jsonObject, [ + ChannelOptions? channelOptions, + ]) : id = jsonObject['id'] as String?, + name = jsonObject['name'] as String?, + clientId = jsonObject['clientId'] as String?, + connectionId = jsonObject['connectionId'] as String?, + _data = MessageData.fromValue(jsonObject['data']), + encoding = jsonObject['encoding'] as String?, + extras = MessageExtras.fromMap( + Map.castFrom( + jsonObject['extras'] as Map, + ), + ), + timestamp = jsonObject['timestamp'] != null + ? DateTime.fromMillisecondsSinceEpoch( + jsonObject['timestamp'] as int, + ) + : null; + + /// https://docs.ably.com/client-lib-development-guide/features/#TM3 + static List fromEncodedArray( + List> jsonArray, [ + ChannelOptions? channelOptions, + ]) => + jsonArray.map((e) => Message.fromEncoded(e, channelOptions)).toList(); + + @override + String toString() => 'Message' + ' id=$id' + ' name=$name' + ' data=$data' + ' extras=$extras' + ' encoding=$encoding' + ' clientId=$clientId' + ' timestamp=$timestamp' + ' connectionId=$connectionId'; + +// TODO(tiholic) add support for fromEncoded and fromEncodedArray (TM3) +} \ No newline at end of file diff --git a/lib/src/message/src/message_data.dart b/lib/src/message/src/message_data.dart new file mode 100644 index 000000000..5930d3da0 --- /dev/null +++ b/lib/src/message/src/message_data.dart @@ -0,0 +1,45 @@ +import 'dart:typed_data'; + +/// Handles supported message data types, their encoding and decoding +class MessageData { + final T _data; + + /// Only Map, List, string and Buffer types are supported + MessageData(this._data) + : assert(T == Map || T == List || T == String || T == Uint8List); + + /// retrieve data + T get data => _data; + + /// initializes [MessageData] with given value and asserts from input type + static MessageData? fromValue(Object? value) { + if (value == null) { + return null; + } + assert( + value is MessageData || + value is Map || + value is List || + value is String || + value is Uint8List, + 'Message data must be either `Map`, `List`, `String` or `Uint8List`.' + ' Does not support $value ("${value.runtimeType}")', + ); + if (value is MessageData) { + return value; + } else if (value is Map) { + return MessageData(value); + } else if (value is Uint8List) { + return MessageData(value); + } else if (value is List) { + return MessageData(value); + } else if (value is String) { + return MessageData(value); + } else { + throw AssertionError( + 'Message data must be either `Map`, `List`, `String` or `Uint8List`.' + ' Does not support $value ("${value.runtimeType}")', + ); + } + } +} \ No newline at end of file diff --git a/lib/src/message/src/message_extras.dart b/lib/src/message/src/message_extras.dart new file mode 100644 index 000000000..c829209a8 --- /dev/null +++ b/lib/src/message/src/message_extras.dart @@ -0,0 +1,52 @@ +import 'dart:convert'; + +import 'package:collection/collection.dart'; +import 'package:meta/meta.dart'; + +import '../../generated/platform_constants.dart'; +import 'delta_extras.dart'; + +/// Handles supported message extras types, their encoding and decoding +@immutable +class MessageExtras { + /// json-encodable map of extras + final Map? map; + + /// configuration for delta compression extension + final DeltaExtras? _delta; + + /// delta configuration received from channel message + DeltaExtras? get delta => _delta; + + /// Creates an instance from given extras map + const MessageExtras(this.map) : _delta = null; + + /// Creates an instance from given extras map and an instance of DeltaExtras + const MessageExtras._withDelta(this.map, this._delta); + + /// initializes [MessageExtras] with given value and validates + /// the data type, runtime + static MessageExtras? fromMap(Map? extrasMap) { + if (extrasMap == null) return null; + extrasMap = Map.castFrom( + json.decode(json.encode(extrasMap)) as Map, + ); + final deltaMap = extrasMap.remove(TxMessageExtras.delta) as Map?; + return MessageExtras._withDelta( + extrasMap, + (deltaMap == null) ? null : DeltaExtras.fromMap(deltaMap), + ); + } + + @override + String toString() => {'extras': map, 'delta': delta}.toString(); + + @override + bool operator ==(Object other) => + other is MessageExtras && + const MapEquality().equals(other.map, map) && + other.delta == delta; + + @override + int get hashCode => '${map.hashCode}:${delta.hashCode}'.hashCode; +} \ No newline at end of file diff --git a/lib/src/message/src/presence_action.dart b/lib/src/message/src/presence_action.dart new file mode 100644 index 000000000..522add5fd --- /dev/null +++ b/lib/src/message/src/presence_action.dart @@ -0,0 +1,22 @@ +/// Status on a presence message +/// +/// https://docs.ably.com/client-lib-development-guide/features/#TP2 +enum PresenceAction { + /// indicates that a client is absent for incoming [PresenceMessage] + absent, + + /// indicates that a client is present for incoming [PresenceMessage] + present, + + /// indicates that a client wants to enter a channel presence via + /// outgoing [PresenceMessage] + enter, + + /// indicates that a client wants to leave a channel presence via + /// outgoing [PresenceMessage] + leave, + + /// indicates that presence status of a client in presence member map + /// needs to be updated + update, +} \ No newline at end of file diff --git a/lib/src/message/src/presence_message.dart b/lib/src/message/src/presence_message.dart new file mode 100644 index 000000000..a29d9211b --- /dev/null +++ b/lib/src/message/src/presence_message.dart @@ -0,0 +1,136 @@ +import 'package:meta/meta.dart'; + +import '../../rest/rest.dart'; +import 'message_data.dart'; +import 'message_extras.dart'; +import 'presence_action.dart'; + +/// An individual presence message sent or received via realtime +/// +/// https://docs.ably.com/client-lib-development-guide/features/#TP1 +@immutable +class PresenceMessage { + /// unique ID for this presence message + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TP3a + final String? id; + + /// presence action - to update presence status of current client, + /// or to understand presence state of another client + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TP3b + final PresenceAction? action; + + /// https://docs.ably.com/client-lib-development-guide/features/#TP3c + final String? clientId; + + /// connection id of the source of this message + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TP3d + final String? connectionId; + + final MessageData? _data; + + /// Message payload + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TP3e + Object? get data => _data?.data; + + /// https://docs.ably.com/client-lib-development-guide/features/#TP3f + final String? encoding; + + /// Message extras that may contain message metadata + /// and/or ancillary payloads + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TP3i + final MessageExtras? extras; + + /// https://docs.ably.com/client-lib-development-guide/features/#TP3g + final DateTime? timestamp; + + /// https://docs.ably.com/client-lib-development-guide/features/#TP3h + String get memberKey => '$connectionId:$clientId'; + + /// instantiates presence message with + PresenceMessage({ + this.id, + this.action, + this.clientId, + this.connectionId, + Object? data, + this.encoding, + this.extras, + this.timestamp, + }) : _data = MessageData.fromValue(data); + + @override + bool operator ==(Object other) => + other is PresenceMessage && + other.id == id && + other.action == action && + other.clientId == clientId && + other.connectionId == connectionId && + other.data == data && + other.encoding == encoding && + other.extras == extras && + other.timestamp == timestamp; + + @override + int get hashCode => '$id:' + '$encoding:' + '$clientId:' + '$timestamp:' + '$connectionId:' + '${data?.toString()}:' + '${action.toString()}:' + '${extras?.toString()}:' + .hashCode; + + /// https://docs.ably.com/client-lib-development-guide/features/#TP4 + /// + /// TODO(tiholic): decoding and decryption is not implemented as per + /// RSL6 and RLS6b as mentioned in TP4 + PresenceMessage.fromEncoded( + Map jsonObject, [ + ChannelOptions? channelOptions, + ]) : id = jsonObject['id'] as String?, + action = PresenceAction.values.firstWhere((e) => + e.toString().split('.')[1] == jsonObject['action'] as String?), + clientId = jsonObject['clientId'] as String?, + connectionId = jsonObject['connectionId'] as String?, + _data = MessageData.fromValue(jsonObject['data']), + encoding = jsonObject['encoding'] as String?, + extras = MessageExtras.fromMap( + Map.castFrom( + jsonObject['extras'] as Map, + ), + ), + timestamp = jsonObject['timestamp'] != null + ? DateTime.fromMillisecondsSinceEpoch( + jsonObject['timestamp'] as int, + ) + : null; + + /// https://docs.ably.com/client-lib-development-guide/features/#TP4 + static List fromEncodedArray( + List> jsonArray, [ + ChannelOptions? channelOptions, + ]) => + jsonArray + .map((jsonObject) => PresenceMessage.fromEncoded( + jsonObject, + channelOptions, + )) + .toList(); + + @override + String toString() => 'PresenceMessage' + ' id=$id' + ' data=$data' + ' action=$action' + ' extras=$extras' + ' encoding=$encoding' + ' clientId=$clientId' + ' timestamp=$timestamp' + ' connectionId=$connectionId'; +} diff --git a/lib/src/platform/platform.dart b/lib/src/platform/platform.dart new file mode 100644 index 000000000..72266face --- /dev/null +++ b/lib/src/platform/platform.dart @@ -0,0 +1,16 @@ +export 'src/ably_event_message.dart'; +export 'src/ably_message.dart'; +export 'src/codec.dart'; +export 'src/info.dart'; +export 'src/method_call_handler.dart'; +export 'src/paginated_result.dart'; +export 'src/platform.dart'; +export 'src/platform_object.dart'; +export 'src/realtime/connection.dart'; +export 'src/realtime/presence.dart'; +export 'src/realtime/realtime.dart'; +export 'src/realtime/realtime_channel.dart'; +export 'src/rest/rest.dart'; +export 'src/rest/rest_channels.dart'; +export 'src/rest/rest_presence.dart'; +export 'src/streams_channel.dart'; diff --git a/lib/src/platform/src/ably_event_message.dart b/lib/src/platform/src/ably_event_message.dart new file mode 100644 index 000000000..41cb7f780 --- /dev/null +++ b/lib/src/platform/src/ably_event_message.dart @@ -0,0 +1,15 @@ +/// An encapsulating object used to pass data to platform for registering events +class AblyEventMessage { + /// name of the event to register a listener for + final String eventName; + + /// data to be passed for starting a listener + final Object? message; + + /// creates an instance with non-nul [eventName] + /// + /// [message] is optional + /// + /// Raises [AssertionError] if [eventName] is null + AblyEventMessage(this.eventName, [this.message]); +} diff --git a/lib/src/impl/message.dart b/lib/src/platform/src/ably_message.dart similarity index 60% rename from lib/src/impl/message.dart rename to lib/src/platform/src/ably_message.dart index 52035a934..165f8faed 100644 --- a/lib/src/impl/message.dart +++ b/lib/src/platform/src/ably_message.dart @@ -1,4 +1,3 @@ -import '../../ably_flutter.dart'; /// An encapsulating object used to pass data to/from platform for method calls class AblyMessage { @@ -28,20 +27,4 @@ class AblyMessage { handle: source.handle, type: source.type, ); -} - -/// An encapsulating object used to pass data to platform for registering events -class AblyEventMessage { - /// name of the event to register a listener for - final String eventName; - - /// data to be passed for starting a listener - final Object? message; - - /// creates an instance with non-nul [eventName] - /// - /// [message] is optional - /// - /// Raises [AssertionError] if [eventName] is null - AblyEventMessage(this.eventName, [this.message]); -} +} \ No newline at end of file diff --git a/lib/src/platform/src/codec.dart b/lib/src/platform/src/codec.dart new file mode 100644 index 000000000..2a15f3373 --- /dev/null +++ b/lib/src/platform/src/codec.dart @@ -0,0 +1,891 @@ +import 'package:ably_flutter/src/authentication/authentication.dart'; +import 'package:ably_flutter/src/error/error.dart'; +import 'package:ably_flutter/src/generated/platform_constants.dart'; +import 'package:ably_flutter/src/message/message.dart'; +import 'package:ably_flutter/src/realtime/realtime.dart'; +import 'package:ably_flutter/src/rest/rest.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +import '../platform.dart'; + +/// a [_Encoder] encodes custom type and converts it to a Map which will +/// be passed on to platform side +typedef _Encoder = Map? Function(T value); + +/// a [_Decoder] decodes Map received from platform side and converts to +/// to respective dart types +typedef _Decoder = T Function(Map jsonMap); + +/// A class to manage encoding/decoding by provided encoder/decoder functions. +class _CodecPair { + final _Encoder? _encoder; + final _Decoder? _decoder; + + _CodecPair(this._encoder, this._decoder); + + /// Convert properties from an ably library object instance (dart) to Map. + /// if passed [value] is null, encoder will not be called. + /// This method will throw an [AblyException] if encoder is null. + Map? encode(final Object? value) { + if (_encoder == null) throw AblyException('Codec encoder is null'); + if (value == null) return null; + return _encoder!(value as T); + } + + /// Convert Map entries to an ably library object instance (dart). + /// if passed [jsonMap] is null, decoder will not be called. + /// This method will throw an [AblyException] if decoder is null. + T? decode(Map? jsonMap) { + if (_decoder == null) throw AblyException('Codec decoder is null'); + if (jsonMap == null) return null; + return _decoder!(jsonMap); + } +} + +/// Custom message codec for encoding objects to send to platform +/// or decoding objects received from platform. +class Codec extends StandardMessageCodec { + /// Map of codec type (a value from [CodecTypes]) vs encoder/decoder pair. + /// Encoder/decoder can be null. + /// For example, [ErrorInfo] only needs a decoder but not an encoder. + late Map codecMap; + + /// initializes codec with codec map linking codec type to codec pair + Codec() : super() { + codecMap = { + // Ably flutter plugin protocol message + CodecTypes.ablyMessage: + _CodecPair(_encodeAblyMessage, _decodeAblyMessage), + CodecTypes.ablyEventMessage: + _CodecPair(_encodeAblyEventMessage, null), + + // Other ably objects + CodecTypes.clientOptions: + _CodecPair(_encodeClientOptions, _decodeClientOptions), + CodecTypes.tokenParams: + _CodecPair(_encodeTokenParams, _decodeTokenParams), + CodecTypes.tokenDetails: + _CodecPair(_encodeTokenDetails, _decodeTokenDetails), + CodecTypes.tokenRequest: + _CodecPair(_encodeTokenRequest, null), + CodecTypes.restChannelOptions: + _CodecPair(_encodeRestChannelOptions, null), + CodecTypes.realtimeChannelOptions: _CodecPair( + _encodeRealtimeChannelOptions, + null, + ), + CodecTypes.paginatedResult: + _CodecPair(null, _decodePaginatedResult), + CodecTypes.realtimeHistoryParams: + _CodecPair(_encodeRealtimeHistoryParams, null), + CodecTypes.restHistoryParams: + _CodecPair(_encodeRestHistoryParams, null), + CodecTypes.restPresenceParams: + _CodecPair(_encodeRestPresenceParams, null), + CodecTypes.realtimePresenceParams: _CodecPair( + _encodeRealtimePresenceParams, + null, + ), + + CodecTypes.errorInfo: + _CodecPair(_encodeErrorInfo, _decodeErrorInfo), + CodecTypes.messageData: _CodecPair( + _encodeChannelMessageData, + _decodeChannelMessageData, + ), + CodecTypes.messageExtras: _CodecPair( + _encodeChannelMessageExtras, + _decodeChannelMessageExtras, + ), + CodecTypes.message: + _CodecPair(_encodeChannelMessage, _decodeChannelMessage), + CodecTypes.presenceMessage: + _CodecPair(null, _decodePresenceMessage), + + // Events - Connection + CodecTypes.connectionStateChange: + _CodecPair(null, _decodeConnectionStateChange), + + // Events - Channel + CodecTypes.channelStateChange: + _CodecPair(null, _decodeChannelStateChange), + }; + } + + /// Converts a Map with dynamic keys and values to + /// a Map with String keys and dynamic values. + /// Returns null of [value] is null. + Map? toJsonMap(Map? value) { + if (value == null) return null; + return Map.castFrom(value); + } + + /// Returns a value from [CodecTypes] based of the [Type] of [value] + int? getCodecType(final Object? value) { + if (value is ClientOptions) { + return CodecTypes.clientOptions; + } else if (value is TokenDetails) { + return CodecTypes.tokenDetails; + } else if (value is TokenParams) { + return CodecTypes.tokenParams; + } else if (value is TokenRequest) { + return CodecTypes.tokenRequest; + } else if (value is MessageData) { + return CodecTypes.messageData; + } else if (value is MessageExtras) { + return CodecTypes.messageExtras; + } else if (value is Message) { + return CodecTypes.message; + } else if (value is RealtimeHistoryParams) { + return CodecTypes.realtimeHistoryParams; + } else if (value is RestHistoryParams) { + return CodecTypes.restHistoryParams; + } else if (value is RestPresenceParams) { + return CodecTypes.restPresenceParams; + } else if (value is RealtimePresenceParams) { + return CodecTypes.realtimePresenceParams; + } else if (value is ErrorInfo) { + return CodecTypes.errorInfo; + } else if (value is AblyMessage) { + return CodecTypes.ablyMessage; + } else if (value is AblyEventMessage) { + return CodecTypes.ablyEventMessage; + } + // ignore: avoid_returning_null + return null; + } + + /// Encodes values from [_CodecPair._encoder] available in [_CodecPair] + /// obtained from [codecMap] against codecType obtained from [value]. + /// If decoder is not found, [StandardMessageCodec] is used to read value + @override + void writeValue(final WriteBuffer buffer, final Object? value) { + final type = getCodecType(value); + if (type == null) { + super.writeValue(buffer, value); + } else { + buffer.putUint8(type); + writeValue(buffer, codecMap[type]!.encode(value)); + } + } + + /// Decodes values from [_CodecPair._decoder] available in codec pair, + /// obtained from [codecMap] against [type]. + /// If decoder is not found, [StandardMessageCodec] is used to read value + @override + dynamic readValueOfType(int type, ReadBuffer buffer) { + final pair = codecMap[type]; + if (pair == null) { + return super.readValueOfType(type, buffer); + } else { + final map = toJsonMap(readValue(buffer) as Map?); + return pair.decode(map); + } + } + + // =========== ENCODERS =========== + /// Writes [value] for [key] in [map] if [value] is not null + void _writeToJson(Map map, String key, Object? value) { + assert(value is! DateTime, '`DateTime` objects cannot be encoded'); + if (value == null) return; + map[key] = value; + } + + /// Encodes [ClientOptions] to a Map + /// returns null of [v] is null + Map _encodeClientOptions(final ClientOptions v) { + final jsonMap = {}; + // AuthOptions (super class of ClientOptions) + _writeToJson(jsonMap, TxClientOptions.authUrl, v.authUrl); + _writeToJson(jsonMap, TxClientOptions.authMethod, v.authMethod); + _writeToJson(jsonMap, TxClientOptions.key, v.key); + _writeToJson(jsonMap, TxClientOptions.tokenDetails, + _encodeTokenDetails(v.tokenDetails)); + _writeToJson(jsonMap, TxClientOptions.authHeaders, v.authHeaders); + _writeToJson(jsonMap, TxClientOptions.authParams, v.authParams); + _writeToJson(jsonMap, TxClientOptions.queryTime, v.queryTime); + _writeToJson(jsonMap, TxClientOptions.useTokenAuth, v.useTokenAuth); + _writeToJson( + jsonMap, TxClientOptions.hasAuthCallback, v.authCallback != null); + + // ClientOptions + _writeToJson(jsonMap, TxClientOptions.clientId, v.clientId); + _writeToJson(jsonMap, TxClientOptions.logLevel, v.logLevel); + //TODO handle logHandler + _writeToJson(jsonMap, TxClientOptions.tls, v.tls); + _writeToJson(jsonMap, TxClientOptions.restHost, v.restHost); + _writeToJson(jsonMap, TxClientOptions.realtimeHost, v.realtimeHost); + _writeToJson(jsonMap, TxClientOptions.port, v.port); + _writeToJson(jsonMap, TxClientOptions.tlsPort, v.tlsPort); + _writeToJson(jsonMap, TxClientOptions.autoConnect, v.autoConnect); + _writeToJson( + jsonMap, TxClientOptions.useBinaryProtocol, v.useBinaryProtocol); + _writeToJson(jsonMap, TxClientOptions.queueMessages, v.queueMessages); + _writeToJson(jsonMap, TxClientOptions.echoMessages, v.echoMessages); + _writeToJson(jsonMap, TxClientOptions.recover, v.recover); + _writeToJson(jsonMap, TxClientOptions.environment, v.environment); + _writeToJson(jsonMap, TxClientOptions.idempotentRestPublishing, + v.idempotentRestPublishing); + _writeToJson(jsonMap, TxClientOptions.httpOpenTimeout, v.httpOpenTimeout); + _writeToJson( + jsonMap, TxClientOptions.httpRequestTimeout, v.httpRequestTimeout); + _writeToJson( + jsonMap, TxClientOptions.httpMaxRetryCount, v.httpMaxRetryCount); + _writeToJson(jsonMap, TxClientOptions.realtimeRequestTimeout, + v.realtimeRequestTimeout); + _writeToJson(jsonMap, TxClientOptions.fallbackHosts, v.fallbackHosts); + _writeToJson(jsonMap, TxClientOptions.fallbackHostsUseDefault, + v.fallbackHostsUseDefault); + _writeToJson( + jsonMap, TxClientOptions.fallbackRetryTimeout, v.fallbackRetryTimeout); + _writeToJson(jsonMap, TxClientOptions.defaultTokenParams, + _encodeTokenParams(v.defaultTokenParams)); + _writeToJson( + jsonMap, TxClientOptions.channelRetryTimeout, v.channelRetryTimeout); + _writeToJson(jsonMap, TxClientOptions.transportParams, v.transportParams); + return jsonMap; + } + + /// Encodes [TokenDetails] to a Map + /// returns null if [v] is null + Map? _encodeTokenDetails(final TokenDetails? v) { + if (v == null) return null; + return { + TxTokenDetails.token: v.token, + TxTokenDetails.expires: v.expires, + TxTokenDetails.issued: v.issued, + TxTokenDetails.capability: v.capability, + TxTokenDetails.clientId: v.clientId, + }; + } + + /// Encodes [TokenParams] to a Map + /// returns null if [v] is null + Map? _encodeTokenParams(final TokenParams? v) { + if (v == null) return null; + final jsonMap = {}; + _writeToJson(jsonMap, TxTokenParams.capability, v.capability); + _writeToJson(jsonMap, TxTokenParams.clientId, v.clientId); + _writeToJson(jsonMap, TxTokenParams.nonce, v.nonce); + _writeToJson(jsonMap, TxTokenParams.timestamp, v.timestamp); + _writeToJson(jsonMap, TxTokenParams.ttl, v.ttl); + return jsonMap; + } + + /// Encodes [TokenRequest] to a Map + /// returns null if [v] is null + Map _encodeTokenRequest(final TokenRequest v) { + final jsonMap = {}; + _writeToJson(jsonMap, TxTokenRequest.capability, v.capability); + _writeToJson(jsonMap, TxTokenRequest.clientId, v.clientId); + _writeToJson(jsonMap, TxTokenRequest.keyName, v.keyName); + _writeToJson(jsonMap, TxTokenRequest.mac, v.mac); + _writeToJson(jsonMap, TxTokenRequest.nonce, v.nonce); + _writeToJson( + jsonMap, TxTokenRequest.timestamp, v.timestamp?.millisecondsSinceEpoch); + _writeToJson(jsonMap, TxTokenRequest.ttl, v.ttl); + return jsonMap; + } + + /// Encodes [ChannelOptions] to a Map + /// returns null if [v] is null + Map _encodeRestChannelOptions(final ChannelOptions v) { + final jsonMap = {}; + _writeToJson(jsonMap, TxRestChannelOptions.cipher, v.cipher); + return jsonMap; + } + + /// Encodes [ChannelMode] to a string constant + String _encodeChannelMode(ChannelMode mode) { + switch (mode) { + case ChannelMode.presence: + return TxEnumConstants.presence; + case ChannelMode.publish: + return TxEnumConstants.publish; + case ChannelMode.subscribe: + return TxEnumConstants.subscribe; + case ChannelMode.presenceSubscribe: + return TxEnumConstants.presenceSubscribe; + } + } + + /// Encodes [RealtimeChannelOptions] to a Map + /// returns null if [v] is null + Map _encodeRealtimeChannelOptions( + final RealtimeChannelOptions v) { + final jsonMap = {}; + _writeToJson(jsonMap, TxRealtimeChannelOptions.cipher, v.cipher); + _writeToJson(jsonMap, TxRealtimeChannelOptions.params, v.params); + _writeToJson( + jsonMap, + TxRealtimeChannelOptions.modes, + v.modes?.map(_encodeChannelMode).toList(), + ); + return jsonMap; + } + + /// Encodes [RestHistoryParams] to a Map + /// returns null if [v] is null + Map _encodeRestHistoryParams(final RestHistoryParams v) { + final jsonMap = {}; + _writeToJson( + jsonMap, TxRestHistoryParams.start, v.start.millisecondsSinceEpoch); + _writeToJson( + jsonMap, TxRestHistoryParams.end, v.end.millisecondsSinceEpoch); + _writeToJson(jsonMap, TxRestHistoryParams.direction, v.direction); + _writeToJson(jsonMap, TxRestHistoryParams.limit, v.limit); + return jsonMap; + } + + /// Encodes [RestPresenceParams] to a Map + /// returns null if [v] is null + Map _encodeRestPresenceParams(final RestPresenceParams v) { + final jsonMap = {}; + _writeToJson(jsonMap, TxRestPresenceParams.limit, v.limit); + _writeToJson(jsonMap, TxRestPresenceParams.clientId, v.clientId); + _writeToJson(jsonMap, TxRestPresenceParams.connectionId, v.connectionId); + return jsonMap; + } + + Map _encodeRealtimePresenceParams( + final RealtimePresenceParams v) { + final jsonMap = {}; + _writeToJson(jsonMap, TxRealtimePresenceParams.waitForSync, v.waitForSync); + _writeToJson(jsonMap, TxRealtimePresenceParams.clientId, v.clientId); + _writeToJson( + jsonMap, TxRealtimePresenceParams.connectionId, v.connectionId); + return jsonMap; + } + + /// Encodes [RealtimeHistoryParams] to a Map + /// returns null of [v] is null + Map _encodeRealtimeHistoryParams( + final RealtimeHistoryParams v, + ) { + final jsonMap = {}; + _writeToJson( + jsonMap, + TxRealtimeHistoryParams.start, + v.start.millisecondsSinceEpoch, + ); + _writeToJson( + jsonMap, + TxRealtimeHistoryParams.end, + v.end.millisecondsSinceEpoch, + ); + _writeToJson(jsonMap, TxRealtimeHistoryParams.direction, v.direction); + _writeToJson(jsonMap, TxRealtimeHistoryParams.limit, v.limit); + _writeToJson(jsonMap, TxRealtimeHistoryParams.untilAttach, v.untilAttach); + return jsonMap; + } + + /// Encodes [AblyMessage] to a Map + /// returns null of [v] is null + Map _encodeAblyMessage(final AblyMessage v) { + final codecType = getCodecType(v.message); + final message = (codecType == null) + ? v.message + : codecMap[codecType]!.encode(v.message); + final jsonMap = {}; + _writeToJson(jsonMap, TxAblyMessage.registrationHandle, v.handle); + _writeToJson(jsonMap, TxAblyMessage.type, codecType); + _writeToJson(jsonMap, TxAblyMessage.message, message); + return jsonMap; + } + + /// Encodes [AblyEventMessage] to a Map + /// returns null of [v] is null + Map _encodeAblyEventMessage(final AblyEventMessage v) { + final codecType = getCodecType(v.message); + final message = (v.message == null) + ? null + : (codecType == null) + ? v.message + : codecMap[codecType]!.encode(v.message); + final jsonMap = {}; + _writeToJson(jsonMap, TxAblyEventMessage.eventName, v.eventName); + _writeToJson(jsonMap, TxAblyEventMessage.type, codecType); + _writeToJson(jsonMap, TxAblyEventMessage.message, message); + return jsonMap; + } + + /// Encodes [ErrorInfo] to a Map + /// returns null of [v] is null + Map? _encodeErrorInfo(final ErrorInfo? v) { + if (v == null) return null; + final jsonMap = {}; + _writeToJson(jsonMap, TxErrorInfo.code, v.code); + _writeToJson(jsonMap, TxErrorInfo.message, v.message); + _writeToJson(jsonMap, TxErrorInfo.statusCode, v.statusCode); + _writeToJson(jsonMap, TxErrorInfo.href, v.href); + _writeToJson(jsonMap, TxErrorInfo.requestId, v.requestId); + _writeToJson(jsonMap, TxErrorInfo.cause, v.cause); + return jsonMap; + } + + /// Encodes [MessageData] to a Map + /// returns null of [v] is null + Map? _encodeChannelMessageData(final MessageData? v) { + if (v == null) return null; + final jsonMap = {}; + _writeToJson(jsonMap, TxMessageData.data, v.data); + return jsonMap; + } + + /// Encodes [MessageExtras] to a Map + /// returns null of [v] is null + Map? _encodeChannelMessageExtras(final MessageExtras? v) { + if (v == null) return null; + final jsonMap = {}; + // Not encoding `delta`, as it is a readonly extension + _writeToJson(jsonMap, TxMessageExtras.extras, v.map); + return jsonMap; + } + + /// Encodes [Message] to a Map + /// returns null of [v] is null + Map _encodeChannelMessage(final Message v) { + final jsonMap = {}; + // Note: connectionId and timestamp are automatically set by platform + // So they are suppressed on dart side + _writeToJson(jsonMap, TxMessage.name, v.name); + _writeToJson(jsonMap, TxMessage.clientId, v.clientId); + _writeToJson(jsonMap, TxMessage.data, v.data); + _writeToJson(jsonMap, TxMessage.id, v.id); + _writeToJson(jsonMap, TxMessage.encoding, v.encoding); + _writeToJson(jsonMap, TxMessage.extras, v.extras); + return jsonMap; + } + + // =========== DECODERS =========== + /// Reads [key] value from [jsonMap] + /// Casts it to [T] if the value is not null + T? _readFromJson(Map jsonMap, String key) { + final value = jsonMap[key]; + if (value == null) return null; + return value as T; + } + + /// Decodes value [jsonMap] to [ClientOptions] + /// returns null if [jsonMap] is null + ClientOptions _decodeClientOptions(Map jsonMap) { + final tokenDetails = toJsonMap(_readFromJson( + jsonMap, + TxClientOptions.tokenDetails, + )); + final tokenParams = toJsonMap(_readFromJson( + jsonMap, + TxClientOptions.defaultTokenParams, + )); + return ClientOptions() + // AuthOptions (super class of ClientOptions) + ..authUrl = _readFromJson( + jsonMap, + TxClientOptions.authUrl, + ) + ..authMethod = _readFromJson( + jsonMap, + TxClientOptions.authMethod, + ) + ..key = _readFromJson( + jsonMap, + TxClientOptions.key, + ) + ..tokenDetails = + (tokenDetails == null) ? null : _decodeTokenDetails(tokenDetails) + ..authHeaders = _readFromJson>( + jsonMap, + TxClientOptions.authHeaders, + ) + ..authParams = _readFromJson>( + jsonMap, + TxClientOptions.authParams, + ) + ..queryTime = _readFromJson( + jsonMap, + TxClientOptions.queryTime, + ) + ..useTokenAuth = _readFromJson( + jsonMap, + TxClientOptions.useTokenAuth, + ) + + // ClientOptions + ..clientId = _readFromJson( + jsonMap, + TxClientOptions.clientId, + ) + ..logLevel = _readFromJson( + jsonMap, + TxClientOptions.logLevel, + ) + //TODO handle logHandler + ..tls = _readFromJson( + jsonMap, + TxClientOptions.tls, + ) + ..restHost = _readFromJson( + jsonMap, + TxClientOptions.restHost, + ) + ..realtimeHost = _readFromJson( + jsonMap, + TxClientOptions.realtimeHost, + ) + ..port = _readFromJson( + jsonMap, + TxClientOptions.port, + ) + ..tlsPort = _readFromJson( + jsonMap, + TxClientOptions.tlsPort, + ) + ..autoConnect = _readFromJson( + jsonMap, + TxClientOptions.autoConnect, + ) + ..useBinaryProtocol = _readFromJson( + jsonMap, + TxClientOptions.useBinaryProtocol, + ) + ..queueMessages = _readFromJson( + jsonMap, + TxClientOptions.queueMessages, + ) + ..echoMessages = _readFromJson( + jsonMap, + TxClientOptions.echoMessages, + ) + ..recover = _readFromJson( + jsonMap, + TxClientOptions.recover, + ) + ..environment = _readFromJson( + jsonMap, + TxClientOptions.environment, + ) + ..idempotentRestPublishing = _readFromJson( + jsonMap, + TxClientOptions.idempotentRestPublishing, + ) + ..httpOpenTimeout = _readFromJson( + jsonMap, + TxClientOptions.httpOpenTimeout, + ) + ..httpRequestTimeout = _readFromJson( + jsonMap, + TxClientOptions.httpRequestTimeout, + ) + ..httpMaxRetryCount = _readFromJson( + jsonMap, + TxClientOptions.httpMaxRetryCount, + ) + ..realtimeRequestTimeout = _readFromJson( + jsonMap, + TxClientOptions.realtimeRequestTimeout, + ) + ..fallbackHosts = _readFromJson>( + jsonMap, + TxClientOptions.fallbackHosts, + ) + ..fallbackHostsUseDefault = _readFromJson( + jsonMap, + TxClientOptions.fallbackHostsUseDefault, + ) + ..fallbackRetryTimeout = _readFromJson( + jsonMap, + TxClientOptions.fallbackRetryTimeout, + ) + ..defaultTokenParams = + (tokenParams == null) ? null : _decodeTokenParams(tokenParams) + ..channelRetryTimeout = _readFromJson( + jsonMap, + TxClientOptions.channelRetryTimeout, + ) + ..transportParams = _readFromJson>( + jsonMap, + TxClientOptions.transportParams, + ); + } + + /// Decodes value [jsonMap] to [TokenDetails] + /// returns null if [jsonMap] is null + TokenDetails _decodeTokenDetails(Map jsonMap) => + TokenDetails(_readFromJson(jsonMap, TxTokenDetails.token)) + ..expires = _readFromJson(jsonMap, TxTokenDetails.expires) + ..issued = _readFromJson(jsonMap, TxTokenDetails.issued) + ..capability = _readFromJson(jsonMap, TxTokenDetails.capability) + ..clientId = _readFromJson(jsonMap, TxTokenDetails.clientId); + + /// Decodes value [jsonMap] to [TokenParams] + /// returns null if [jsonMap] is null + TokenParams _decodeTokenParams(Map jsonMap) => TokenParams() + ..capability = _readFromJson(jsonMap, TxTokenParams.capability) + ..clientId = _readFromJson(jsonMap, TxTokenParams.clientId) + ..nonce = _readFromJson(jsonMap, TxTokenParams.nonce) + ..timestamp = DateTime.fromMillisecondsSinceEpoch( + _readFromJson(jsonMap, TxTokenParams.timestamp)!) + ..ttl = _readFromJson(jsonMap, TxTokenParams.ttl); + + /// Decodes value [jsonMap] to [AblyMessage] + /// returns null if [jsonMap] is null + AblyMessage _decodeAblyMessage(Map jsonMap) { + final type = _readFromJson(jsonMap, TxAblyMessage.type); + var message = jsonMap[TxAblyMessage.message] as Object; + if (type != null) { + message = codecMap[type]!.decode( + toJsonMap(_readFromJson(jsonMap, TxAblyMessage.message)), + ) as Object; + } + return AblyMessage( + message, + handle: jsonMap[TxAblyMessage.registrationHandle] as int?, + type: type, + ); + } + + /// Decodes value [jsonMap] to [ErrorInfo] + /// returns null if [jsonMap] is null + ErrorInfo _decodeErrorInfo(Map jsonMap) => ErrorInfo( + code: jsonMap[TxErrorInfo.code] as int?, + message: jsonMap[TxErrorInfo.message] as String?, + statusCode: jsonMap[TxErrorInfo.statusCode] as int?, + href: jsonMap[TxErrorInfo.href] as String?, + requestId: jsonMap[TxErrorInfo.requestId] as String?, + cause: jsonMap[TxErrorInfo.cause] as ErrorInfo?, + ); + + /// Decodes [eventName] to [ConnectionEvent] enum if not null + ConnectionEvent _decodeConnectionEvent(String? eventName) { + switch (eventName) { + case TxEnumConstants.initialized: + return ConnectionEvent.initialized; + case TxEnumConstants.connecting: + return ConnectionEvent.connecting; + case TxEnumConstants.connected: + return ConnectionEvent.connected; + case TxEnumConstants.disconnected: + return ConnectionEvent.disconnected; + case TxEnumConstants.suspended: + return ConnectionEvent.suspended; + case TxEnumConstants.closing: + return ConnectionEvent.closing; + case TxEnumConstants.closed: + return ConnectionEvent.closed; + case TxEnumConstants.failed: + return ConnectionEvent.failed; + case TxEnumConstants.update: + return ConnectionEvent.update; + } + throw AblyException( + 'Platform communication error. Connection event is invalid: $eventName', + ); + } + + /// Decodes [state] to [ConnectionState] enum if not null + ConnectionState _decodeConnectionState(String? state) { + switch (state) { + case TxEnumConstants.initialized: + return ConnectionState.initialized; + case TxEnumConstants.connecting: + return ConnectionState.connecting; + case TxEnumConstants.connected: + return ConnectionState.connected; + case TxEnumConstants.disconnected: + return ConnectionState.disconnected; + case TxEnumConstants.suspended: + return ConnectionState.suspended; + case TxEnumConstants.closing: + return ConnectionState.closing; + case TxEnumConstants.closed: + return ConnectionState.closed; + case TxEnumConstants.failed: + return ConnectionState.failed; + } + throw AblyException( + 'Platform communication error. Connection state is invalid: $state', + ); + } + + /// Decodes [eventName] to [ChannelEvent] enum if not null + ChannelEvent _decodeChannelEvent(String? eventName) { + switch (eventName) { + case TxEnumConstants.initialized: + return ChannelEvent.initialized; + case TxEnumConstants.attaching: + return ChannelEvent.attaching; + case TxEnumConstants.attached: + return ChannelEvent.attached; + case TxEnumConstants.detaching: + return ChannelEvent.detaching; + case TxEnumConstants.detached: + return ChannelEvent.detached; + case TxEnumConstants.suspended: + return ChannelEvent.suspended; + case TxEnumConstants.failed: + return ChannelEvent.failed; + case TxEnumConstants.update: + return ChannelEvent.update; + } + throw AblyException( + 'Platform communication error. Channel event is invalid: $eventName', + ); + } + + /// Decodes [state] to [ChannelState] enum if not null + ChannelState _decodeChannelState(String? state) { + switch (state) { + case TxEnumConstants.initialized: + return ChannelState.initialized; + case TxEnumConstants.attaching: + return ChannelState.attaching; + case TxEnumConstants.attached: + return ChannelState.attached; + case TxEnumConstants.detaching: + return ChannelState.detaching; + case TxEnumConstants.detached: + return ChannelState.detached; + case TxEnumConstants.suspended: + return ChannelState.suspended; + case TxEnumConstants.failed: + return ChannelState.failed; + } + throw AblyException( + 'Platform communication error. Channel state is invalid: $state', + ); + } + + /// Decodes value [jsonMap] to [ConnectionStateChange] + /// returns null if [jsonMap] is null + ConnectionStateChange _decodeConnectionStateChange( + Map jsonMap, + ) { + final current = _decodeConnectionState( + _readFromJson(jsonMap, TxConnectionStateChange.current)); + final previous = _decodeConnectionState( + _readFromJson(jsonMap, TxConnectionStateChange.previous)); + final event = _decodeConnectionEvent( + _readFromJson(jsonMap, TxConnectionStateChange.event)); + final retryIn = + _readFromJson(jsonMap, TxConnectionStateChange.retryIn); + final errorInfo = + toJsonMap(_readFromJson(jsonMap, TxConnectionStateChange.reason)); + final reason = (errorInfo == null) ? null : _decodeErrorInfo(errorInfo); + return ConnectionStateChange( + current, + previous, + event, + retryIn: retryIn, + reason: reason, + ); + } + + /// Decodes value [jsonMap] to [ChannelStateChange] + /// returns null if [jsonMap] is null + ChannelStateChange _decodeChannelStateChange(Map jsonMap) { + final current = _decodeChannelState( + _readFromJson(jsonMap, TxChannelStateChange.current)); + final previous = _decodeChannelState( + _readFromJson(jsonMap, TxChannelStateChange.previous)); + final event = _decodeChannelEvent( + _readFromJson(jsonMap, TxChannelStateChange.event)); + final resumed = _readFromJson(jsonMap, TxChannelStateChange.resumed); + final errorInfo = + toJsonMap(_readFromJson(jsonMap, TxChannelStateChange.reason)); + final reason = (errorInfo == null) ? null : _decodeErrorInfo(errorInfo); + return ChannelStateChange(current, previous, event, + resumed: resumed, reason: reason); + } + + /// Decodes value [jsonMap] to [MessageData] + /// returns null if [jsonMap] is null + MessageData? _decodeChannelMessageData(Map jsonMap) => + MessageData.fromValue(_readFromJson(jsonMap, TxMessageData.data)); + + /// Decodes value [jsonMap] to [MessageExtras] + /// returns null if [jsonMap] is null + MessageExtras? _decodeChannelMessageExtras(Map jsonMap) => + MessageExtras.fromMap(jsonMap); + + /// Decodes value [jsonMap] to [Message] + /// returns null if [jsonMap] is null + Message _decodeChannelMessage(Map jsonMap) { + final timestamp = _readFromJson(jsonMap, TxMessage.timestamp); + // here extras may be a Map or a MessageExtras instance as + // cocoa side doesn't have dedicated models to handle MessageExtras + var extras = _readFromJson(jsonMap, TxMessage.extras); + if (extras is! MessageExtras) { + extras = MessageExtras.fromMap(toJsonMap(extras as Map?)); + } + return Message( + name: _readFromJson(jsonMap, TxMessage.name), + clientId: _readFromJson(jsonMap, TxMessage.clientId), + data: _readFromJson(jsonMap, TxMessage.data), + id: _readFromJson(jsonMap, TxMessage.id), + connectionId: _readFromJson(jsonMap, TxMessage.connectionId), + encoding: _readFromJson(jsonMap, TxMessage.encoding), + extras: extras as MessageExtras?, + timestamp: (timestamp == null) + ? null + : DateTime.fromMillisecondsSinceEpoch(timestamp), + ); + } + + /// Decodes [action] to [PresenceAction] enum if not null + PresenceAction? _decodePresenceAction(String? action) { + switch (action) { + case TxEnumConstants.present: + return PresenceAction.present; + case TxEnumConstants.absent: + return PresenceAction.absent; + case TxEnumConstants.enter: + return PresenceAction.enter; + case TxEnumConstants.leave: + return PresenceAction.leave; + case TxEnumConstants.update: + return PresenceAction.update; + default: + return null; + } + } + + /// Decodes value [jsonMap] to [PresenceMessage] + /// returns null if [jsonMap] is null + PresenceMessage _decodePresenceMessage(Map jsonMap) { + final timestamp = _readFromJson(jsonMap, TxPresenceMessage.timestamp); + return PresenceMessage( + id: _readFromJson(jsonMap, TxPresenceMessage.id), + action: _decodePresenceAction(_readFromJson( + jsonMap, + TxPresenceMessage.action, + )), + clientId: _readFromJson(jsonMap, TxPresenceMessage.clientId), + data: _readFromJson(jsonMap, TxPresenceMessage.data), + connectionId: + _readFromJson(jsonMap, TxPresenceMessage.connectionId), + encoding: _readFromJson(jsonMap, TxPresenceMessage.encoding), + extras: _readFromJson(jsonMap, TxPresenceMessage.extras), + timestamp: (timestamp == null) + ? null + : DateTime.fromMillisecondsSinceEpoch(timestamp), + ); + } + + /// Decodes value [jsonMap] to [PaginatedResult] + /// returns null if [jsonMap] is null + PaginatedResult _decodePaginatedResult(Map jsonMap) { + final type = _readFromJson(jsonMap, TxPaginatedResult.type); + final items = _readFromJson(jsonMap, TxPaginatedResult.items) + ?.map((e) => codecMap[type]?.decode(toJsonMap(e as Map)) as Object) + .toList() ?? + []; + final hasNext = _readFromJson(jsonMap, TxPaginatedResult.hasNext); + if (hasNext == null) { + throw AblyException( + 'Platform communication error. PaginatedResult.hasNext is null', + ); + } + return PaginatedResult(items, hasNext: hasNext); + } +} diff --git a/lib/src/platform/src/info.dart b/lib/src/platform/src/info.dart new file mode 100644 index 000000000..f9527e9d7 --- /dev/null +++ b/lib/src/platform/src/info.dart @@ -0,0 +1,10 @@ +import '../../generated/platform_constants.dart'; +import '../platform.dart'; + +/// Get android/iOS platform version +Future platformVersion() async => + (await Platform.invokePlatformMethod(PlatformMethod.getPlatformVersion))!; + +/// Get ably library version +Future version() async => + (await Platform.invokePlatformMethod(PlatformMethod.getVersion))!; diff --git a/lib/src/platform/src/method_call_handler.dart b/lib/src/platform/src/method_call_handler.dart new file mode 100644 index 000000000..7d4d32a29 --- /dev/null +++ b/lib/src/platform/src/method_call_handler.dart @@ -0,0 +1,57 @@ +import 'package:flutter/services.dart'; + +import '../../authentication/authentication.dart'; +import '../../error/error.dart'; +import '../../generated/platform_constants.dart'; +import '../platform.dart'; +import 'ably_message.dart'; + +/// Handles method calls invoked from platform side to dart side +class AblyMethodCallHandler { + /// creates instance with method channel and forwards calls respective + /// instance methods: [onAuthCallback], [onRealtimeAuthCallback], etc + AblyMethodCallHandler(MethodChannel channel) { + channel.setMethodCallHandler((call) async { + switch (call.method) { + case PlatformMethod.authCallback: + return onAuthCallback(call.arguments as AblyMessage); + case PlatformMethod.realtimeAuthCallback: + return onRealtimeAuthCallback(call.arguments as AblyMessage?); + default: + throw PlatformException( + code: 'invalid_method', message: 'No such method ${call.method}'); + } + }); + } + + /// handles auth callback for rest instances + Future onAuthCallback(AblyMessage message) async { + final tokenParams = message.message as TokenParams; + final rest = restInstances[message.handle]; + if (rest == null) { + throw AblyException('invalid message handle ${message.handle}'); + } + final callbackResponse = await rest.options.authCallback!(tokenParams); + Future.delayed(Duration.zero, rest.authUpdateComplete); + return callbackResponse; + } + + bool _realtimeAuthInProgress = false; + + /// handles auth callback for realtime instances + Future onRealtimeAuthCallback(AblyMessage? message) async { + if (_realtimeAuthInProgress) { + return null; + } + _realtimeAuthInProgress = true; + final tokenParams = message!.message as TokenParams; + final realtime = realtimeInstances[message.handle]; + if (realtime == null) { + throw AblyException('invalid message handle ${message.handle}'); + } + final callbackResponse = await realtime.options.authCallback!(tokenParams); + Future.delayed(Duration.zero, realtime.authUpdateComplete); + _realtimeAuthInProgress = false; + return callbackResponse; + } +} diff --git a/lib/src/impl/paginated_result.dart b/lib/src/platform/src/paginated_result.dart similarity index 90% rename from lib/src/impl/paginated_result.dart rename to lib/src/platform/src/paginated_result.dart index 9ba74859e..10b0aef9c 100644 --- a/lib/src/impl/paginated_result.dart +++ b/lib/src/platform/src/paginated_result.dart @@ -1,7 +1,6 @@ -import '../../ably_flutter.dart'; -import '../spec/spec.dart' as spec; -import 'message.dart'; -import 'platform_object.dart'; +import '../../common/common.dart'; +import '../../generated/platform_constants.dart'; +import '../platform.dart'; /// PaginatedResult [TG1](https://docs.ably.com/client-lib-development-guide/features/#TG1) /// @@ -9,7 +8,7 @@ import 'platform_object.dart'; /// The response is accompanied by metadata that indicates the /// relative queries available. class PaginatedResult extends PlatformObject - implements spec.PaginatedResultInterface { + implements PaginatedResultInterface { /// stores page handle created by platform APIs /// /// handle is updated after creating an instance as they are received @@ -17,7 +16,7 @@ class PaginatedResult extends PlatformObject /// instantiated by the codec. So the code invoking platform method /// is bound to update this [_pageHandle] /// - /// [PaginatedResult.fromAblyMessage] will act as a utility to update + /// [PaginatedResultInterface.fromAblyMessage] will act as a utility to update /// this property. See [next] and [first] for usages int? _pageHandle; diff --git a/lib/src/platform/src/platform.dart b/lib/src/platform/src/platform.dart new file mode 100644 index 000000000..a8a06d70d --- /dev/null +++ b/lib/src/platform/src/platform.dart @@ -0,0 +1,60 @@ +import 'dart:async'; + +import 'package:flutter/services.dart'; + +import '../../error/error.dart'; +import '../../generated/platform_constants.dart'; +import '../platform.dart'; + +class Platform { + /// instance of [StandardMethodCodec] with custom [MessageCodec] for + /// exchanging Ably types with platform via platform channels + /// viz., [MethodChannel] and [StreamsChannel] + static StandardMethodCodec codec = StandardMethodCodec(Codec()); + + /// instance of method channel to interact with android/ios code + static final MethodChannel methodChannel = + MethodChannel('io.ably.flutter.plugin', codec); + + /// instance of method channel to listen to android/ios events + static final StreamsChannel streamsChannel = + StreamsChannel('io.ably.flutter.stream', codec); + + /// Initializing ably on platform side by invoking `register` platform method. + /// Register will clear any stale instances on platform. + static Future? _initializer; + + static Future _initialize() async { + if (_initializer == null) { + AblyMethodCallHandler(methodChannel); + _initializer = methodChannel + .invokeMethod(PlatformMethod.registerAbly) + .timeout(Timeouts.initializeTimeout, onTimeout: () { + _initializer = null; + throw TimeoutException( + 'Initialization timed out.', + Timeouts.initializeTimeout, + ); + }); + } + return _initializer; + } + + /// invokes a platform [method] with [arguments] + /// + /// calls an [_initialize] method before invoking any method so as to handle + /// any cleanup tasks that are especially required while performing hot-restart + /// (as hot-restart is known to not clear any objects on platform side) + static Future invokePlatformMethod(String method, [Object? arguments]) async { + await _initialize(); + try { + return await methodChannel.invokeMethod(method, arguments); + } on PlatformException catch (pe) { + if (pe.details is ErrorInfo) { + throw AblyException.fromPlatformException(pe); + } else { + rethrow; + } + } + } +} diff --git a/lib/src/impl/platform_object.dart b/lib/src/platform/src/platform_object.dart similarity index 90% rename from lib/src/impl/platform_object.dart rename to lib/src/platform/src/platform_object.dart index 4973f505d..a4385e388 100644 --- a/lib/src/impl/platform_object.dart +++ b/lib/src/platform/src/platform_object.dart @@ -3,11 +3,9 @@ import 'dart:async'; import 'package:flutter/services.dart'; import 'package:meta/meta.dart'; -import '../platform.dart' as platform; -import '../spec/common.dart'; -import '../spec/constants.dart'; -import 'message.dart'; -import 'streams_channel.dart'; +import '../../error/error.dart'; +import '../platform.dart'; +import 'ably_event_message.dart'; /// An object which has a live counterpart in the Platform client library SDK, /// where that live counterpart is held as a strong reference by the plugin @@ -50,10 +48,10 @@ abstract class PlatformObject { ).then((value) => (_handleValue = value)!); /// [MethodChannel] to make method calls to platform side - MethodChannel get methodChannel => platform.methodChannel; + MethodChannel get methodChannel => Platform.methodChannel; /// [EventChannel] to register events on platform side - StreamsChannel get eventChannel => platform.streamsChannel; + StreamsChannel get eventChannel => Platform.streamsChannel; /// invoke platform method channel without AblyMessage encapsulation @protected @@ -61,7 +59,7 @@ abstract class PlatformObject { final String method, [ final Object? arguments, ]) async => - platform.invokePlatformMethod(method, arguments); + Platform.invokePlatformMethod(method, arguments); /// invoke platform method channel with AblyMessage encapsulation /// diff --git a/lib/src/impl/realtime/connection.dart b/lib/src/platform/src/realtime/connection.dart similarity index 87% rename from lib/src/impl/realtime/connection.dart rename to lib/src/platform/src/realtime/connection.dart index 58bbc7520..9cd4a9b50 100644 --- a/lib/src/impl/realtime/connection.dart +++ b/lib/src/platform/src/realtime/connection.dart @@ -1,10 +1,10 @@ import 'dart:async'; -import '../../../ably_flutter.dart'; -import '../../spec/spec.dart' - show ConnectionInterface, ConnectionState, ErrorInfo; -import '../platform_object.dart'; -import 'realtime.dart'; +import 'package:ably_flutter/src/error/error.dart'; +import 'package:ably_flutter/src/generated/platform_constants.dart'; +import 'package:ably_flutter/src/realtime/realtime.dart'; + +import '../../platform.dart'; /// connects to Ably service class Connection extends PlatformObject implements ConnectionInterface { diff --git a/lib/src/impl/realtime/presence.dart b/lib/src/platform/src/realtime/presence.dart similarity index 92% rename from lib/src/impl/realtime/presence.dart rename to lib/src/platform/src/realtime/presence.dart index aaedf6271..42fd2d490 100644 --- a/lib/src/impl/realtime/presence.dart +++ b/lib/src/platform/src/realtime/presence.dart @@ -1,12 +1,9 @@ -import '../../generated/platformconstants.dart'; -import '../../spec/spec.dart'; -import '../message.dart'; -import '../paginated_result.dart'; -import '../platform_object.dart'; -import 'channels.dart'; -import 'realtime.dart'; +import '../../../generated/platform_constants.dart'; +import '../../../message/message.dart'; +import '../../../realtime/realtime.dart'; +import '../../platform.dart'; -/// Plugin based implementation of [RestPresenceInterface] +/// Plugin based implementation of [RestPresence] class RealtimePresence extends PlatformObject implements RealtimePresenceInterface { final RealtimeChannel _channel; diff --git a/lib/src/impl/realtime/realtime.dart b/lib/src/platform/src/realtime/realtime.dart similarity index 92% rename from lib/src/impl/realtime/realtime.dart rename to lib/src/platform/src/realtime/realtime.dart index 3612413c4..fd5ffcd84 100644 --- a/lib/src/impl/realtime/realtime.dart +++ b/lib/src/platform/src/realtime/realtime.dart @@ -3,12 +3,14 @@ import 'dart:collection'; import 'package:pedantic/pedantic.dart'; -import '../../../ably_flutter.dart'; -import '../../spec/spec.dart' as spec; -import '../message.dart'; -import '../platform_object.dart'; -import 'channels.dart'; -import 'connection.dart'; +import '../../../authentication/authentication.dart'; +import '../../../common/common.dart'; +import '../../../error/error.dart'; +import '../../../generated/platform_constants.dart'; +import '../../../push_notifications/push_notifications.dart'; +import '../../../realtime/realtime.dart'; +import '../../../stats/stats.dart'; +import '../../platform.dart'; Map _realtimeInstances = {}; Map? _realtimeInstancesUnmodifiableView; @@ -20,7 +22,7 @@ Map get realtimeInstances => /// Ably's Realtime client class Realtime extends PlatformObject - implements spec.RealtimeInterface { + implements RealtimeInterface { /// instantiates with [ClientOptions] and a String [key] /// /// creates client options from key if [key] is provided diff --git a/lib/src/impl/realtime/channels.dart b/lib/src/platform/src/realtime/realtime_channel.dart similarity index 94% rename from lib/src/impl/realtime/channels.dart rename to lib/src/platform/src/realtime/realtime_channel.dart index c956e7c4d..02f50eb58 100644 --- a/lib/src/impl/realtime/channels.dart +++ b/lib/src/platform/src/realtime/realtime_channel.dart @@ -5,12 +5,12 @@ import 'package:flutter/services.dart'; import 'package:meta/meta.dart'; import 'package:pedantic/pedantic.dart'; -import '../../../ably_flutter.dart'; -import '../../spec/push/channels.dart'; -import '../message.dart'; -import '../platform_object.dart'; -import 'presence.dart'; -import 'realtime.dart'; +import '../../../error/error.dart'; +import '../../../generated/platform_constants.dart'; +import '../../../message/message.dart'; +import '../../../push_notifications/push_notifications.dart'; +import '../../../realtime/realtime.dart'; +import '../../platform.dart'; /// Plugin based implementation of Realtime channel class RealtimeChannel extends PlatformObject @@ -211,13 +211,15 @@ class RealtimeChannel extends PlatformObject /// A collection of realtime channel objects /// /// https://docs.ably.com/client-lib-development-guide/features/#RTS1 -class RealtimePlatformChannels extends RealtimeChannels { +class RealtimePlatformChannels + extends RealtimeChannelsInterface { /// instantiates with the ably [Realtime] instance RealtimePlatformChannels(Realtime realtime) : super(realtime); @override @protected - RealtimeChannel createChannel(String name) => RealtimeChannel(realtime, name); + RealtimeChannel createChannel(String name) + => RealtimeChannel(realtime, name); @override void release(String name) { diff --git a/lib/src/impl/rest/rest.dart b/lib/src/platform/src/rest/rest.dart similarity index 82% rename from lib/src/impl/rest/rest.dart rename to lib/src/platform/src/rest/rest.dart index 4ac871163..2551d177c 100644 --- a/lib/src/impl/rest/rest.dart +++ b/lib/src/platform/src/rest/rest.dart @@ -1,11 +1,13 @@ import 'dart:async'; import 'dart:collection'; -import '../../../ably_flutter.dart'; -import '../../spec/spec.dart' as spec; -import '../message.dart'; -import '../platform_object.dart'; -import 'channels.dart'; +import '../../../authentication/authentication.dart'; +import '../../../common/common.dart'; +import '../../../generated/platform_constants.dart'; +import '../../../push_notifications/push_notifications.dart'; +import '../../../rest/rest.dart'; +import '../../../stats/stats.dart'; +import '../../platform.dart'; Map _restInstances = {}; Map? _restInstancesUnmodifiableView; @@ -16,7 +18,7 @@ Map get restInstances => /// Ably's Rest client class Rest extends PlatformObject - implements spec.RestInterface { + implements RestInterface { /// instantiates with [ClientOptions] and a String [key] /// /// creates client options from key if [key] is provided @@ -28,7 +30,7 @@ class Rest extends PlatformObject }) : assert(options != null || key != null), options = options ?? ClientOptions.fromKey(key!), super() { - channels = RestPlatformChannels(this); + channels = RestChannels(this); } @override @@ -84,5 +86,5 @@ class Rest extends PlatformObject Push? push; @override - late RestPlatformChannels channels; + late RestChannels channels; } diff --git a/lib/src/impl/rest/channels.dart b/lib/src/platform/src/rest/rest_channels.dart similarity index 93% rename from lib/src/impl/rest/channels.dart rename to lib/src/platform/src/rest/rest_channels.dart index 1e8c1000d..57c75ce45 100644 --- a/lib/src/impl/rest/channels.dart +++ b/lib/src/platform/src/rest/rest_channels.dart @@ -1,15 +1,15 @@ import 'dart:async'; import 'dart:collection'; +import 'package:ably_flutter/src/error/error.dart'; +import 'package:ably_flutter/src/generated/platform_constants.dart'; +import 'package:ably_flutter/src/message/message.dart'; +import 'package:ably_flutter/src/rest/rest.dart'; import 'package:flutter/services.dart'; import 'package:meta/meta.dart'; import 'package:pedantic/pedantic.dart'; -import '../../../ably_flutter.dart'; -import '../message.dart'; -import '../platform_object.dart'; -import 'presence.dart'; -import 'rest.dart'; +import '../../platform.dart'; /// Plugin based implementation of Rest channel class RestChannel extends PlatformObject implements RestChannelInterface { @@ -163,9 +163,9 @@ class RestChannel extends PlatformObject implements RestChannelInterface { /// A collection of rest channel objects /// /// https://docs.ably.io/client-lib-development-guide/features/#RSN1 -class RestPlatformChannels extends RestChannels { +class RestChannels extends RestChannelsInterface { /// instantiates with the ably [Rest] instance - RestPlatformChannels(Rest rest) : super(rest); + RestChannels(Rest rest) : super(rest); @override @protected diff --git a/lib/src/impl/rest/presence.dart b/lib/src/platform/src/rest/rest_presence.dart similarity index 87% rename from lib/src/impl/rest/presence.dart rename to lib/src/platform/src/rest/rest_presence.dart index 312b89e43..965848325 100644 --- a/lib/src/impl/rest/presence.dart +++ b/lib/src/platform/src/rest/rest_presence.dart @@ -1,10 +1,10 @@ -import '../../generated/platformconstants.dart'; -import '../../spec/spec.dart'; -import '../message.dart'; +import '../../../generated/platform_constants.dart'; +import '../../../message/message.dart'; +import '../../../rest/rest.dart'; +import '../../platform.dart'; import '../paginated_result.dart'; -import '../platform_object.dart'; -import 'channels.dart'; import 'rest.dart'; +import 'rest_channels.dart'; /// Plugin based implementation of [RestPresenceInterface] class RestPresence extends PlatformObject implements RestPresenceInterface { diff --git a/lib/src/impl/streams_channel.dart b/lib/src/platform/src/streams_channel.dart similarity index 98% rename from lib/src/impl/streams_channel.dart rename to lib/src/platform/src/streams_channel.dart index 3e1041e12..41096fefa 100644 --- a/lib/src/impl/streams_channel.dart +++ b/lib/src/platform/src/streams_channel.dart @@ -23,7 +23,7 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; -import '../spec/common.dart'; +import '../../error/error.dart'; /// Manages multiple event listeners which would otherwise require verbose code /// on platform side diff --git a/lib/src/push_notifications/push_notifications.dart b/lib/src/push_notifications/push_notifications.dart new file mode 100644 index 000000000..b3707d9ef --- /dev/null +++ b/lib/src/push_notifications/push_notifications.dart @@ -0,0 +1,14 @@ +export 'src/admin/push_admin.dart'; +export 'src/admin/push_device_registrations.dart'; +export 'src/device_details.dart'; +export 'src/device_platform.dart'; +export 'src/device_push_details.dart'; +export 'src/device_push_state.dart'; +export 'src/device_registration_params.dart'; +export 'src/form_factor.dart'; +export 'src/push.dart'; +export 'src/push_channel.dart'; +export 'src/push_channel_params.dart'; +export 'src/push_channel_subscription.dart'; +export 'src/push_channel_subscription_params.dart'; +export 'src/push_channel_subscriptions.dart'; \ No newline at end of file diff --git a/lib/src/push_notifications/src/admin/push_admin.dart b/lib/src/push_notifications/src/admin/push_admin.dart new file mode 100644 index 000000000..1b7e94d6a --- /dev/null +++ b/lib/src/push_notifications/src/admin/push_admin.dart @@ -0,0 +1,21 @@ +import '../push_channel_subscriptions.dart'; +import 'push_device_registrations.dart'; + +/// Class providing push notification administrative functionality +/// for registering devices and attaching to channels etc. +/// +/// https://docs.ably.com/client-lib-development-guide/features/#RSH1 +abstract class PushAdmin { + /// Manage device registrations. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#RSH1b + PushDeviceRegistrations? deviceRegistrations; + + /// Manage channel subscriptions for devices or clients. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#RSH1c + PushChannelSubscriptions? channelSubscriptions; + + /// https://docs.ably.com/client-lib-development-guide/features/#RSH1a + Future publish(Map recipient, Map payload); +} \ No newline at end of file diff --git a/lib/src/push_notifications/src/admin/push_device_registrations.dart b/lib/src/push_notifications/src/admin/push_device_registrations.dart new file mode 100644 index 000000000..6b9931f2b --- /dev/null +++ b/lib/src/push_notifications/src/admin/push_device_registrations.dart @@ -0,0 +1,40 @@ +import '../../../common/common.dart'; +import '../../push_notifications.dart'; + +/// Manage device registrations for push notifications +/// +/// https://ably.com/documentation/general/push/admin#device-registrations +abstract class PushDeviceRegistrations { + /// Get registered device by device ID. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#RSH1b1 + Future get({ + DeviceDetails? deviceDetails, + String? deviceId, + }); + + /// List registered devices filtered by optional params. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#RSH1b2 + Future> list( + DeviceRegistrationParams params, + ); + + /// Save and register device. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#RSH1b3 + Future save(DeviceDetails deviceDetails); + + /// Remove device. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#RSH1b4 + Future remove({ + DeviceDetails? deviceDetails, + String? deviceId, + }); + + /// Remove device matching where params. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#RSH1b5 + Future removeWhere(DeviceRegistrationParams params); +} \ No newline at end of file diff --git a/lib/src/push_notifications/src/device_details.dart b/lib/src/push_notifications/src/device_details.dart new file mode 100644 index 000000000..e111b6a0d --- /dev/null +++ b/lib/src/push_notifications/src/device_details.dart @@ -0,0 +1,44 @@ +import '../push_notifications.dart'; +import 'device_push_details.dart'; +import 'form_factor.dart'; + +/// Details of a registered device. +/// +/// https://docs.ably.com/client-lib-development-guide/features/#PCD1 +abstract class DeviceDetails { + /// The id of the device registration. + /// + /// Generated locally if not available + /// + /// https://docs.ably.com/client-lib-development-guide/features/#PCD2 + String? id; + + /// populated for device registrations associated with a clientId (optional) + /// + /// https://docs.ably.com/client-lib-development-guide/features/#PCD3 + String? clientId; + + /// The device platform. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#PCD6 + DevicePlatform? platform; + + /// the device form factor. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#PCD4 + FormFactor? formFactor; + + /// a map of string key/value pairs containing any other registered + /// metadata associated with the device registration + /// + /// https://docs.ably.com/client-lib-development-guide/features/#PCD5 + Map? metadata; + + /// Device token. Generated locally, if not available. + String? deviceSecret; + + /// Details of the push registration for this device. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#PCD7 + DevicePushDetails? push; +} \ No newline at end of file diff --git a/lib/src/push_notifications/src/device_platform.dart b/lib/src/push_notifications/src/device_platform.dart new file mode 100644 index 000000000..3bf72ca83 --- /dev/null +++ b/lib/src/push_notifications/src/device_platform.dart @@ -0,0 +1,12 @@ +/// To indicate the operating system -or- platform of the client using SDK +/// in [DeviceDetails] while registering +enum DevicePlatform { + /// indicates an android device + android, + + /// indicates an iOS device + ios, + + /// indicates a browser + browser, +} \ No newline at end of file diff --git a/lib/src/push_notifications/src/device_push_details.dart b/lib/src/push_notifications/src/device_push_details.dart new file mode 100644 index 000000000..97e1e93d8 --- /dev/null +++ b/lib/src/push_notifications/src/device_push_details.dart @@ -0,0 +1,24 @@ +import '../../error/src/error_info.dart'; + +import 'device_push_state.dart'; + +/// Details of the push registration for a given device +/// +/// https://docs.ably.com/client-lib-development-guide/features/#PCP1 +abstract class DevicePushDetails { + /// A map of string key/value pairs containing details of the push transport + /// and address. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#PCP3 + Map? recipient; + + /// The state of the push registration. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#PCP4 + DevicePushState? state; + + /// Any error information associated with the registration. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#PCP2 + ErrorInfo? errorReason; +} diff --git a/lib/src/push_notifications/src/device_push_state.dart b/lib/src/push_notifications/src/device_push_state.dart new file mode 100644 index 000000000..1e76ef6c2 --- /dev/null +++ b/lib/src/push_notifications/src/device_push_state.dart @@ -0,0 +1,12 @@ +/// To indicate Push State of a device in [DeviceDetails] via [DevicePushState] +/// while registering +enum DevicePushState { + /// indicates active push state of the device + active, + + /// indicates that push state is failing + failing, + + /// indicates the device push state failed + failed, +} \ No newline at end of file diff --git a/lib/src/push_notifications/src/device_registration_params.dart b/lib/src/push_notifications/src/device_registration_params.dart new file mode 100644 index 000000000..44f423dea --- /dev/null +++ b/lib/src/push_notifications/src/device_registration_params.dart @@ -0,0 +1,19 @@ +import 'device_push_state.dart'; + +/// Params to filter push device registrations. +/// +/// see: [PushDeviceRegistrations.list], [PushDeviceRegistrations.removeWhere] +/// https://docs.ably.com/client-lib-development-guide/features/#RSH1b2 +abstract class DeviceRegistrationParams { + /// filter by client id + String? clientId; + + /// filter by device id + String? deviceId; + + /// limit results for each page + int? limit; + + /// filter by device state + DevicePushState? state; +} \ No newline at end of file diff --git a/lib/src/push_notifications/src/form_factor.dart b/lib/src/push_notifications/src/form_factor.dart new file mode 100644 index 000000000..8e7831302 --- /dev/null +++ b/lib/src/push_notifications/src/form_factor.dart @@ -0,0 +1,28 @@ +/// To indicate the type of device in [DeviceDetails] while registering +/// +/// https://docs.ably.com/client-lib-development-guide/features/#PCD4 +enum FormFactor { + /// indicates the device is a mobile phone + phone, + + /// indicates the device is a tablet + tablet, + + /// indicates the device is a desktop + desktop, + + /// indicates the device is a television + tv, + + /// indicates the device is a smart watch + watch, + + /// indicates the device is an automobile + car, + + /// indicates the device is an embedded system / iOT device + embedded, + + /// indicates the device belong to categories other than mentioned above + other, +} diff --git a/lib/src/push_notifications/src/push.dart b/lib/src/push_notifications/src/push.dart new file mode 100644 index 000000000..f15e70c41 --- /dev/null +++ b/lib/src/push_notifications/src/push.dart @@ -0,0 +1,27 @@ +import '../push_notifications.dart'; +import 'admin/push_admin.dart'; + +/// Class providing push notification functionality +/// +/// https://docs.ably.com/client-lib-development-guide/features/#RSH1 +abstract class Push { + /// Admin features for push notifications like managing devices + /// and channel subscriptions. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#RSH1 + PushAdmin? admin; + + /// Activate this device for push notifications by registering + /// with the push transport such as GCM/APNS. + /// + /// returns DeviceDetails + /// https://docs.ably.com/client-lib-development-guide/features/#RSH2a + Future activate(); + + /// Deactivate this device for push notifications by removing + /// the registration with the push transport such as GCM/APNS. + /// + /// returns deviceId + /// https://docs.ably.com/client-lib-development-guide/features/#RSH2b + Future deactivate(); +} diff --git a/lib/src/spec/push/channels.dart b/lib/src/push_notifications/src/push_channel.dart similarity index 94% rename from lib/src/spec/push/channels.dart rename to lib/src/push_notifications/src/push_channel.dart index 5516f9024..06b010804 100644 --- a/lib/src/spec/push/channels.dart +++ b/lib/src/push_notifications/src/push_channel.dart @@ -1,4 +1,5 @@ -import '../common.dart'; +import '../../common/common.dart'; +import 'push_channel_subscription.dart'; /// Channel to receive push notifications on /// diff --git a/lib/src/push_notifications/src/push_channel_params.dart b/lib/src/push_notifications/src/push_channel_params.dart new file mode 100644 index 000000000..5d6e288aa --- /dev/null +++ b/lib/src/push_notifications/src/push_channel_params.dart @@ -0,0 +1,7 @@ +/// params to filter channels on a [PushChannelSubscriptions] +/// +/// https://docs.ably.com/client-lib-development-guide/features/#RSH1c2 +abstract class PushChannelsParams { + /// limit results for each page + int? limit; +} \ No newline at end of file diff --git a/lib/src/push_notifications/src/push_channel_subscription.dart b/lib/src/push_notifications/src/push_channel_subscription.dart new file mode 100644 index 000000000..62a8b6982 --- /dev/null +++ b/lib/src/push_notifications/src/push_channel_subscription.dart @@ -0,0 +1,20 @@ +/// Details of a push subscription to a channel. +/// +/// https://docs.ably.com/client-lib-development-guide/features/#PCS1 +abstract class PushChannelSubscription { + /// the channel name associated with this subscription + /// + /// https://docs.ably.com/client-lib-development-guide/features/#PCS4 + String? channel; + + /// populated for subscriptions made for a specific device registration + /// (optional) + /// + /// https://docs.ably.com/client-lib-development-guide/features/#PCS2 + String? deviceId; + + /// populated for subscriptions made for a specific clientId (optional) + /// + /// https://docs.ably.com/client-lib-development-guide/features/#PCS3 + String? clientId; +} \ No newline at end of file diff --git a/lib/src/push_notifications/src/push_channel_subscription_params.dart b/lib/src/push_notifications/src/push_channel_subscription_params.dart new file mode 100644 index 000000000..b34b94006 --- /dev/null +++ b/lib/src/push_notifications/src/push_channel_subscription_params.dart @@ -0,0 +1,17 @@ +/// Params to filter push channel subscriptions. +/// +/// See [PushChannelSubscriptions.list], [PushChannelSubscriptions.removeWhere] +/// https://docs.ably.com/client-lib-development-guide/features/#RSH1c1 +abstract class PushChannelSubscriptionParams { + /// filter by channel + String? channel; + + /// filter by clientId + String? clientId; + + /// filter by deviceId + String? deviceId; + + /// limit results for each page + int? limit; +} \ No newline at end of file diff --git a/lib/src/push_notifications/src/push_channel_subscriptions.dart b/lib/src/push_notifications/src/push_channel_subscriptions.dart new file mode 100644 index 000000000..a5e61b63e --- /dev/null +++ b/lib/src/push_notifications/src/push_channel_subscriptions.dart @@ -0,0 +1,34 @@ +import '../../common/common.dart'; +import '../push_notifications.dart'; + +/// Manage push notification channel subscriptions for devices or clients +abstract class PushChannelSubscriptions { + /// List channel subscriptions filtered by optional params. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#RSH1c1 + Future> list( + PushChannelSubscriptionParams params, + ); + + /// List channels with at least one subscribed device. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#RSH1c2 + Future> listChannels( + PushChannelsParams params, + ); + + /// Save push channel subscription for a device or client ID. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#RSH1c3 + Future save(PushChannelSubscription subscription); + + /// Remove a push channel subscription. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#RSH1c4 + Future remove(PushChannelSubscription subscription); + + /// Remove all matching push channel subscriptions. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#RSH1c5 + Future removeWhere(PushChannelSubscriptionParams params); +} diff --git a/lib/src/realtime/realtime.dart b/lib/src/realtime/realtime.dart new file mode 100644 index 000000000..8ca65d671 --- /dev/null +++ b/lib/src/realtime/realtime.dart @@ -0,0 +1,13 @@ +export 'src/channel_event.dart'; +export 'src/channel_mode.dart'; +export 'src/channel_state.dart'; +export 'src/channel_state_event.dart'; +export 'src/channels.dart'; +export 'src/connection.dart'; +export 'src/connection_event.dart'; +export 'src/connection_state.dart'; +export 'src/connection_state_change.dart'; +export 'src/presence.dart'; +export 'src/realtime.dart'; +export 'src/realtime_history_params.dart'; +export 'src/realtime_presence_params.dart'; \ No newline at end of file diff --git a/lib/src/realtime/src/channel_event.dart b/lib/src/realtime/src/channel_event.dart new file mode 100644 index 000000000..63d931184 --- /dev/null +++ b/lib/src/realtime/src/channel_event.dart @@ -0,0 +1,27 @@ +/// See Ably Realtime API documentation for more details. +enum ChannelEvent { + /// represents that a channel is initialized and no action was taken + /// i.e., even auto connect was not triggered - if enabled + initialized, + + /// channel is attaching + attaching, + + /// channel is attached + attached, + + /// channel is detaching + detaching, + + /// channel is detached + detached, + + /// channel is suspended + suspended, + + /// channel failed to connect + failed, + + /// specifies that a channel state is updated + update, +} \ No newline at end of file diff --git a/lib/src/realtime/src/channel_mode.dart b/lib/src/realtime/src/channel_mode.dart new file mode 100644 index 000000000..7725dec31 --- /dev/null +++ b/lib/src/realtime/src/channel_mode.dart @@ -0,0 +1,18 @@ +/// Set of flags that represent the capabilities of a channel for current client +/// +/// See: +/// https://docs.ably.com/client-lib-development-guide/features/#TB2d +/// https://docs.ably.com/client-lib-development-guide/features/#RTL4m +enum ChannelMode { + /// specifies that channel can check for presence + presence, + + /// specifies that channel can publish + publish, + + /// specifies that channel can subscribe to messages + subscribe, + + /// specifies that channel can subscribe to presence events + presenceSubscribe, +} \ No newline at end of file diff --git a/lib/src/realtime/src/channel_state.dart b/lib/src/realtime/src/channel_state.dart new file mode 100644 index 000000000..f31640be7 --- /dev/null +++ b/lib/src/realtime/src/channel_state.dart @@ -0,0 +1,25 @@ +/// See Ably Realtime API documentation for more details. +/// https://docs.ably.com/client-lib-development-guide/features/#channel-states-operations +enum ChannelState { + /// represents that a channel is initialized and no action was taken + /// i.e., even auto connect was not triggered - if enabled + initialized, + + /// channel is attaching + attaching, + + /// channel is attached + attached, + + /// channel is detaching + detaching, + + /// channel is detached + detached, + + /// channel is suspended + suspended, + + /// channel failed to connect + failed, +} \ No newline at end of file diff --git a/lib/src/realtime/src/channel_state_event.dart b/lib/src/realtime/src/channel_state_event.dart new file mode 100644 index 000000000..0f588c7a4 --- /dev/null +++ b/lib/src/realtime/src/channel_state_event.dart @@ -0,0 +1,46 @@ +import '../../error/src/error_info.dart'; + +import 'channel_event.dart'; +import 'channel_state.dart'; + +/// Whenever the channel state changes, a ChannelStateChange object +/// is emitted on the Channel object +/// +/// https://docs.ably.com/client-lib-development-guide/features/#TH1 +class ChannelStateChange { + /// the event that generated the channel state change + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TH5 + final ChannelEvent event; + + /// current state of the channel + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TH2 + final ChannelState current; + + /// previous state of the channel + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TH2 + final ChannelState previous; + + /// reason for failure, in case of a failed state + /// + /// If the channel state change includes error information, + /// then the reason attribute will contain an ErrorInfo + /// object describing the reason for the error + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TH3 + ErrorInfo? reason; + + /// https://docs.ably.com/client-lib-development-guide/features/#TH4 + final bool? resumed; + + /// initializes with [resumed] set to false + ChannelStateChange( + this.current, + this.previous, + this.event, { + this.reason, + this.resumed = false, + }); +} diff --git a/lib/src/spec/realtime/channels.dart b/lib/src/realtime/src/channels.dart similarity index 87% rename from lib/src/spec/realtime/channels.dart rename to lib/src/realtime/src/channels.dart index ea35a553f..c066020f6 100644 --- a/lib/src/spec/realtime/channels.dart +++ b/lib/src/realtime/src/channels.dart @@ -1,11 +1,16 @@ import 'dart:async'; -import '../../../ably_flutter.dart'; -import '../common.dart'; -import '../enums.dart'; -import '../message.dart'; -import '../push/channels.dart'; -import '../rest/channels.dart'; +import '../../common/common.dart'; +import '../../common/src/channels.dart'; +import '../../common/src/event_emitter.dart'; +import '../../error/src/error_info.dart'; +import '../../message/src/message.dart'; +import '../../push_notifications/src/push_channel.dart'; +import '../../rest/src/channel_options.dart'; +import '../realtime.dart'; +import 'channel_event.dart'; +import 'channel_state.dart'; +import 'channel_state_event.dart'; import 'presence.dart'; import 'realtime.dart'; @@ -115,11 +120,11 @@ abstract class RealtimeChannelInterface /// A collection of realtime channel objects /// /// https://docs.ably.com/client-lib-development-guide/features/#RTS1 -abstract class RealtimeChannels +abstract class RealtimeChannelsInterface extends Channels { /// instance of ably realtime client RealtimeInterface realtime; /// instantiates with the ably [RealtimeInterface] instance - RealtimeChannels(this.realtime); + RealtimeChannelsInterface(this.realtime); } diff --git a/lib/src/spec/connection.dart b/lib/src/realtime/src/connection.dart similarity index 95% rename from lib/src/spec/connection.dart rename to lib/src/realtime/src/connection.dart index 1d143bc90..534db2fdb 100644 --- a/lib/src/spec/connection.dart +++ b/lib/src/realtime/src/connection.dart @@ -1,5 +1,6 @@ -import 'common.dart'; -import 'enums.dart'; +import '../../common/common.dart'; +import '../../error/error.dart'; +import '../realtime.dart'; /// connects to Ably service using a [web-socket](https://www.ably.com/topic/websockets) connection /// diff --git a/lib/src/realtime/src/connection_event.dart b/lib/src/realtime/src/connection_event.dart new file mode 100644 index 000000000..d68706d94 --- /dev/null +++ b/lib/src/realtime/src/connection_event.dart @@ -0,0 +1,32 @@ +/// Connection event is same as [ConnectionState] except that it also handles +/// update operations on a connection +/// +/// See Ably Realtime API documentation for more details. +enum ConnectionEvent { + /// specifies that a connection is initialized + initialized, + + /// specifies that a connection to ably is being established + connecting, + + /// specifies that a connection to ably is established + connected, + + /// specifies that a connection to ably is disconnected + disconnected, + + /// specifies that a connection to ably is suspended + suspended, + + /// specifies that a connection to ably is closing + closing, + + /// specifies that a connection to ably is closed + closed, + + /// specifies that a connection to ably is failed + failed, + + /// specifies that a connection is updated + update, +} \ No newline at end of file diff --git a/lib/src/realtime/src/connection_state.dart b/lib/src/realtime/src/connection_state.dart new file mode 100644 index 000000000..694063de1 --- /dev/null +++ b/lib/src/realtime/src/connection_state.dart @@ -0,0 +1,27 @@ +/// See Ably Realtime API documentation for more details. +/// https://docs.ably.com/client-lib-development-guide/features/#connection-states-operations +enum ConnectionState { + /// specifies that a connection is initialized + initialized, + + /// specifies that a connection to ably is being established + connecting, + + /// specifies that a connection to ably is established + connected, + + /// specifies that a connection to ably is disconnected + disconnected, + + /// specifies that a connection to ably is suspended + suspended, + + /// specifies that a connection to ably is closing + closing, + + /// specifies that a connection to ably is closed + closed, + + /// specifies that a connection to ably is failed + failed, +} \ No newline at end of file diff --git a/lib/src/realtime/src/connection_state_change.dart b/lib/src/realtime/src/connection_state_change.dart new file mode 100644 index 000000000..da886553d --- /dev/null +++ b/lib/src/realtime/src/connection_state_change.dart @@ -0,0 +1,47 @@ +import '../../error/error.dart'; +import 'connection_event.dart'; +import 'connection_state.dart'; + +/// Whenever the connection state changes, +/// a ConnectionStateChange object is emitted on the [Connection] object +/// +/// https://docs.ably.com/client-lib-development-guide/features/#TA1 +class ConnectionStateChange { + /// https://docs.ably.com/client-lib-development-guide/features/#TA2 + final ConnectionEvent event; + + /// current state of the channel + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TA2 + final ConnectionState current; + + /// previous state of the channel + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TA2 + final ConnectionState previous; + + /// reason for failure, in case of a failed state + /// + /// If the channel state change includes error information, + /// then the reason attribute will contain an ErrorInfo + /// object describing the reason for the error + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TA3 + ErrorInfo? reason; + + /// when the client is not connected, a connection attempt will be made + /// automatically by the library after the number of milliseconds + /// specified by [retryIn] + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TA2 + int? retryIn; + + /// initializes without any defaults + ConnectionStateChange( + this.current, + this.previous, + this.event, { + this.reason, + this.retryIn, + }); +} diff --git a/lib/src/spec/realtime/presence.dart b/lib/src/realtime/src/presence.dart similarity index 90% rename from lib/src/spec/realtime/presence.dart rename to lib/src/realtime/src/presence.dart index d72eb089b..7e680d2fa 100644 --- a/lib/src/spec/realtime/presence.dart +++ b/lib/src/realtime/src/presence.dart @@ -1,8 +1,11 @@ import 'dart:async'; -import '../common.dart'; -import '../enums.dart'; -import '../message.dart'; +import 'package:ably_flutter/src/platform/platform.dart'; + +import '../../common/common.dart'; +import '../../message/src/presence_action.dart'; +import '../../message/src/presence_message.dart'; +import '../realtime.dart'; import 'channels.dart'; /// Presence object on a [RealtimeChannelInterface] helps to query @@ -28,7 +31,7 @@ abstract class RealtimePresenceInterface { /// filters the results if [params] are passed /// /// https://docs.ably.com/client-lib-development-guide/features/#RTP12 - Future> history([ + Future> history([ RealtimeHistoryParams? params, ]); diff --git a/lib/src/spec/realtime/realtime.dart b/lib/src/realtime/src/realtime.dart similarity index 78% rename from lib/src/spec/realtime/realtime.dart rename to lib/src/realtime/src/realtime.dart index 78dfaf720..3cecbe830 100644 --- a/lib/src/spec/realtime/realtime.dart +++ b/lib/src/realtime/src/realtime.dart @@ -1,12 +1,11 @@ -import '../connection.dart'; -import '../rest/ably_base.dart'; -import '../rest/options.dart'; -import 'channels.dart'; +import '../../authentication/authentication.dart'; +import '../../common/common.dart'; +import '../realtime.dart'; /// an abstract class for Ably's Realtime client /// /// https://docs.ably.com/client-lib-development-guide/features/#RTC1 -abstract class RealtimeInterface extends AblyBase { +abstract class RealtimeInterface extends AblyBase { /// https://docs.ably.com/client-lib-development-guide/features/#RTC1 RealtimeInterface({ ClientOptions? options, diff --git a/lib/src/realtime/src/realtime_history_params.dart b/lib/src/realtime/src/realtime_history_params.dart new file mode 100644 index 000000000..4da86c9ad --- /dev/null +++ b/lib/src/realtime/src/realtime_history_params.dart @@ -0,0 +1,28 @@ +// TODO stop extending RestHistoryParams like this +import 'package:ably_flutter/src/rest/rest.dart'; + +/// https://docs.ably.com/client-lib-development-guide/features/#RTL10 +class RealtimeHistoryParams extends RestHistoryParams { + /// Decides whether to retrieve messages from earlier session. + /// + /// if true, will only retrieve messages prior to the moment that the channel + /// was attached or emitted an UPDATE indicating loss of continuity. + bool? untilAttach; + + /// instantiates with [direction] set to "backwards", [limit] to 100 + /// [start] to epoch and end to current time + /// + /// Raises [AssertionError] if [direction] is not "backwards" or "forwards" + RealtimeHistoryParams({ + DateTime? start, + DateTime? end, + String direction = 'backwards', + int limit = 100, + this.untilAttach, + }) : super( + start: start, + end: end, + direction: direction, + limit: limit, + ); +} diff --git a/lib/src/realtime/src/realtime_presence_params.dart b/lib/src/realtime/src/realtime_presence_params.dart new file mode 100644 index 000000000..aadf93105 --- /dev/null +++ b/lib/src/realtime/src/realtime_presence_params.dart @@ -0,0 +1,27 @@ +/// Params used as a filter for querying presence on a channel +/// +/// https://docs.ably.com/client-lib-development-guide/features/#RTP11c +class RealtimePresenceParams { + /// When true, [RealtimePresence.get] will wait until SYNC is complete + /// before returning a list of members + /// + /// https://docs.ably.com/client-lib-development-guide/features/#RTP11c1 + final bool waitForSync; + + /// filters members by the provided clientId + /// + /// https://docs.ably.com/client-lib-development-guide/features/#RTP11c2 + final String? clientId; + + /// filters members by the provided connectionId + /// + /// https://docs.ably.com/client-lib-development-guide/features/#RTP11c3 + final String? connectionId; + + /// initializes with [waitForSync] set to true by default + RealtimePresenceParams({ + this.waitForSync = true, + this.clientId, + this.connectionId, + }); +} diff --git a/lib/src/rest/rest.dart b/lib/src/rest/rest.dart new file mode 100644 index 000000000..a215417d7 --- /dev/null +++ b/lib/src/rest/rest.dart @@ -0,0 +1,7 @@ +export 'src/channel_options.dart'; +export 'src/channels.dart'; +export 'src/options.dart'; +export 'src/presence.dart'; +export 'src/rest.dart'; +export 'src/rest_history_params.dart'; +export 'src/rest_presence_params.dart'; \ No newline at end of file diff --git a/lib/src/rest/src/channel_options.dart b/lib/src/rest/src/channel_options.dart new file mode 100644 index 000000000..28722e8d4 --- /dev/null +++ b/lib/src/rest/src/channel_options.dart @@ -0,0 +1,10 @@ +/// options provided when instantiating a channel +/// +/// https://docs.ably.com/client-lib-development-guide/features/#TB1 +class ChannelOptions { + /// https://docs.ably.com/client-lib-development-guide/features/#TB2b + final Object cipher; + + /// create channel options with a cipher + ChannelOptions(this.cipher); +} \ No newline at end of file diff --git a/lib/src/spec/rest/channels.dart b/lib/src/rest/src/channels.dart similarity index 75% rename from lib/src/spec/rest/channels.dart rename to lib/src/rest/src/channels.dart index e3f4c891b..c4065274f 100644 --- a/lib/src/spec/rest/channels.dart +++ b/lib/src/rest/src/channels.dart @@ -1,18 +1,9 @@ -import '../../../ably_flutter.dart'; -import '../common.dart'; -import '../message.dart'; -import 'presence.dart'; - -/// options provided when instantiating a channel -/// -/// https://docs.ably.com/client-lib-development-guide/features/#TB1 -class ChannelOptions { - /// https://docs.ably.com/client-lib-development-guide/features/#TB2b - final Object cipher; - - /// create channel options with a cipher - ChannelOptions(this.cipher); -} +import '../../common/common.dart'; +import '../../common/src/channels.dart'; +import '../../message/src/message.dart'; +import '../rest.dart'; +import 'channel_options.dart'; +import 'rest.dart'; /// A named channel through with rest client can interact with ably service. /// @@ -62,11 +53,11 @@ abstract class RestChannelInterface { /// A collection of rest channel objects /// /// https://docs.ably.com/client-lib-development-guide/features/#RSN1 -abstract class RestChannels +abstract class RestChannelsInterface extends Channels { /// instance of a rest client RestInterface rest; /// instantiates with the ably [RestInterface] instance - RestChannels(this.rest); + RestChannelsInterface(this.rest); } diff --git a/lib/src/rest/src/options.dart b/lib/src/rest/src/options.dart new file mode 100644 index 000000000..e69de29bb diff --git a/lib/src/spec/rest/presence.dart b/lib/src/rest/src/presence.dart similarity index 74% rename from lib/src/spec/rest/presence.dart rename to lib/src/rest/src/presence.dart index 6bd11dc3c..c39b20b3f 100644 --- a/lib/src/spec/rest/presence.dart +++ b/lib/src/rest/src/presence.dart @@ -1,8 +1,9 @@ -import '../common.dart'; -import '../message.dart'; -import 'channels.dart'; +import '../../common/common.dart'; +import '../../message/message.dart'; +import 'rest_history_params.dart'; +import 'rest_presence_params.dart'; -/// Presence object on a [RestChannelInterface] helps to query Presence members +/// Presence object on a [RestChannel] helps to query Presence members /// and presence history /// /// https://docs.ably.com/client-lib-development-guide/features/#RSP1 diff --git a/lib/src/spec/rest/rest.dart b/lib/src/rest/src/rest.dart similarity index 70% rename from lib/src/spec/rest/rest.dart rename to lib/src/rest/src/rest.dart index 9624d96f0..7672b6e78 100644 --- a/lib/src/spec/rest/rest.dart +++ b/lib/src/rest/src/rest.dart @@ -1,17 +1,15 @@ -import '../rest/ably_base.dart'; -import '../rest/options.dart'; -import 'ably_base.dart'; +import '../../authentication/authentication.dart'; +import '../../common/common.dart'; import 'channels.dart'; -import 'options.dart'; /// an abstract class for Ably's Rest client /// /// https://docs.ably.com/client-lib-development-guide/features/#RSC1 -abstract class RestInterface extends AblyBase { +abstract class RestInterface extends AblyBase { /// collection of [RestChannelInterface] objects /// /// https://docs.ably.com/client-lib-development-guide/features/#RSN1 - late C channels; + late T channels; /// https://docs.ably.com/client-lib-development-guide/features/#RSC1 RestInterface({ diff --git a/lib/src/rest/src/rest_history_params.dart b/lib/src/rest/src/rest_history_params.dart new file mode 100644 index 000000000..39fe1a313 --- /dev/null +++ b/lib/src/rest/src/rest_history_params.dart @@ -0,0 +1,49 @@ +/// Params for rest history +/// +/// https://docs.ably.com/client-lib-development-guide/features/#RSL2b +class RestHistoryParams { + /// [start] must be equal to or less than end and is unaffected + /// by the request direction + /// + /// RLS2b1 + final DateTime start; + + /// [end] must be equal to or greater than start and is unaffected + /// by the request direction + /// + /// RLS2b1 + final DateTime end; + + /// Sorting history backwards or forwards + /// + /// if omitted the direction defaults to the REST API default (backwards) + /// RLS2b2 + final String direction; + + /// Number of items returned in one page + /// + /// [limit] supports up to 1,000 items. + /// if omitted the direction defaults to the REST API default (100) + /// RLS2b3 + final int limit; + + /// instantiates with [direction] set to "backwards", [limit] to 100 + /// [start] to epoch and end to current time + /// + /// Raises [AssertionError] if [direction] is not "backwards" or "forwards" + RestHistoryParams({ + DateTime? start, + DateTime? end, + this.direction = 'backwards', + this.limit = 100, + }) : assert(direction == 'backwards' || direction == 'forwards'), + start = start ?? DateTime.fromMillisecondsSinceEpoch(0), + end = end ?? DateTime.now(); + + @override + String toString() => 'RestHistoryParams:' + ' start=$start' + ' end=$end' + ' direction=$direction' + ' limit=$limit'; +} diff --git a/lib/src/rest/src/rest_presence_params.dart b/lib/src/rest/src/rest_presence_params.dart new file mode 100644 index 000000000..56c625703 --- /dev/null +++ b/lib/src/rest/src/rest_presence_params.dart @@ -0,0 +1,26 @@ +/// Params used as a filter for querying presence on a channel +/// +/// https://docs.ably.com/client-lib-development-guide/features/#RSP3a +class RestPresenceParams { + /// number of records to fetch per page + /// + /// https://docs.ably.com/client-lib-development-guide/features/#RSP3a1 + int limit; + + /// filters members by the provided clientId + /// + /// https://docs.ably.com/client-lib-development-guide/features/#RSP3a2 + String? clientId; + + /// filters members by the provided connectionId + /// + /// https://docs.ably.com/client-lib-development-guide/features/#RSP3a3 + String? connectionId; + + /// initializes with default [limit] set to 100 + RestPresenceParams({ + this.limit = 100, + this.clientId, + this.connectionId, + }); +} diff --git a/lib/src/spec/common.dart b/lib/src/spec/common.dart deleted file mode 100644 index 635b1f68b..000000000 --- a/lib/src/spec/common.dart +++ /dev/null @@ -1,975 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/services.dart'; -import 'package:meta/meta.dart'; - -import '../../ably_flutter.dart'; -import '../impl/realtime/connection.dart'; -import '../impl/realtime/presence.dart'; -import 'auth.dart'; -import 'enums.dart'; -import 'spec.dart'; - -/// params to configure encryption for a channel -/// -/// https://docs.ably.com/client-lib-development-guide/features/#TZ1 -abstract class CipherParams { - /// Specifies the algorithm to use for encryption - /// - /// Default is AES. Currently only AES is supported. - /// https://docs.ably.com/client-lib-development-guide/features/#TZ2a - String? algorithm; - - /// private key used to encrypt and decrypt payloads - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TZ2d - dynamic key; - - /// the length in bits of the key - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TZ2b - int? keyLength; - - /// Specify cipher mode - /// - /// Default is CBC. Currently only CBC is supported - /// https://docs.ably.com/client-lib-development-guide/features/#TZ2c - String? mode; -} - -/// An [AblyException] encapsulates [ErrorInfo] which carries details -/// about information related to Ably-specific error [code], -/// generic [statusCode], error [message], -/// link to error related documentation as [href], -/// [requestId] and [cause] of this exception -/// -/// https://docs.ably.com/client-lib-development-guide/features/#TI1 -class ErrorInfo { - /// ably specific error code - final int? code; - - /// link to error related documentation as - final String? href; - - /// error message - final String? message; - - /// cause for the error - final ErrorInfo? cause; - - /// generic status code - final int? statusCode; - - /// request id which triggered this exception - final String? requestId; - - /// instantiates a [ErrorInfo] with provided values - ErrorInfo({ - this.code, - this.href, - this.message, - this.cause, - this.statusCode, - this.requestId, - }); - - @override - String toString() => 'ErrorInfo' - ' message=$message' - ' code=$code' - ' statusCode=$statusCode' - ' href=$href'; -} - -/// MessageCount contains aggregate counts for messages and data transferred -/// -/// https://docs.ably.com/client-lib-development-guide/features/#TS5 -abstract class StatsMessageCount { - /// Count of all messages. - int? count; - - /// Total data transferred for all messages in bytes. - int? data; -} - -/// MessageTypes contains a breakdown of summary stats data -/// for different (message vs presence) message types -/// -/// https://docs.ably.com/client-lib-development-guide/features/#TS6 -abstract class StatsMessageTypes { - /// All messages count (includes both presence & messages). - StatsMessageCount? all; - - /// Count of channel messages. - StatsMessageCount? messages; - - /// Count of presence messages. - StatsMessageCount? presence; -} - -/// RequestCount contains aggregate counts for requests made -/// -/// https://docs.ably.com/client-lib-development-guide/features/#TS8 -abstract class StatsRequestCount { - /// Requests failed. - int? failed; - - /// Requests refused typically as a result of permissions - /// or a limit being exceeded. - int? refused; - - /// Requests succeeded. - int? succeeded; -} - -/// ResourceCount contains aggregate data for usage of a resource -/// in a specific scope -/// -/// https://docs.ably.com/client-lib-development-guide/features/#TS9 -abstract class StatsResourceCount { - /// Average resources of this type used for this period. - int? mean; - - /// Minimum total resources of this type used for this period. - int? min; - - /// Total resources of this type opened. - int? opened; - - /// Peak resources of this type used for this period. - int? peak; - - /// Resource requests refused within this period. - int? refused; -} - -/// ConnectionTypes contains a breakdown of summary stats data -/// for different (TLS vs non-TLS) connection types -/// -/// https://docs.ably.com/client-lib-development-guide/features/#TS4 -abstract class StatsConnectionTypes { - /// All connection count (includes both TLS & non-TLS connections). - StatsResourceCount? all; - - /// Non-TLS connection count (unencrypted). - StatsResourceCount? plain; - - /// TLS connection count. - StatsResourceCount? tls; -} - -/// MessageTraffic contains a breakdown of summary stats data -/// for traffic over various transport types -/// -/// https://docs.ably.com/client-lib-development-guide/features/#TS7 -abstract class StatsMessageTraffic { - /// All messages count (includes realtime, rest and webhook messages). - StatsMessageTypes? all; - - /// Count of messages transferred over a realtime transport - /// such as WebSockets. - StatsMessageTypes? realtime; - - /// Count of messages transferred using REST. - StatsMessageTypes? rest; - - /// Count of messages delivered using WebHooks. - StatsMessageTypes? webhook; -} - -/// A class providing parameters of a token request. -/// -/// Parameters for a token request -/// -/// [Auth.authorize], [Auth.requestToken] and [Auth.createTokenRequest] -/// accept an instance of TokenParams as a parameter -/// -/// https://docs.ably.com/client-lib-development-guide/features/#TK1 -class TokenParams { - /// Capability of the token. - /// - /// If the token request is successful, the capability of the - /// returned token will be the intersection of this [capability] - /// with the capability of the issuing key. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TK2b - String? capability; - - /// A clientId to associate with this token. - /// - /// The generated token may be used to authenticate as this clientId. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TK2c - String? clientId; - - /// An opaque nonce string of at least 16 characters to ensure uniqueness. - /// - /// Timestamps, in conjunction with the nonce, - /// are used to prevent requests from being replayed - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TK2d - String? nonce; - - /// The timestamp (in millis since the epoch) of this request. - /// - /// Timestamps, in conjunction with the nonce, are used to prevent - /// token requests from being replayed. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TK2d - DateTime? timestamp; - - /// Requested time to live for the token. - /// - /// If the token request is successful, the TTL of the returned - /// token will be less than or equal to this value depending on - /// application settings and the attributes of the issuing key. - /// - /// 0 means Ably will set it to the default value - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TK2a - int? ttl; - - /// instantiates a [TokenParams] with provided values - TokenParams({ - this.capability, - this.clientId, - this.nonce, - this.timestamp, - this.ttl, - }); -} - -/// Response to a `requestToken` request -/// -/// https://docs.ably.com/client-lib-development-guide/features/#TD1 -class TokenDetails { - /// https://docs.ably.com/client-lib-development-guide/features/#TD2 - String? token; - - /// Token expiry time in milliseconds - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TD3 - int? expires; - - /// the time the token was issued in milliseconds - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TD4 - int? issued; - - /// stringified capabilities JSON - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TD5 - String? capability; - - /// Client ID assigned to the token. - /// - /// If [clientId] is not set (i.e. null), then the token is prohibited - /// from assuming a clientId in any operations, however if clientId - /// is a wildcard string '*', then the token is permitted to assume - /// any clientId. Any other string value for clientId implies that the - /// clientId is both enforced and assumed for all operations for this token - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TD6 - String? clientId; - - /// instantiates a [TokenDetails] with provided values - TokenDetails( - this.token, { - this.expires, - this.issued, - this.capability, - this.clientId, - }); - - /// Creates an instance from the map - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TD7 - TokenDetails.fromMap(Map map) { - token = map['token'] as String?; - expires = map['expires'] as int?; - issued = map['issued'] as int?; - capability = map['capability'] as String?; - clientId = map['clientId'] as String?; - } -} - -/// spec: https://docs.ably.com/client-lib-development-guide/features/#TE1 -class TokenRequest { - /// [keyName] is the first part of Ably API Key. - /// - /// provided keyName will be used to authorize requests made to Ably. - /// spec: https://docs.ably.com/client-lib-development-guide/features/#TE2 - /// - /// More details about Ably API Key: - /// https://docs.ably.com/client-lib-development-guide/features/#RSA11 - String? keyName; - - /// An opaque nonce string of at least 16 characters to ensure - /// uniqueness of this request. Any subsequent request using the - /// same nonce will be rejected. - /// - /// spec: - /// https://docs.ably.com/client-lib-development-guide/features/#TE2 - /// https://docs.ably.com/client-lib-development-guide/features/#TE5 - String? nonce; - - /// The "Message Authentication Code" for this request. - /// - /// See the Ably Authentication documentation for more details. - /// spec: https://docs.ably.com/client-lib-development-guide/features/#TE2 - String? mac; - - /// stringified capabilities JSON - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TE3 - String? capability; - - /// Client ID assigned to the tokenRequest. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TE2 - String? clientId; - - /// timestamp long – The timestamp (in milliseconds since the epoch) - /// of this request. Timestamps, in conjunction with the nonce, - /// are used to prevent requests from being replayed - /// - /// spec: https://docs.ably.com/client-lib-development-guide/features/#TE5 - DateTime? timestamp; - - /// ttl attribute represents time to live (expiry) - /// of this token in milliseconds - /// - /// spec: https://docs.ably.com/client-lib-development-guide/features/#TE4 - int? ttl; - - /// instantiates a [TokenRequest] with provided values - TokenRequest({ - this.keyName, - this.nonce, - this.clientId, - this.mac, - this.capability, - this.timestamp, - this.ttl, - }); - - /// spec: https://docs.ably.com/client-lib-development-guide/features/#TE7 - TokenRequest.fromMap(Map map) { - keyName = map['keyName'] as String?; - nonce = map['nonce'] as String?; - mac = map['mac'] as String?; - capability = map['capability'] as String?; - clientId = map['clientId'] as String?; - timestamp = DateTime.fromMillisecondsSinceEpoch(map['timestamp'] as int); - ttl = map['ttl'] as int?; - } -} - -/// Params for rest history -/// -/// https://docs.ably.com/client-lib-development-guide/features/#RSL2b -class RestHistoryParams { - /// [start] must be equal to or less than end and is unaffected - /// by the request direction - /// - /// RLS2b1 - final DateTime start; - - /// [end] must be equal to or greater than start and is unaffected - /// by the request direction - /// - /// RLS2b1 - final DateTime end; - - /// Sorting history backwards or forwards - /// - /// if omitted the direction defaults to the REST API default (backwards) - /// RLS2b2 - final String direction; - - /// Number of items returned in one page - /// - /// [limit] supports up to 1,000 items. - /// if omitted the direction defaults to the REST API default (100) - /// RLS2b3 - final int limit; - - /// instantiates with [direction] set to "backwards", [limit] to 100 - /// [start] to epoch and end to current time - /// - /// Raises [AssertionError] if [direction] is not "backwards" or "forwards" - RestHistoryParams({ - DateTime? start, - DateTime? end, - this.direction = 'backwards', - this.limit = 100, - }) : assert(direction == 'backwards' || direction == 'forwards'), - start = start ?? DateTime.fromMillisecondsSinceEpoch(0), - end = end ?? DateTime.now(); - - @override - String toString() => 'RestHistoryParams:' - ' start=$start' - ' end=$end' - ' direction=$direction' - ' limit=$limit'; -} - -/// https://docs.ably.com/client-lib-development-guide/features/#RTL10 -class RealtimeHistoryParams extends RestHistoryParams { - /// Decides whether to retrieve messages from earlier session. - /// - /// if true, will only retrieve messages prior to the moment that the channel - /// was attached or emitted an UPDATE indicating loss of continuity. - bool? untilAttach; - - /// instantiates with [direction] set to "backwards", [limit] to 100 - /// [start] to epoch and end to current time - /// - /// Raises [AssertionError] if [direction] is not "backwards" or "forwards" - RealtimeHistoryParams({ - DateTime? start, - DateTime? end, - String direction = 'backwards', - int limit = 100, - this.untilAttach, - }) : super( - start: start, - end: end, - direction: direction, - limit: limit, - ); -} - -/// Params used as a filter for querying presence on a channel -/// -/// https://docs.ably.com/client-lib-development-guide/features/#RSP3a -class RestPresenceParams { - /// number of records to fetch per page - /// - /// https://docs.ably.com/client-lib-development-guide/features/#RSP3a1 - int limit; - - /// filters members by the provided clientId - /// - /// https://docs.ably.com/client-lib-development-guide/features/#RSP3a2 - String? clientId; - - /// filters members by the provided connectionId - /// - /// https://docs.ably.com/client-lib-development-guide/features/#RSP3a3 - String? connectionId; - - /// initializes with default [limit] set to 100 - RestPresenceParams({ - this.limit = 100, - this.clientId, - this.connectionId, - }); -} - -/// Params used as a filter for querying presence on a channel -/// -/// https://docs.ably.com/client-lib-development-guide/features/#RTP11c -class RealtimePresenceParams { - /// When true, [RealtimePresence.get] will wait until SYNC is complete - /// before returning a list of members - /// - /// https://docs.ably.com/client-lib-development-guide/features/#RTP11c1 - final bool waitForSync; - - /// filters members by the provided clientId - /// - /// https://docs.ably.com/client-lib-development-guide/features/#RTP11c2 - final String? clientId; - - /// filters members by the provided connectionId - /// - /// https://docs.ably.com/client-lib-development-guide/features/#RTP11c3 - final String? connectionId; - - /// initializes with [waitForSync] set to true by default - RealtimePresenceParams({ - this.waitForSync = true, - this.clientId, - this.connectionId, - }); -} - -/// An exception generated by the native client library called by this plugin -class AblyException implements Exception { - /// platform error code - /// - /// Mostly used for storing [PlatformException.code] - final String? code; - - /// platform error message - /// - /// Mostly used for storing [PlatformException.message] - final String? message; - - /// error message from ably native sdk - final ErrorInfo? errorInfo; - - /// initializes with no defaults - AblyException([ - this.code, - this.message, - this.errorInfo, - ]); - - /// create AblyException from [PlatformException] - AblyException.fromPlatformException(PlatformException pe) - : code = pe.code, - message = pe.message, - errorInfo = pe.details as ErrorInfo?; - - @override - String toString() { - if (message == null) { - return 'AblyException (${(code == null) ? "" : '$code '})'; - } - return 'AblyException: $message (${(code == null) ? "" : '$code '})'; - } -} - -/// Whenever the channel state changes, a ChannelStateChange object -/// is emitted on the Channel object -/// -/// https://docs.ably.com/client-lib-development-guide/features/#TH1 -class ChannelStateChange { - /// the event that generated the channel state change - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TH5 - final ChannelEvent event; - - /// current state of the channel - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TH2 - final ChannelState current; - - /// previous state of the channel - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TH2 - final ChannelState previous; - - /// reason for failure, in case of a failed state - /// - /// If the channel state change includes error information, - /// then the reason attribute will contain an ErrorInfo - /// object describing the reason for the error - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TH3 - ErrorInfo? reason; - - /// https://docs.ably.com/client-lib-development-guide/features/#TH4 - final bool? resumed; - - /// initializes with [resumed] set to false - ChannelStateChange( - this.current, - this.previous, - this.event, { - this.reason, - this.resumed = false, - }); -} - -/// Whenever the connection state changes, -/// a ConnectionStateChange object is emitted on the [Connection] object -/// -/// https://docs.ably.com/client-lib-development-guide/features/#TA1 -class ConnectionStateChange { - /// https://docs.ably.com/client-lib-development-guide/features/#TA2 - final ConnectionEvent event; - - /// current state of the channel - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TA2 - final ConnectionState current; - - /// previous state of the channel - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TA2 - final ConnectionState previous; - - /// reason for failure, in case of a failed state - /// - /// If the channel state change includes error information, - /// then the reason attribute will contain an ErrorInfo - /// object describing the reason for the error - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TA3 - ErrorInfo? reason; - - /// when the client is not connected, a connection attempt will be made - /// automatically by the library after the number of milliseconds - /// specified by [retryIn] - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TA2 - int? retryIn; - - /// initializes without any defaults - ConnectionStateChange( - this.current, - this.previous, - this.event, { - this.reason, - this.retryIn, - }); -} - -/// Details of the push registration for a given device -/// -/// https://docs.ably.com/client-lib-development-guide/features/#PCP1 -abstract class DevicePushDetails { - /// A map of string key/value pairs containing details of the push transport - /// and address. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#PCP3 - Map? recipient; - - /// The state of the push registration. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#PCP4 - DevicePushState? state; - - /// Any error information associated with the registration. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#PCP2 - ErrorInfo? errorReason; -} - -/// Details of a registered device. -/// -/// https://docs.ably.com/client-lib-development-guide/features/#PCD1 -abstract class DeviceDetails { - /// The id of the device registration. - /// - /// Generated locally if not available - /// - /// https://docs.ably.com/client-lib-development-guide/features/#PCD2 - String? id; - - /// populated for device registrations associated with a clientId (optional) - /// - /// https://docs.ably.com/client-lib-development-guide/features/#PCD3 - String? clientId; - - /// The device platform. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#PCD6 - DevicePlatform? platform; - - /// the device form factor. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#PCD4 - FormFactor? formFactor; - - /// a map of string key/value pairs containing any other registered - /// metadata associated with the device registration - /// - /// https://docs.ably.com/client-lib-development-guide/features/#PCD5 - Map? metadata; - - /// Device token. Generated locally, if not available. - String? deviceSecret; - - /// Details of the push registration for this device. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#PCD7 - DevicePushDetails? push; -} - -/// Details of a push subscription to a channel. -/// -/// https://docs.ably.com/client-lib-development-guide/features/#PCS1 -abstract class PushChannelSubscription { - /// the channel name associated with this subscription - /// - /// https://docs.ably.com/client-lib-development-guide/features/#PCS4 - String? channel; - - /// populated for subscriptions made for a specific device registration - /// (optional) - /// - /// https://docs.ably.com/client-lib-development-guide/features/#PCS2 - String? deviceId; - - /// populated for subscriptions made for a specific clientId (optional) - /// - /// https://docs.ably.com/client-lib-development-guide/features/#PCS3 - String? clientId; -} - -/// Params to filter push device registrations. -/// -/// see: [PushDeviceRegistrations.list], [PushDeviceRegistrations.removeWhere] -/// https://docs.ably.com/client-lib-development-guide/features/#RSH1b2 -abstract class DeviceRegistrationParams { - /// filter by client id - String? clientId; - - /// filter by device id - String? deviceId; - - /// limit results for each page - int? limit; - - /// filter by device state - DevicePushState? state; -} - -/// Params to filter push channel subscriptions. -/// -/// See [PushChannelSubscriptions.list], [PushChannelSubscriptions.removeWhere] -/// https://docs.ably.com/client-lib-development-guide/features/#RSH1c1 -abstract class PushChannelSubscriptionParams { - /// filter by channel - String? channel; - - /// filter by clientId - String? clientId; - - /// filter by deviceId - String? deviceId; - - /// limit results for each page - int? limit; -} - -/// params to filter channels on a [PushChannelSubscriptions] -/// -/// https://docs.ably.com/client-lib-development-guide/features/#RSH1c2 -abstract class PushChannelsParams { - /// limit results for each page - int? limit; -} - -/// Interface implemented by event listeners, returned by event emitters. -abstract class EventListener { - /// Register for all events (no parameter), or a specific event. - Stream on([E? event]); - - /// Register for a single occurrence of any event (no parameter), - /// or a specific event. - Future once([E? event]); - - /// Remove registrations for this listener, irrespective of type. - Future off(); -} - -/// Interface implemented by Ably classes that can emit events, -/// offering the capability to create listeners for those events. -/// [E] is type of event to listen for -/// [G] is the instance which will be passed back in streams. -/// -/// -/// There is no `off` API as in other Ably client libraries as on returns a -/// [Stream] which can be subscribed for, and that subscription can be cancelled -/// using [StreamSubscription.cancel] API -abstract class EventEmitter { - /// Create a listener, with which registrations may be made. - Stream on([E? event]); -} - -/// PaginatedResult [TG1](https://docs.ably.com/client-lib-development-guide/features/#TG1) -/// -/// A type that represents page results from a paginated query. -/// The response is accompanied by metadata that indicates the -/// relative queries available. -abstract class PaginatedResultInterface { - /// items contain page of results - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TG3 - List get items; - - /// returns a new PaginatedResult loaded with the next page of results. - /// - /// If there are no further pages, then null is returned. - /// https://docs.ably.com/client-lib-development-guide/features/#TG4 - Future> next(); - - /// returns a new PaginatedResult with the first page of results - /// - /// If there are no further pages, then null is returned. - /// https://docs.ably.com/client-lib-development-guide/features/#TG5 - Future> first(); - - /// returns true if there are further pages - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TG6 - bool hasNext(); - - /// returns true if this page is the last page - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TG7 - bool isLast(); -} - -/// The response from an HTTP request containing an empty or -/// JSON-encodable object response -/// -/// [T] can be a [Map] or [List] -/// -/// https://docs.ably.com/client-lib-development-guide/features/#HP1 -abstract class HttpPaginatedResponse extends PaginatedResultInterface { - /// HTTP status code for the response - /// - /// https://docs.ably.com/client-lib-development-guide/features/#HP4 - int? statusCode; - - /// indicates whether the request is successful - /// - /// true when 200 <= [statusCode] < 300 - /// - /// https://docs.ably.com/client-lib-development-guide/features/#HP5 - bool? success; - - /// Value from X-Ably-Errorcode HTTP header, if available in response - /// - /// https://docs.ably.com/client-lib-development-guide/features/#HP6 - int? errorCode; - - /// Value from X-Ably-Errormessage HTTP header, if available in response - /// - /// https://docs.ably.com/client-lib-development-guide/features/#HP7 - String? errorMessage; - - /// Array of key value pairs of each response header - /// - /// https://docs.ably.com/client-lib-development-guide/features/#HP8 - List>? headers; - - /// returns a new HttpPaginatedResponse loaded with the next page of results. - /// - /// If there are no further pages, then null is returned. - /// https://docs.ably.com/client-lib-development-guide/features/#HP2 - @override - Future> next(); - - /// returns a new HttpPaginatedResponse with the first page of results - /// - /// If there are no further pages, then null is returned. - /// https://docs.ably.com/client-lib-development-guide/features/#HP2 - @override - Future> first(); -} - -/// A class representing an individual statistic for a specified [intervalId] -/// -/// https://docs.ably.com/client-lib-development-guide/features/#TS1 -class Stats { - /// Aggregates inbound and outbound messages. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TS12e - StatsMessageTypes? all; - - /// Breakdown of API requests received via the REST API. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TS12e - StatsRequestCount? apiRequests; - - /// Breakdown of channels stats. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TS12e - StatsResourceCount? channels; - - /// Breakdown of connection stats data for different (TLS vs non-TLS) - /// connection types. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TS12i - StatsConnectionTypes? connections; - - /// All inbound messages i.e. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TS12f - StatsMessageTraffic? inbound; - - /// The interval that this statistic applies to, - /// see GRANULARITY and INTERVAL_FORMAT_STRING. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TS12a - String? intervalId; - - /// All outbound messages i.e. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TS12g - StatsMessageTraffic? outbound; - - /// Messages persisted for later retrieval via the history API. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TS12h - StatsMessageTypes? persisted; - - /// Breakdown of Token requests received via the REST API. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TS12l - StatsRequestCount? tokenRequests; -} - -/// Iterator class for [Channels.iterator] -class _ChannelIterator implements Iterator { - _ChannelIterator(this._channels); - - final List _channels; - - int _currentIndex = 0; - - T? _currentChannel; - - @override - T get current { - if (_currentChannel == null) { - throw StateError('Not iterating'); - } - return _currentChannel!; - } - - @override - bool moveNext() { - if (_currentIndex == _channels.length) { - return false; - } - _currentChannel = _channels[_currentIndex++]; - return true; - } -} - -/// A collection of Channel objects accessible -/// through [Rest.channels] or [Realtime.channels] -abstract class Channels extends Iterable { - /// stores channel name vs instance of [ChannelType] - final _channels = {}; - - /// creates a channel with provided name and options - /// - /// This is a private method to be overridden by implementation classes - @protected - ChannelType createChannel(String name); - - /// creates a channel with [name]. - /// - /// Doesn't create a channel instance on platform side yet. - ChannelType get(String name) { - if (_channels[name] == null) { - _channels[name] = createChannel(name); - } - return _channels[name]!; - } - - /// returns true if a channel exists [name] - bool exists(String name) => _channels[name] != null; - - /// Same as [get]. - ChannelType operator [](String name) => get(name); - - @override - Iterator get iterator => - _ChannelIterator(_channels.values.toList()); - - /// releases channel with [name] - void release(String name) { - _channels.remove(name); - } -} diff --git a/lib/src/spec/constants.dart b/lib/src/spec/constants.dart deleted file mode 100644 index 39a9bc770..000000000 --- a/lib/src/spec/constants.dart +++ /dev/null @@ -1,54 +0,0 @@ -import '../../ably_flutter.dart'; - -/// Log levels - control verbosity of log messages -/// -/// Can be used for [ClientOptions.logLevel] -/// -/// https://docs.ably.com/client-lib-development-guide/features/#TO3b -/// -/// TODO(tiholic) convert [LogLevel] to enum and update encoder to pass -/// right numeric values to platform methods -class LogLevel { - /// No logging - static const int none = 99; - - /// Verbose logs - static const int verbose = 2; - - /// debug logs - static const int debug = 3; - - /// info logs - static const int info = 4; - - /// warning logs - static const int warn = 5; - - /// error logs - static const int error = 6; -} - -/// Static error codes used inside the SDK -class ErrorCodes { - /// error code sent from platform in case if response from - /// authCallback is not of valid type. - /// When this is encountered, flutter side will re-try the - /// method call after responding to authCallback method channel - /// call triggered from platform side - static const int authCallbackFailure = 80019; -} - -/// Static timeouts used inside the SDK -class Timeouts { - /// max time allowed for retrying an operation for auth failure - /// in case of usage of authCallback - static const retryOperationOnAuthFailure = Duration(seconds: 30); - - /// max time dart side will wait for platform side to respond with a - /// platform handle - static const acquireHandleTimeout = Duration(seconds: 5); - - /// max time dart side will wait for platform side to respond after - /// initializing an Ably instance on platform side - static const initializeTimeout = Duration(seconds: 5); -} diff --git a/lib/src/spec/enums.dart b/lib/src/spec/enums.dart deleted file mode 100644 index f2177a7b2..000000000 --- a/lib/src/spec/enums.dart +++ /dev/null @@ -1,246 +0,0 @@ -import '../../ably_flutter.dart'; - -/// Set of flags that represent the capabilities of a channel for current client -/// -/// See: -/// https://docs.ably.com/client-lib-development-guide/features/#TB2d -/// https://docs.ably.com/client-lib-development-guide/features/#RTL4m -enum ChannelMode { - /// specifies that channel can check for presence - presence, - - /// specifies that channel can publish - publish, - - /// specifies that channel can subscribe to messages - subscribe, - - /// specifies that channel can subscribe to presence events - presenceSubscribe, -} - -/// Connection state -/// -/// See Ably Realtime API documentation for more details. -/// https://docs.ably.com/client-lib-development-guide/features/#connection-states-operations -enum ConnectionState { - /// specifies that a connection is initialized - initialized, - - /// specifies that a connection to ably is being established - connecting, - - /// specifies that a connection to ably is established - connected, - - /// specifies that a connection to ably is disconnected - disconnected, - - /// specifies that a connection to ably is suspended - suspended, - - /// specifies that a connection to ably is closing - closing, - - /// specifies that a connection to ably is closed - closed, - - /// specifies that a connection to ably is failed - failed, -} - -/// Connection event is same as [ConnectionState] except that it also handles -/// update operations on a connection -/// -/// See Ably Realtime API documentation for more details. -enum ConnectionEvent { - /// specifies that a connection is initialized - initialized, - - /// specifies that a connection to ably is being established - connecting, - - /// specifies that a connection to ably is established - connected, - - /// specifies that a connection to ably is disconnected - disconnected, - - /// specifies that a connection to ably is suspended - suspended, - - /// specifies that a connection to ably is closing - closing, - - /// specifies that a connection to ably is closed - closed, - - /// specifies that a connection to ably is failed - failed, - - /// specifies that a connection is updated - update, -} - -/// Channel states -/// -/// See Ably Realtime API documentation for more details. -/// https://docs.ably.com/client-lib-development-guide/features/#channel-states-operations -enum ChannelState { - /// represents that a channel is initialized and no action was taken - /// i.e., even auto connect was not triggered - if enabled - initialized, - - /// channel is attaching - attaching, - - /// channel is attached - attached, - - /// channel is detaching - detaching, - - /// channel is detached - detached, - - /// channel is suspended - suspended, - - /// channel failed to connect - failed, -} - -/// Channel events -/// -/// See Ably Realtime API documentation for more details. -enum ChannelEvent { - /// represents that a channel is initialized and no action was taken - /// i.e., even auto connect was not triggered - if enabled - initialized, - - /// channel is attaching - attaching, - - /// channel is attached - attached, - - /// channel is detaching - detaching, - - /// channel is detached - detached, - - /// channel is suspended - suspended, - - /// channel failed to connect - failed, - - /// specifies that a channel state is updated - update, -} - -/// Status on a presence message -/// -/// https://docs.ably.com/client-lib-development-guide/features/#TP2 -enum PresenceAction { - /// indicates that a client is absent for incoming [PresenceMessage] - absent, - - /// indicates that a client is present for incoming [PresenceMessage] - present, - - /// indicates that a client wants to enter a channel presence via - /// outgoing [PresenceMessage] - enter, - - /// indicates that a client wants to leave a channel presence via - /// outgoing [PresenceMessage] - leave, - - /// indicates that presence status of a client in presence member map - /// needs to be updated - update, -} - -/// https://docs.ably.com/client-lib-development-guide/features/#TS12c -enum StatsIntervalGranularity { - /// indicates units in minutes - minute, - - /// indicates units in hours - hour, - - /// indicates units in days - day, - - /// indicates units in months - month, -} - -/// Java: io.ably.lib.http.HttpAuth.Type -enum HttpAuthType { - /// indicates basic authentication - basic, - - /// digest authentication - digest, - - /// Token auth - xAblyToken, -} - -/// To indicate Push State of a device in [DeviceDetails] via [DevicePushState] -/// while registering -enum DevicePushState { - /// indicates active push state of the device - active, - - /// indicates that push state is failing - failing, - - /// indicates the device push state failed - failed, -} - -/// To indicate the operating system -or- platform of the client using SDK -/// in [DeviceDetails] while registering -enum DevicePlatform { - /// indicates an android device - android, - - /// indicates an iOS device - ios, - - /// indicates a browser - browser, -} - -/// To indicate the type of device in [DeviceDetails] while registering -/// -/// https://docs.ably.com/client-lib-development-guide/features/#PCD4 -enum FormFactor { - /// indicates the device is a mobile phone - phone, - - /// indicates the device is a tablet - tablet, - - /// indicates the device is a desktop - desktop, - - /// indicates the device is a television - tv, - - /// indicates the device is a smart watch - watch, - - /// indicates the device is an automobile - car, - - /// indicates the device is an embedded system / iOT device - embedded, - - /// indicates the device belong to categories other than mentioned above - other, -} diff --git a/lib/src/spec/message.dart b/lib/src/spec/message.dart deleted file mode 100644 index 350ef5fe9..000000000 --- a/lib/src/spec/message.dart +++ /dev/null @@ -1,376 +0,0 @@ -import 'dart:convert'; -import 'dart:typed_data'; - -import 'package:collection/collection.dart'; -import 'package:meta/meta.dart'; - -import '../generated/platformconstants.dart'; -import 'enums.dart'; -import 'rest/channels.dart'; - -/// Handles supported message data types, their encoding and decoding -class MessageData { - final T _data; - - /// Only Map, List, string and Buffer types are supported - MessageData(this._data) - : assert(T == Map || T == List || T == String || T == Uint8List); - - /// retrieve data - T get data => _data; - - /// initializes [MessageData] with given value and asserts from input type - static MessageData? fromValue(Object? value) { - if (value == null) { - return null; - } - assert( - value is MessageData || - value is Map || - value is List || - value is String || - value is Uint8List, - 'Message data must be either `Map`, `List`, `String` or `Uint8List`.' - ' Does not support $value ("${value.runtimeType}")', - ); - if (value is MessageData) { - return value; - } else if (value is Map) { - return MessageData(value); - } else if (value is Uint8List) { - return MessageData(value); - } else if (value is List) { - return MessageData(value); - } else if (value is String) { - return MessageData(value); - } else { - throw AssertionError( - 'Message data must be either `Map`, `List`, `String` or `Uint8List`.' - ' Does not support $value ("${value.runtimeType}")', - ); - } - } -} - -/// Delta extension configuration for [MessageExtras] -@immutable -class DeltaExtras { - /// the id of the message the delta was generated from - final String? from; - - /// the delta format. Only "vcdiff" is supported currently - final String? format; - - /// create instance from a map - DeltaExtras._fromMap(Map value) - : from = value[TxDeltaExtras.from] as String?, - format = value[TxDeltaExtras.format] as String?; - - @override - bool operator ==(Object other) => - other is DeltaExtras && other.from == from && other.format == format; - - @override - int get hashCode => '$from:$format'.hashCode; -} - -/// Handles supported message extras types, their encoding and decoding -@immutable -class MessageExtras { - /// json-encodable map of extras - final Map? map; - - /// configuration for delta compression extension - final DeltaExtras? _delta; - - /// delta configuration received from channel message - DeltaExtras? get delta => _delta; - - /// Creates an instance from given extras map - const MessageExtras(this.map) : _delta = null; - - /// Creates an instance from given extras map and an instance of DeltaExtras - const MessageExtras._withDelta(this.map, this._delta); - - /// initializes [MessageExtras] with given value and validates - /// the data type, runtime - static MessageExtras? fromMap(Map? extrasMap) { - if (extrasMap == null) return null; - extrasMap = Map.castFrom( - json.decode(json.encode(extrasMap)) as Map, - ); - final deltaMap = extrasMap.remove(TxMessageExtras.delta) as Map?; - return MessageExtras._withDelta( - extrasMap, - (deltaMap == null) ? null : DeltaExtras._fromMap(deltaMap), - ); - } - - @override - String toString() => {'extras': map, 'delta': delta}.toString(); - - @override - bool operator ==(Object other) => - other is MessageExtras && - const MapEquality().equals(other.map, map) && - other.delta == delta; - - @override - int get hashCode => '${map.hashCode}:${delta.hashCode}'.hashCode; -} - -/// An individual message to be sent/received by Ably -/// -/// https://docs.ably.com/client-lib-development-guide/features/#TM1 -@immutable -class Message { - /// A unique ID for this message - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TM2a - final String? id; - - /// The timestamp for this message - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TM2f - final DateTime? timestamp; - - /// The id of the publisher of this message - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TM2b - final String? clientId; - - /// The connection id of the publisher of this message - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TM2c - final String? connectionId; - - /// Any transformation applied to the data for this message - final String? encoding; - - final MessageData? _data; - - /// Message payload - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TM2d - Object? get data => _data?.data; - - /// Name of the message - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TM2g - final String? name; - - /// Message extras that may contain message metadata - /// and/or ancillary payloads - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TM2i - final MessageExtras? extras; - - /// Creates a message instance with [name], [data] and [clientId] - Message({ - this.id, - this.name, - Object? data, - this.clientId, - this.connectionId, - this.timestamp, - this.encoding, - this.extras, - }) : _data = MessageData.fromValue(data); - - @override - bool operator ==(Object other) => - other is Message && - other.id == id && - other.name == name && - other.data == data && - other.extras == extras && - other.encoding == encoding && - other.clientId == clientId && - other.timestamp == timestamp && - other.connectionId == connectionId; - - @override - int get hashCode => '$id:' - '$name:' - '$encoding:' - '$clientId:' - '$timestamp:' - '$connectionId:' - '${data?.hashCode}:' - '${extras?.hashCode}:' - .hashCode; - - /// https://docs.ably.com/client-lib-development-guide/features/#TM3 - /// - /// TODO(tiholic): decoding and decryption is not implemented as per - /// RSL6 and RLS6b as mentioned in TM3 - Message.fromEncoded( - Map jsonObject, [ - ChannelOptions? channelOptions, - ]) : id = jsonObject['id'] as String?, - name = jsonObject['name'] as String?, - clientId = jsonObject['clientId'] as String?, - connectionId = jsonObject['connectionId'] as String?, - _data = MessageData.fromValue(jsonObject['data']), - encoding = jsonObject['encoding'] as String?, - extras = MessageExtras.fromMap( - Map.castFrom( - jsonObject['extras'] as Map, - ), - ), - timestamp = jsonObject['timestamp'] != null - ? DateTime.fromMillisecondsSinceEpoch( - jsonObject['timestamp'] as int, - ) - : null; - - /// https://docs.ably.com/client-lib-development-guide/features/#TM3 - static List fromEncodedArray( - List> jsonArray, [ - ChannelOptions? channelOptions, - ]) => - jsonArray.map((e) => Message.fromEncoded(e, channelOptions)).toList(); - - @override - String toString() => 'Message' - ' id=$id' - ' name=$name' - ' data=$data' - ' extras=$extras' - ' encoding=$encoding' - ' clientId=$clientId' - ' timestamp=$timestamp' - ' connectionId=$connectionId'; - -// TODO(tiholic) add support for fromEncoded and fromEncodedArray (TM3) -} - -/// An individual presence message sent or received via realtime -/// -/// https://docs.ably.com/client-lib-development-guide/features/#TP1 -@immutable -class PresenceMessage { - /// unique ID for this presence message - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TP3a - final String? id; - - /// presence action - to update presence status of current client, - /// or to understand presence state of another client - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TP3b - final PresenceAction? action; - - /// https://docs.ably.com/client-lib-development-guide/features/#TP3c - final String? clientId; - - /// connection id of the source of this message - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TP3d - final String? connectionId; - - final MessageData? _data; - - /// Message payload - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TP3e - Object? get data => _data?.data; - - /// https://docs.ably.com/client-lib-development-guide/features/#TP3f - final String? encoding; - - /// Message extras that may contain message metadata - /// and/or ancillary payloads - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TP3i - final MessageExtras? extras; - - /// https://docs.ably.com/client-lib-development-guide/features/#TP3g - final DateTime? timestamp; - - /// https://docs.ably.com/client-lib-development-guide/features/#TP3h - String get memberKey => '$connectionId:$clientId'; - - /// instantiates presence message with - PresenceMessage({ - this.id, - this.action, - this.clientId, - this.connectionId, - Object? data, - this.encoding, - this.extras, - this.timestamp, - }) : _data = MessageData.fromValue(data); - - @override - bool operator ==(Object other) => - other is PresenceMessage && - other.id == id && - other.action == action && - other.clientId == clientId && - other.connectionId == connectionId && - other.data == data && - other.encoding == encoding && - other.extras == extras && - other.timestamp == timestamp; - - @override - int get hashCode => '$id:' - '$encoding:' - '$clientId:' - '$timestamp:' - '$connectionId:' - '${data?.toString()}:' - '${action.toString()}:' - '${extras?.toString()}:' - .hashCode; - - /// https://docs.ably.com/client-lib-development-guide/features/#TP4 - /// - /// TODO(tiholic): decoding and decryption is not implemented as per - /// RSL6 and RLS6b as mentioned in TP4 - PresenceMessage.fromEncoded( - Map jsonObject, [ - ChannelOptions? channelOptions, - ]) : id = jsonObject['id'] as String?, - action = PresenceAction.values.firstWhere((e) => - e.toString().split('.')[1] == jsonObject['action'] as String?), - clientId = jsonObject['clientId'] as String?, - connectionId = jsonObject['connectionId'] as String?, - _data = MessageData.fromValue(jsonObject['data']), - encoding = jsonObject['encoding'] as String?, - extras = MessageExtras.fromMap( - Map.castFrom( - jsonObject['extras'] as Map, - ), - ), - timestamp = jsonObject['timestamp'] != null - ? DateTime.fromMillisecondsSinceEpoch( - jsonObject['timestamp'] as int, - ) - : null; - - /// https://docs.ably.com/client-lib-development-guide/features/#TP4 - static List fromEncodedArray( - List> jsonArray, [ - ChannelOptions? channelOptions, - ]) => - jsonArray - .map((jsonObject) => PresenceMessage.fromEncoded( - jsonObject, - channelOptions, - )) - .toList(); - - @override - String toString() => 'PresenceMessage' - ' id=$id' - ' data=$data' - ' action=$action' - ' extras=$extras' - ' encoding=$encoding' - ' clientId=$clientId' - ' timestamp=$timestamp' - ' connectionId=$connectionId'; -} diff --git a/lib/src/spec/push/push.dart b/lib/src/spec/push/push.dart deleted file mode 100644 index 8dd035560..000000000 --- a/lib/src/spec/push/push.dart +++ /dev/null @@ -1,113 +0,0 @@ -import '../common.dart'; - -/// Manage push notification channel subscriptions for devices or clients -abstract class PushChannelSubscriptions { - /// List channel subscriptions filtered by optional params. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#RSH1c1 - Future> list( - PushChannelSubscriptionParams params, - ); - - /// List channels with at least one subscribed device. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#RSH1c2 - Future> listChannels( - PushChannelsParams params, - ); - - /// Save push channel subscription for a device or client ID. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#RSH1c3 - Future save(PushChannelSubscription subscription); - - /// Remove a push channel subscription. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#RSH1c4 - Future remove(PushChannelSubscription subscription); - - /// Remove all matching push channel subscriptions. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#RSH1c5 - Future removeWhere(PushChannelSubscriptionParams params); -} - -/// Manage device registrations for push notifications -abstract class PushDeviceRegistrations { - /// Get registered device by device ID. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#RSH1b1 - Future get({ - DeviceDetails? deviceDetails, - String? deviceId, - }); - - /// List registered devices filtered by optional params. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#RSH1b2 - Future> list( - DeviceRegistrationParams params, - ); - - /// Save and register device. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#RSH1b3 - Future save(DeviceDetails deviceDetails); - - /// Remove device. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#RSH1b4 - Future remove({ - DeviceDetails? deviceDetails, - String? deviceId, - }); - - /// Remove device matching where params. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#RSH1b5 - Future removeWhere(DeviceRegistrationParams params); -} - -/// Class providing push notification administrative functionality -/// for registering devices and attaching to channels etc. -/// -/// https://docs.ably.com/client-lib-development-guide/features/#RSH1 -abstract class PushAdmin { - /// Manage device registrations. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#RSH1b - PushDeviceRegistrations? deviceRegistrations; - - /// Manage channel subscriptions for devices or clients. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#RSH1c - PushChannelSubscriptions? channelSubscriptions; - - /// https://docs.ably.com/client-lib-development-guide/features/#RSH1a - Future publish(Map recipient, Map payload); -} - -/// Class providing push notification functionality -/// -/// https://docs.ably.com/client-lib-development-guide/features/#RSH1 -abstract class Push { - /// Admin features for push notifications like managing devices - /// and channel subscriptions. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#RSH1 - PushAdmin? admin; - - /// Activate this device for push notifications by registering - /// with the push transport such as GCM/APNS. - /// - /// returns DeviceDetails - /// https://docs.ably.com/client-lib-development-guide/features/#RSH2a - Future activate(); - - /// Deactivate this device for push notifications by removing - /// the registration with the push transport such as GCM/APNS. - /// - /// returns deviceId - /// https://docs.ably.com/client-lib-development-guide/features/#RSH2b - Future deactivate(); -} diff --git a/lib/src/spec/spec.dart b/lib/src/spec/spec.dart deleted file mode 100644 index c27687597..000000000 --- a/lib/src/spec/spec.dart +++ /dev/null @@ -1,16 +0,0 @@ -export 'auth.dart'; -export 'common.dart'; -export 'common.dart'; -export 'connection.dart'; -export 'constants.dart'; -export 'enums.dart'; -export 'message.dart'; -export 'push/push.dart'; -export 'realtime/channels.dart'; -export 'realtime/presence.dart'; -export 'realtime/realtime.dart'; -export 'rest/ably_base.dart'; -export 'rest/channels.dart'; -export 'rest/options.dart'; -export 'rest/presence.dart'; -export 'rest/rest.dart'; diff --git a/lib/src/stats/stats.dart b/lib/src/stats/stats.dart new file mode 100644 index 000000000..03b4d9bb4 --- /dev/null +++ b/lib/src/stats/stats.dart @@ -0,0 +1,57 @@ +import 'stats_connection_types.dart'; +import 'stats_message_traffic.dart'; +import 'stats_message_types.dart'; +import 'stats_request_count.dart'; +import 'stats_resource_count.dart'; + +/// A class representing an individual statistic for a specified [intervalId] +/// +/// https://docs.ably.com/client-lib-development-guide/features/#TS1 +class Stats { + /// Aggregates inbound and outbound messages. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TS12e + StatsMessageTypes? all; + + /// Breakdown of API requests received via the REST API. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TS12e + StatsRequestCount? apiRequests; + + /// Breakdown of channels stats. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TS12e + StatsResourceCount? channels; + + /// Breakdown of connection stats data for different (TLS vs non-TLS) + /// connection types. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TS12i + StatsConnectionTypes? connections; + + /// All inbound messages i.e. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TS12f + StatsMessageTraffic? inbound; + + /// The interval that this statistic applies to, + /// see GRANULARITY and INTERVAL_FORMAT_STRING. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TS12a + String? intervalId; + + /// All outbound messages i.e. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TS12g + StatsMessageTraffic? outbound; + + /// Messages persisted for later retrieval via the history API. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TS12h + StatsMessageTypes? persisted; + + /// Breakdown of Token requests received via the REST API. + /// + /// https://docs.ably.com/client-lib-development-guide/features/#TS12l + StatsRequestCount? tokenRequests; +} diff --git a/lib/src/stats/stats_connection_types.dart b/lib/src/stats/stats_connection_types.dart new file mode 100644 index 000000000..1b7e2f2e9 --- /dev/null +++ b/lib/src/stats/stats_connection_types.dart @@ -0,0 +1,16 @@ +import 'stats_resource_count.dart'; + +/// ConnectionTypes contains a breakdown of summary stats data +/// for different (TLS vs non-TLS) connection types +/// +/// https://docs.ably.com/client-lib-development-guide/features/#TS4 +abstract class StatsConnectionTypes { + /// All connection count (includes both TLS & non-TLS connections). + StatsResourceCount? all; + + /// Non-TLS connection count (unencrypted). + StatsResourceCount? plain; + + /// TLS connection count. + StatsResourceCount? tls; +} \ No newline at end of file diff --git a/lib/src/stats/stats_internal_granularity.dart b/lib/src/stats/stats_internal_granularity.dart new file mode 100644 index 000000000..738e2fe70 --- /dev/null +++ b/lib/src/stats/stats_internal_granularity.dart @@ -0,0 +1,14 @@ +/// https://docs.ably.com/client-lib-development-guide/features/#TS12c +enum StatsIntervalGranularity { + /// indicates units in minutes + minute, + + /// indicates units in hours + hour, + + /// indicates units in days + day, + + /// indicates units in months + month, +} \ No newline at end of file diff --git a/lib/src/stats/stats_message_count.dart b/lib/src/stats/stats_message_count.dart new file mode 100644 index 000000000..de803b55a --- /dev/null +++ b/lib/src/stats/stats_message_count.dart @@ -0,0 +1,10 @@ +/// MessageCount contains aggregate counts for messages and data transferred +/// +/// https://docs.ably.com/client-lib-development-guide/features/#TS5 +abstract class StatsMessageCount { + /// Count of all messages. + int? count; + + /// Total data transferred for all messages in bytes. + int? data; +} \ No newline at end of file diff --git a/lib/src/stats/stats_message_traffic.dart b/lib/src/stats/stats_message_traffic.dart new file mode 100644 index 000000000..cf444fc80 --- /dev/null +++ b/lib/src/stats/stats_message_traffic.dart @@ -0,0 +1,20 @@ +import 'stats_message_types.dart'; + +/// MessageTraffic contains a breakdown of summary stats data +/// for traffic over various transport types +/// +/// https://docs.ably.com/client-lib-development-guide/features/#TS7 +abstract class StatsMessageTraffic { + /// All messages count (includes realtime, rest and webhook messages). + StatsMessageTypes? all; + + /// Count of messages transferred over a realtime transport + /// such as WebSockets. + StatsMessageTypes? realtime; + + /// Count of messages transferred using REST. + StatsMessageTypes? rest; + + /// Count of messages delivered using WebHooks. + StatsMessageTypes? webhook; +} \ No newline at end of file diff --git a/lib/src/stats/stats_message_types.dart b/lib/src/stats/stats_message_types.dart new file mode 100644 index 000000000..608938213 --- /dev/null +++ b/lib/src/stats/stats_message_types.dart @@ -0,0 +1,16 @@ +import 'stats_message_count.dart'; + +/// MessageTypes contains a breakdown of summary stats data +/// for different (message vs presence) message types +/// +/// https://docs.ably.com/client-lib-development-guide/features/#TS6 +abstract class StatsMessageTypes { + /// All messages count (includes both presence & messages). + StatsMessageCount? all; + + /// Count of channel messages. + StatsMessageCount? messages; + + /// Count of presence messages. + StatsMessageCount? presence; +} \ No newline at end of file diff --git a/lib/src/stats/stats_request_count.dart b/lib/src/stats/stats_request_count.dart new file mode 100644 index 000000000..970d6b8d4 --- /dev/null +++ b/lib/src/stats/stats_request_count.dart @@ -0,0 +1,14 @@ +/// RequestCount contains aggregate counts for requests made +/// +/// https://docs.ably.com/client-lib-development-guide/features/#TS8 +abstract class StatsRequestCount { + /// Requests failed. + int? failed; + + /// Requests refused typically as a result of permissions + /// or a limit being exceeded. + int? refused; + + /// Requests succeeded. + int? succeeded; +} \ No newline at end of file diff --git a/lib/src/stats/stats_resource_count.dart b/lib/src/stats/stats_resource_count.dart new file mode 100644 index 000000000..89781023f --- /dev/null +++ b/lib/src/stats/stats_resource_count.dart @@ -0,0 +1,20 @@ +/// ResourceCount contains aggregate data for usage of a resource +/// in a specific scope +/// +/// https://docs.ably.com/client-lib-development-guide/features/#TS9 +abstract class StatsResourceCount { + /// Average resources of this type used for this period. + int? mean; + + /// Minimum total resources of this type used for this period. + int? min; + + /// Total resources of this type opened. + int? opened; + + /// Peak resources of this type used for this period. + int? peak; + + /// Resource requests refused within this period. + int? refused; +} From 3dcd2f735c88f87708acc8e65039249a38b51110 Mon Sep 17 00:00:00 2001 From: Ben Butterworth <24711048+ben-xD@users.noreply.github.com> Date: Wed, 7 Jul 2021 17:36:30 +0100 Subject: [PATCH 07/16] Import classes/ files when they are referenced in comments. To disable dart analysis warning: Only reference in scope identifiers in doc comments. --- lib/src/authentication/src/client_options.dart | 1 + lib/src/authentication/src/token_params.dart | 2 ++ lib/src/common/src/ably_base.dart | 4 ++-- lib/src/common/src/channels.dart | 2 ++ lib/src/common/src/event_emitter.dart | 2 ++ lib/src/error/src/error_info.dart | 2 ++ lib/src/logging/logging.dart | 4 ++-- lib/src/logging/src/log_level.dart | 2 ++ lib/src/message/src/delta_extras.dart | 1 + lib/src/message/src/presence_action.dart | 2 ++ lib/src/platform/src/ably_message.dart | 1 + lib/src/platform/src/codec.dart | 12 ++++++------ lib/src/platform/src/info.dart | 3 ++- lib/src/platform/src/paginated_result.dart | 2 +- lib/src/platform/src/platform.dart | 11 ++++++----- lib/src/platform/src/realtime/connection.dart | 7 +++---- lib/src/platform/src/rest/rest_channels.dart | 8 ++++---- lib/src/push_notifications/src/device_platform.dart | 2 ++ .../push_notifications/src/device_push_state.dart | 2 ++ .../src/device_registration_params.dart | 1 + lib/src/push_notifications/src/form_factor.dart | 2 ++ .../push_notifications/src/push_channel_params.dart | 2 ++ .../src/push_channel_subscription_params.dart | 2 ++ lib/src/realtime/src/channels.dart | 1 + lib/src/realtime/src/connection_event.dart | 2 ++ lib/src/realtime/src/connection_state_change.dart | 1 + lib/src/realtime/src/presence.dart | 4 +--- lib/src/realtime/src/realtime.dart | 3 ++- lib/src/realtime/src/realtime_history_params.dart | 5 +++-- lib/src/realtime/src/realtime_presence_params.dart | 2 ++ lib/src/rest/src/presence.dart | 1 + test/mock_method_call_manager.dart | 5 +++-- 32 files changed, 68 insertions(+), 33 deletions(-) diff --git a/lib/src/authentication/src/client_options.dart b/lib/src/authentication/src/client_options.dart index e54e329df..968695ed4 100644 --- a/lib/src/authentication/src/client_options.dart +++ b/lib/src/authentication/src/client_options.dart @@ -1,4 +1,5 @@ import '../../logging/logging.dart'; +import '../../realtime/realtime.dart'; import '../authentication.dart'; /// Ably library options used when instancing a REST or Realtime client library diff --git a/lib/src/authentication/src/token_params.dart b/lib/src/authentication/src/token_params.dart index da6009a15..9929a9abc 100644 --- a/lib/src/authentication/src/token_params.dart +++ b/lib/src/authentication/src/token_params.dart @@ -1,3 +1,5 @@ +import '../authentication.dart'; + /// A class providing parameters of a token request. /// /// Parameters for a token request diff --git a/lib/src/common/src/ably_base.dart b/lib/src/common/src/ably_base.dart index 11460334d..6a931d818 100644 --- a/lib/src/common/src/ably_base.dart +++ b/lib/src/common/src/ably_base.dart @@ -1,5 +1,5 @@ import '../../authentication/authentication.dart'; -import '../../authentication/src/auth.dart'; +import '../../platform/platform.dart'; import '../../push_notifications/push_notifications.dart'; import '../../stats/stats.dart'; import '../common.dart'; @@ -27,7 +27,7 @@ abstract class AblyBase { /// viz., subscribing for push notifications, etc Push? push; - /// gets stats based on params as a [PaginatedResultNative] + /// gets stats based on params as a [PaginatedResult] /// /// https://docs.ably.com/client-lib-development-guide/features/#RSC6 Future> stats([ diff --git a/lib/src/common/src/channels.dart b/lib/src/common/src/channels.dart index 9af729619..930ec804a 100644 --- a/lib/src/common/src/channels.dart +++ b/lib/src/common/src/channels.dart @@ -1,5 +1,7 @@ import 'package:meta/meta.dart'; +import '../../platform/platform.dart'; + /// A collection of Channel objects accessible /// through [Rest.channels] or [Realtime.channels] abstract class Channels extends Iterable { diff --git a/lib/src/common/src/event_emitter.dart b/lib/src/common/src/event_emitter.dart index b21a02408..42cc2393e 100644 --- a/lib/src/common/src/event_emitter.dart +++ b/lib/src/common/src/event_emitter.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + /// Interface implemented by Ably classes that can emit events, /// offering the capability to create listeners for those events. /// [E] is type of event to listen for diff --git a/lib/src/error/src/error_info.dart b/lib/src/error/src/error_info.dart index d90ac76c5..d1516ea8e 100644 --- a/lib/src/error/src/error_info.dart +++ b/lib/src/error/src/error_info.dart @@ -1,3 +1,5 @@ +import '../error.dart'; + /// An [AblyException] encapsulates [ErrorInfo] which carries details /// about information related to Ably-specific error [code], /// generic [statusCode], error [message], diff --git a/lib/src/logging/logging.dart b/lib/src/logging/logging.dart index 2c3fa4bf6..0a34e0d73 100644 --- a/lib/src/logging/logging.dart +++ b/lib/src/logging/logging.dart @@ -1,2 +1,2 @@ -export 'src/log_level.dart'; -export 'src/log_handler.dart'; \ No newline at end of file +export 'src/log_handler.dart'; +export 'src/log_level.dart'; \ No newline at end of file diff --git a/lib/src/logging/src/log_level.dart b/lib/src/logging/src/log_level.dart index 415fa4a2e..f21dde844 100644 --- a/lib/src/logging/src/log_level.dart +++ b/lib/src/logging/src/log_level.dart @@ -1,3 +1,5 @@ +import '../../authentication/authentication.dart'; + /// Log levels - control verbosity of log messages /// /// Can be used for [ClientOptions.logLevel] diff --git a/lib/src/message/src/delta_extras.dart b/lib/src/message/src/delta_extras.dart index 0f858191d..6ae64c12a 100644 --- a/lib/src/message/src/delta_extras.dart +++ b/lib/src/message/src/delta_extras.dart @@ -1,6 +1,7 @@ import 'package:meta/meta.dart'; import '../../generated/platform_constants.dart'; +import '../message.dart'; /// Delta extension configuration for [MessageExtras] @immutable diff --git a/lib/src/message/src/presence_action.dart b/lib/src/message/src/presence_action.dart index 522add5fd..54f42045f 100644 --- a/lib/src/message/src/presence_action.dart +++ b/lib/src/message/src/presence_action.dart @@ -1,3 +1,5 @@ +import '../message.dart'; + /// Status on a presence message /// /// https://docs.ably.com/client-lib-development-guide/features/#TP2 diff --git a/lib/src/platform/src/ably_message.dart b/lib/src/platform/src/ably_message.dart index 165f8faed..634ea5a2d 100644 --- a/lib/src/platform/src/ably_message.dart +++ b/lib/src/platform/src/ably_message.dart @@ -1,3 +1,4 @@ +import '../../generated/platform_constants.dart'; /// An encapsulating object used to pass data to/from platform for method calls class AblyMessage { diff --git a/lib/src/platform/src/codec.dart b/lib/src/platform/src/codec.dart index 2a15f3373..1895f9e7c 100644 --- a/lib/src/platform/src/codec.dart +++ b/lib/src/platform/src/codec.dart @@ -1,12 +1,12 @@ -import 'package:ably_flutter/src/authentication/authentication.dart'; -import 'package:ably_flutter/src/error/error.dart'; -import 'package:ably_flutter/src/generated/platform_constants.dart'; -import 'package:ably_flutter/src/message/message.dart'; -import 'package:ably_flutter/src/realtime/realtime.dart'; -import 'package:ably_flutter/src/rest/rest.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; +import '../../authentication/authentication.dart'; +import '../../error/error.dart'; +import '../../generated/platform_constants.dart'; +import '../../message/message.dart'; +import '../../realtime/realtime.dart'; +import '../../rest/rest.dart'; import '../platform.dart'; /// a [_Encoder] encodes custom type and converts it to a Map which will diff --git a/lib/src/platform/src/info.dart b/lib/src/platform/src/info.dart index f9527e9d7..eec742171 100644 --- a/lib/src/platform/src/info.dart +++ b/lib/src/platform/src/info.dart @@ -3,7 +3,8 @@ import '../platform.dart'; /// Get android/iOS platform version Future platformVersion() async => - (await Platform.invokePlatformMethod(PlatformMethod.getPlatformVersion))!; + (await Platform.invokePlatformMethod( + PlatformMethod.getPlatformVersion))!; /// Get ably library version Future version() async => diff --git a/lib/src/platform/src/paginated_result.dart b/lib/src/platform/src/paginated_result.dart index 10b0aef9c..518a0659d 100644 --- a/lib/src/platform/src/paginated_result.dart +++ b/lib/src/platform/src/paginated_result.dart @@ -16,7 +16,7 @@ class PaginatedResult extends PlatformObject /// instantiated by the codec. So the code invoking platform method /// is bound to update this [_pageHandle] /// - /// [PaginatedResultInterface.fromAblyMessage] will act as a utility to update + /// [PaginatedResult.fromAblyMessage] will act as a utility to update /// this property. See [next] and [first] for usages int? _pageHandle; diff --git a/lib/src/platform/src/platform.dart b/lib/src/platform/src/platform.dart index a8a06d70d..feb0ea1ba 100644 --- a/lib/src/platform/src/platform.dart +++ b/lib/src/platform/src/platform.dart @@ -14,11 +14,11 @@ class Platform { /// instance of method channel to interact with android/ios code static final MethodChannel methodChannel = - MethodChannel('io.ably.flutter.plugin', codec); + MethodChannel('io.ably.flutter.plugin', codec); /// instance of method channel to listen to android/ios events static final StreamsChannel streamsChannel = - StreamsChannel('io.ably.flutter.stream', codec); + StreamsChannel('io.ably.flutter.stream', codec); /// Initializing ably on platform side by invoking `register` platform method. /// Register will clear any stale instances on platform. @@ -42,10 +42,11 @@ class Platform { /// invokes a platform [method] with [arguments] /// - /// calls an [_initialize] method before invoking any method so as to handle - /// any cleanup tasks that are especially required while performing hot-restart + /// calls an [_initialize] method before invoking any method to handle any + /// cleanup tasks that are especially required while performing hot-restart /// (as hot-restart is known to not clear any objects on platform side) - static Future invokePlatformMethod(String method, [Object? arguments]) async { + static Future invokePlatformMethod(String method, + [Object? arguments]) async { await _initialize(); try { return await methodChannel.invokeMethod(method, arguments); diff --git a/lib/src/platform/src/realtime/connection.dart b/lib/src/platform/src/realtime/connection.dart index 9cd4a9b50..1147a64ad 100644 --- a/lib/src/platform/src/realtime/connection.dart +++ b/lib/src/platform/src/realtime/connection.dart @@ -1,9 +1,8 @@ import 'dart:async'; -import 'package:ably_flutter/src/error/error.dart'; -import 'package:ably_flutter/src/generated/platform_constants.dart'; -import 'package:ably_flutter/src/realtime/realtime.dart'; - +import '../../../error/error.dart'; +import '../../../generated/platform_constants.dart'; +import '../../../realtime/realtime.dart'; import '../../platform.dart'; /// connects to Ably service diff --git a/lib/src/platform/src/rest/rest_channels.dart b/lib/src/platform/src/rest/rest_channels.dart index 57c75ce45..dc86ec5e3 100644 --- a/lib/src/platform/src/rest/rest_channels.dart +++ b/lib/src/platform/src/rest/rest_channels.dart @@ -1,14 +1,14 @@ import 'dart:async'; import 'dart:collection'; -import 'package:ably_flutter/src/error/error.dart'; -import 'package:ably_flutter/src/generated/platform_constants.dart'; -import 'package:ably_flutter/src/message/message.dart'; -import 'package:ably_flutter/src/rest/rest.dart'; import 'package:flutter/services.dart'; import 'package:meta/meta.dart'; import 'package:pedantic/pedantic.dart'; +import '../../../error/error.dart'; +import '../../../generated/platform_constants.dart'; +import '../../../message/message.dart'; +import '../../../rest/rest.dart'; import '../../platform.dart'; /// Plugin based implementation of Rest channel diff --git a/lib/src/push_notifications/src/device_platform.dart b/lib/src/push_notifications/src/device_platform.dart index 3bf72ca83..f0029f1c5 100644 --- a/lib/src/push_notifications/src/device_platform.dart +++ b/lib/src/push_notifications/src/device_platform.dart @@ -1,3 +1,5 @@ +import '../push_notifications.dart'; + /// To indicate the operating system -or- platform of the client using SDK /// in [DeviceDetails] while registering enum DevicePlatform { diff --git a/lib/src/push_notifications/src/device_push_state.dart b/lib/src/push_notifications/src/device_push_state.dart index 1e76ef6c2..6f93101a7 100644 --- a/lib/src/push_notifications/src/device_push_state.dart +++ b/lib/src/push_notifications/src/device_push_state.dart @@ -1,3 +1,5 @@ +import '../push_notifications.dart'; + /// To indicate Push State of a device in [DeviceDetails] via [DevicePushState] /// while registering enum DevicePushState { diff --git a/lib/src/push_notifications/src/device_registration_params.dart b/lib/src/push_notifications/src/device_registration_params.dart index 44f423dea..c019e4949 100644 --- a/lib/src/push_notifications/src/device_registration_params.dart +++ b/lib/src/push_notifications/src/device_registration_params.dart @@ -1,3 +1,4 @@ +import '../push_notifications.dart'; import 'device_push_state.dart'; /// Params to filter push device registrations. diff --git a/lib/src/push_notifications/src/form_factor.dart b/lib/src/push_notifications/src/form_factor.dart index 8e7831302..abec508e8 100644 --- a/lib/src/push_notifications/src/form_factor.dart +++ b/lib/src/push_notifications/src/form_factor.dart @@ -1,3 +1,5 @@ +import '../push_notifications.dart'; + /// To indicate the type of device in [DeviceDetails] while registering /// /// https://docs.ably.com/client-lib-development-guide/features/#PCD4 diff --git a/lib/src/push_notifications/src/push_channel_params.dart b/lib/src/push_notifications/src/push_channel_params.dart index 5d6e288aa..210065f51 100644 --- a/lib/src/push_notifications/src/push_channel_params.dart +++ b/lib/src/push_notifications/src/push_channel_params.dart @@ -1,3 +1,5 @@ +import '../push_notifications.dart'; + /// params to filter channels on a [PushChannelSubscriptions] /// /// https://docs.ably.com/client-lib-development-guide/features/#RSH1c2 diff --git a/lib/src/push_notifications/src/push_channel_subscription_params.dart b/lib/src/push_notifications/src/push_channel_subscription_params.dart index b34b94006..34e686bb3 100644 --- a/lib/src/push_notifications/src/push_channel_subscription_params.dart +++ b/lib/src/push_notifications/src/push_channel_subscription_params.dart @@ -1,3 +1,5 @@ +import '../push_notifications.dart'; + /// Params to filter push channel subscriptions. /// /// See [PushChannelSubscriptions.list], [PushChannelSubscriptions.removeWhere] diff --git a/lib/src/realtime/src/channels.dart b/lib/src/realtime/src/channels.dart index c066020f6..49e9317fa 100644 --- a/lib/src/realtime/src/channels.dart +++ b/lib/src/realtime/src/channels.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import '../../authentication/authentication.dart'; import '../../common/common.dart'; import '../../common/src/channels.dart'; import '../../common/src/event_emitter.dart'; diff --git a/lib/src/realtime/src/connection_event.dart b/lib/src/realtime/src/connection_event.dart index d68706d94..7346efd32 100644 --- a/lib/src/realtime/src/connection_event.dart +++ b/lib/src/realtime/src/connection_event.dart @@ -1,3 +1,5 @@ +import '../realtime.dart'; + /// Connection event is same as [ConnectionState] except that it also handles /// update operations on a connection /// diff --git a/lib/src/realtime/src/connection_state_change.dart b/lib/src/realtime/src/connection_state_change.dart index da886553d..42e1c87b5 100644 --- a/lib/src/realtime/src/connection_state_change.dart +++ b/lib/src/realtime/src/connection_state_change.dart @@ -1,4 +1,5 @@ import '../../error/error.dart'; +import '../../platform/platform.dart'; import 'connection_event.dart'; import 'connection_state.dart'; diff --git a/lib/src/realtime/src/presence.dart b/lib/src/realtime/src/presence.dart index 7e680d2fa..a2870dbf3 100644 --- a/lib/src/realtime/src/presence.dart +++ b/lib/src/realtime/src/presence.dart @@ -1,10 +1,8 @@ import 'dart:async'; -import 'package:ably_flutter/src/platform/platform.dart'; - -import '../../common/common.dart'; import '../../message/src/presence_action.dart'; import '../../message/src/presence_message.dart'; +import '../../platform/platform.dart'; import '../realtime.dart'; import 'channels.dart'; diff --git a/lib/src/realtime/src/realtime.dart b/lib/src/realtime/src/realtime.dart index 3cecbe830..e3f4ed9bf 100644 --- a/lib/src/realtime/src/realtime.dart +++ b/lib/src/realtime/src/realtime.dart @@ -5,7 +5,8 @@ import '../realtime.dart'; /// an abstract class for Ably's Realtime client /// /// https://docs.ably.com/client-lib-development-guide/features/#RTC1 -abstract class RealtimeInterface extends AblyBase { +abstract class RealtimeInterface + extends AblyBase { /// https://docs.ably.com/client-lib-development-guide/features/#RTC1 RealtimeInterface({ ClientOptions? options, diff --git a/lib/src/realtime/src/realtime_history_params.dart b/lib/src/realtime/src/realtime_history_params.dart index 4da86c9ad..5d03b89ba 100644 --- a/lib/src/realtime/src/realtime_history_params.dart +++ b/lib/src/realtime/src/realtime_history_params.dart @@ -1,5 +1,6 @@ -// TODO stop extending RestHistoryParams like this -import 'package:ably_flutter/src/rest/rest.dart'; +// TODO stop extending RestHistoryParams: +// RealtimeHistoryParams is not a RestHistoryParams +import '../../rest/rest.dart'; /// https://docs.ably.com/client-lib-development-guide/features/#RTL10 class RealtimeHistoryParams extends RestHistoryParams { diff --git a/lib/src/realtime/src/realtime_presence_params.dart b/lib/src/realtime/src/realtime_presence_params.dart index aadf93105..7ccc13d95 100644 --- a/lib/src/realtime/src/realtime_presence_params.dart +++ b/lib/src/realtime/src/realtime_presence_params.dart @@ -1,3 +1,5 @@ +import '../../platform/platform.dart'; + /// Params used as a filter for querying presence on a channel /// /// https://docs.ably.com/client-lib-development-guide/features/#RTP11c diff --git a/lib/src/rest/src/presence.dart b/lib/src/rest/src/presence.dart index c39b20b3f..eed737a85 100644 --- a/lib/src/rest/src/presence.dart +++ b/lib/src/rest/src/presence.dart @@ -1,5 +1,6 @@ import '../../common/common.dart'; import '../../message/message.dart'; +import '../../platform/platform.dart'; import 'rest_history_params.dart'; import 'rest_presence_params.dart'; diff --git a/test/mock_method_call_manager.dart b/test/mock_method_call_manager.dart index 14c758040..7c7f73802 100644 --- a/test/mock_method_call_manager.dart +++ b/test/mock_method_call_manager.dart @@ -67,8 +67,9 @@ class MockMethodCallManager { // because function references (in `authCallback`) get dropped by the // PlatformChannel. if (!isAuthenticated && clientOptions.authUrl == 'hasAuthCallback') { - await AblyMethodCallHandler(Platform.methodChannel).onRealtimeAuthCallback( - AblyMessage(TokenParams(timestamp: DateTime.now()), + await AblyMethodCallHandler(Platform.methodChannel) + .onRealtimeAuthCallback(AblyMessage( + TokenParams(timestamp: DateTime.now()), handle: handle)); isAuthenticated = true; throw PlatformException( From 3a6b5483ee5b2e0e1c8be718c85a7acdc5f1a4dd Mon Sep 17 00:00:00 2001 From: Ben Butterworth <24711048+ben-xD@users.noreply.github.com> Date: Thu, 8 Jul 2021 13:55:18 +0100 Subject: [PATCH 08/16] Fix formatting using `flutter format .` --- lib/ably_flutter.dart | 2 +- lib/src/authentication/authentication.dart | 2 +- lib/src/authentication/src/auth_options.dart | 1 - .../authentication/src/http_auth_type.dart | 2 +- lib/src/authentication/src/token_details.dart | 12 ++--- lib/src/common/common.dart | 2 +- lib/src/common/src/channels.dart | 2 +- lib/src/common/src/event_emitter.dart | 2 +- lib/src/common/src/event_listener.dart | 2 +- .../common/src/http_paginated_response.dart | 1 - lib/src/common/src/paginated_result.dart | 2 +- lib/src/error/error.dart | 2 +- lib/src/error/src/error_codes.dart | 2 +- lib/src/error/src/error_info.dart | 2 +- lib/src/error/src/timeouts.dart | 2 - lib/src/logging/logging.dart | 2 +- lib/src/logging/src/log_handler.dart | 6 +-- lib/src/message/message.dart | 2 +- lib/src/message/src/delta_extras.dart | 2 +- lib/src/message/src/message.dart | 50 +++++++++-------- lib/src/message/src/message_data.dart | 18 +++---- lib/src/message/src/message_extras.dart | 6 +-- lib/src/message/src/presence_action.dart | 2 +- lib/src/message/src/presence_message.dart | 54 +++++++++---------- lib/src/platform/src/ably_message.dart | 2 +- .../src/realtime/realtime_channel.dart | 3 +- lib/src/platform/src/rest/rest.dart | 3 +- .../push_notifications.dart | 2 +- .../src/admin/push_admin.dart | 2 +- .../src/admin/push_device_registrations.dart | 6 +-- .../src/device_details.dart | 2 +- .../src/device_platform.dart | 2 +- .../src/device_push_state.dart | 2 +- .../src/device_registration_params.dart | 2 +- .../src/push_channel_params.dart | 2 +- .../src/push_channel_subscription.dart | 2 +- .../src/push_channel_subscription_params.dart | 2 +- .../src/push_channel_subscriptions.dart | 8 +-- lib/src/realtime/realtime.dart | 2 +- lib/src/realtime/src/channel_event.dart | 2 +- lib/src/realtime/src/channel_mode.dart | 2 +- lib/src/realtime/src/channel_state.dart | 2 +- lib/src/realtime/src/channel_state_event.dart | 12 ++--- lib/src/realtime/src/connection_event.dart | 2 +- lib/src/realtime/src/connection_state.dart | 2 +- .../realtime/src/connection_state_change.dart | 12 ++--- .../realtime/src/realtime_history_params.dart | 10 ++-- lib/src/rest/rest.dart | 2 +- lib/src/rest/src/channel_options.dart | 2 +- lib/src/rest/src/options.dart | 1 + lib/src/stats/stats_connection_types.dart | 2 +- lib/src/stats/stats_internal_granularity.dart | 2 +- lib/src/stats/stats_message_count.dart | 2 +- lib/src/stats/stats_message_traffic.dart | 2 +- lib/src/stats/stats_message_types.dart | 2 +- lib/src/stats/stats_request_count.dart | 2 +- test/realtime/channel_test.dart | 1 - 57 files changed, 138 insertions(+), 146 deletions(-) diff --git a/lib/ably_flutter.dart b/lib/ably_flutter.dart index e8cce200d..17b49b666 100644 --- a/lib/ably_flutter.dart +++ b/lib/ably_flutter.dart @@ -9,4 +9,4 @@ export 'src/platform/platform.dart'; export 'src/push_notifications/push_notifications.dart'; export 'src/realtime/realtime.dart'; export 'src/rest/rest.dart'; -export 'src/stats/stats.dart'; \ No newline at end of file +export 'src/stats/stats.dart'; diff --git a/lib/src/authentication/authentication.dart b/lib/src/authentication/authentication.dart index f1ee187f1..692a004d2 100644 --- a/lib/src/authentication/authentication.dart +++ b/lib/src/authentication/authentication.dart @@ -5,4 +5,4 @@ export 'src/client_options.dart'; export 'src/http_auth_type.dart'; export 'src/token_details.dart'; export 'src/token_params.dart'; -export 'src/token_request.dart'; \ No newline at end of file +export 'src/token_request.dart'; diff --git a/lib/src/authentication/src/auth_options.dart b/lib/src/authentication/src/auth_options.dart index bf07ea9fe..396ca2328 100644 --- a/lib/src/authentication/src/auth_options.dart +++ b/lib/src/authentication/src/auth_options.dart @@ -91,4 +91,3 @@ abstract class AuthOptions { /// Java: io.ably.lib.rest.Auth.TokenCallback.getTokenRequest(TokenParams) /// returns either a [String] token or [TokenDetails] or [TokenRequest] typedef AuthCallback = Future Function(TokenParams params); - diff --git a/lib/src/authentication/src/http_auth_type.dart b/lib/src/authentication/src/http_auth_type.dart index 803c012f4..47ca033de 100644 --- a/lib/src/authentication/src/http_auth_type.dart +++ b/lib/src/authentication/src/http_auth_type.dart @@ -8,4 +8,4 @@ enum HttpAuthType { /// Token auth xAblyToken, -} \ No newline at end of file +} diff --git a/lib/src/authentication/src/token_details.dart b/lib/src/authentication/src/token_details.dart index 19fe69ef9..ca901b857 100644 --- a/lib/src/authentication/src/token_details.dart +++ b/lib/src/authentication/src/token_details.dart @@ -33,12 +33,12 @@ class TokenDetails { /// instantiates a [TokenDetails] with provided values TokenDetails( - this.token, { - this.expires, - this.issued, - this.capability, - this.clientId, - }); + this.token, { + this.expires, + this.issued, + this.capability, + this.clientId, + }); /// Creates an instance from the map /// diff --git a/lib/src/common/common.dart b/lib/src/common/common.dart index 509a5c504..28a3801cf 100644 --- a/lib/src/common/common.dart +++ b/lib/src/common/common.dart @@ -3,4 +3,4 @@ export 'src/channels.dart'; export 'src/event_emitter.dart'; export 'src/event_listener.dart'; export 'src/http_paginated_response.dart'; -export 'src/paginated_result.dart'; \ No newline at end of file +export 'src/paginated_result.dart'; diff --git a/lib/src/common/src/channels.dart b/lib/src/common/src/channels.dart index 930ec804a..fe1e4b22a 100644 --- a/lib/src/common/src/channels.dart +++ b/lib/src/common/src/channels.dart @@ -66,4 +66,4 @@ class _ChannelIterator implements Iterator { _currentChannel = _channels[_currentIndex++]; return true; } -} \ No newline at end of file +} diff --git a/lib/src/common/src/event_emitter.dart b/lib/src/common/src/event_emitter.dart index 42cc2393e..215176765 100644 --- a/lib/src/common/src/event_emitter.dart +++ b/lib/src/common/src/event_emitter.dart @@ -12,4 +12,4 @@ import 'dart:async'; abstract class EventEmitter { /// Create a listener, with which registrations may be made. Stream on([E? event]); -} \ No newline at end of file +} diff --git a/lib/src/common/src/event_listener.dart b/lib/src/common/src/event_listener.dart index f26a56e15..53b091fe6 100644 --- a/lib/src/common/src/event_listener.dart +++ b/lib/src/common/src/event_listener.dart @@ -9,4 +9,4 @@ abstract class EventListener { /// Remove registrations for this listener, irrespective of type. Future off(); -} \ No newline at end of file +} diff --git a/lib/src/common/src/http_paginated_response.dart b/lib/src/common/src/http_paginated_response.dart index af8fdfa7b..307c5e685 100644 --- a/lib/src/common/src/http_paginated_response.dart +++ b/lib/src/common/src/http_paginated_response.dart @@ -1,4 +1,3 @@ - import '../common.dart'; /// The response from an HTTP request containing an empty or diff --git a/lib/src/common/src/paginated_result.dart b/lib/src/common/src/paginated_result.dart index 5525555a8..409249a22 100644 --- a/lib/src/common/src/paginated_result.dart +++ b/lib/src/common/src/paginated_result.dart @@ -30,4 +30,4 @@ abstract class PaginatedResultInterface { /// /// https://docs.ably.com/client-lib-development-guide/features/#TG7 bool isLast(); -} \ No newline at end of file +} diff --git a/lib/src/error/error.dart b/lib/src/error/error.dart index 06bce19c8..773a066bb 100644 --- a/lib/src/error/error.dart +++ b/lib/src/error/error.dart @@ -1,4 +1,4 @@ export 'src/ably_exception.dart'; export 'src/error_codes.dart'; export 'src/error_info.dart'; -export 'src/timeouts.dart'; \ No newline at end of file +export 'src/timeouts.dart'; diff --git a/lib/src/error/src/error_codes.dart b/lib/src/error/src/error_codes.dart index d6515fbc8..7f5eaf63f 100644 --- a/lib/src/error/src/error_codes.dart +++ b/lib/src/error/src/error_codes.dart @@ -6,4 +6,4 @@ class ErrorCodes { /// method call after responding to authCallback method channel /// call triggered from platform side static const int authCallbackFailure = 80019; -} \ No newline at end of file +} diff --git a/lib/src/error/src/error_info.dart b/lib/src/error/src/error_info.dart index d1516ea8e..b9971b793 100644 --- a/lib/src/error/src/error_info.dart +++ b/lib/src/error/src/error_info.dart @@ -42,4 +42,4 @@ class ErrorInfo { ' code=$code' ' statusCode=$statusCode' ' href=$href'; -} \ No newline at end of file +} diff --git a/lib/src/error/src/timeouts.dart b/lib/src/error/src/timeouts.dart index 876cb37e4..18eedd063 100644 --- a/lib/src/error/src/timeouts.dart +++ b/lib/src/error/src/timeouts.dart @@ -1,5 +1,3 @@ - - /// Static timeouts used inside the SDK class Timeouts { /// max time allowed for retrying an operation for auth failure diff --git a/lib/src/logging/logging.dart b/lib/src/logging/logging.dart index 0a34e0d73..17777c582 100644 --- a/lib/src/logging/logging.dart +++ b/lib/src/logging/logging.dart @@ -1,2 +1,2 @@ export 'src/log_handler.dart'; -export 'src/log_level.dart'; \ No newline at end of file +export 'src/log_level.dart'; diff --git a/lib/src/logging/src/log_handler.dart b/lib/src/logging/src/log_handler.dart index a4b7b1ca4..3150a875b 100644 --- a/lib/src/logging/src/log_handler.dart +++ b/lib/src/logging/src/log_handler.dart @@ -4,6 +4,6 @@ import '../../error/src/ably_exception.dart'; /// /// https://docs.ably.com/client-lib-development-guide/features/#TO3c typedef LogHandler = void Function({ -String? msg, -AblyException? exception, -}); \ No newline at end of file + String? msg, + AblyException? exception, +}); diff --git a/lib/src/message/message.dart b/lib/src/message/message.dart index 17d9150be..0415a6866 100644 --- a/lib/src/message/message.dart +++ b/lib/src/message/message.dart @@ -3,4 +3,4 @@ export 'src/message.dart'; export 'src/message_data.dart'; export 'src/message_extras.dart'; export 'src/presence_action.dart'; -export 'src/presence_message.dart'; \ No newline at end of file +export 'src/presence_message.dart'; diff --git a/lib/src/message/src/delta_extras.dart b/lib/src/message/src/delta_extras.dart index 6ae64c12a..40076971f 100644 --- a/lib/src/message/src/delta_extras.dart +++ b/lib/src/message/src/delta_extras.dart @@ -24,4 +24,4 @@ class DeltaExtras { @override int get hashCode => '$from:$format'.hashCode; -} \ No newline at end of file +} diff --git a/lib/src/message/src/message.dart b/lib/src/message/src/message.dart index e28e3aa2c..3a67000a3 100644 --- a/lib/src/message/src/message.dart +++ b/lib/src/message/src/message.dart @@ -1,5 +1,3 @@ - - import 'package:meta/meta.dart'; import '../../rest/src/channel_options.dart'; @@ -67,24 +65,24 @@ class Message { @override bool operator ==(Object other) => other is Message && - other.id == id && - other.name == name && - other.data == data && - other.extras == extras && - other.encoding == encoding && - other.clientId == clientId && - other.timestamp == timestamp && - other.connectionId == connectionId; + other.id == id && + other.name == name && + other.data == data && + other.extras == extras && + other.encoding == encoding && + other.clientId == clientId && + other.timestamp == timestamp && + other.connectionId == connectionId; @override int get hashCode => '$id:' - '$name:' - '$encoding:' - '$clientId:' - '$timestamp:' - '$connectionId:' - '${data?.hashCode}:' - '${extras?.hashCode}:' + '$name:' + '$encoding:' + '$clientId:' + '$timestamp:' + '$connectionId:' + '${data?.hashCode}:' + '${extras?.hashCode}:' .hashCode; /// https://docs.ably.com/client-lib-development-guide/features/#TM3 @@ -92,9 +90,9 @@ class Message { /// TODO(tiholic): decoding and decryption is not implemented as per /// RSL6 and RLS6b as mentioned in TM3 Message.fromEncoded( - Map jsonObject, [ - ChannelOptions? channelOptions, - ]) : id = jsonObject['id'] as String?, + Map jsonObject, [ + ChannelOptions? channelOptions, + ]) : id = jsonObject['id'] as String?, name = jsonObject['name'] as String?, clientId = jsonObject['clientId'] as String?, connectionId = jsonObject['connectionId'] as String?, @@ -107,15 +105,15 @@ class Message { ), timestamp = jsonObject['timestamp'] != null ? DateTime.fromMillisecondsSinceEpoch( - jsonObject['timestamp'] as int, - ) + jsonObject['timestamp'] as int, + ) : null; /// https://docs.ably.com/client-lib-development-guide/features/#TM3 static List fromEncodedArray( - List> jsonArray, [ - ChannelOptions? channelOptions, - ]) => + List> jsonArray, [ + ChannelOptions? channelOptions, + ]) => jsonArray.map((e) => Message.fromEncoded(e, channelOptions)).toList(); @override @@ -130,4 +128,4 @@ class Message { ' connectionId=$connectionId'; // TODO(tiholic) add support for fromEncoded and fromEncodedArray (TM3) -} \ No newline at end of file +} diff --git a/lib/src/message/src/message_data.dart b/lib/src/message/src/message_data.dart index 5930d3da0..f978fd5cd 100644 --- a/lib/src/message/src/message_data.dart +++ b/lib/src/message/src/message_data.dart @@ -17,13 +17,13 @@ class MessageData { return null; } assert( - value is MessageData || - value is Map || - value is List || - value is String || - value is Uint8List, - 'Message data must be either `Map`, `List`, `String` or `Uint8List`.' - ' Does not support $value ("${value.runtimeType}")', + value is MessageData || + value is Map || + value is List || + value is String || + value is Uint8List, + 'Message data must be either `Map`, `List`, `String` or `Uint8List`.' + ' Does not support $value ("${value.runtimeType}")', ); if (value is MessageData) { return value; @@ -38,8 +38,8 @@ class MessageData { } else { throw AssertionError( 'Message data must be either `Map`, `List`, `String` or `Uint8List`.' - ' Does not support $value ("${value.runtimeType}")', + ' Does not support $value ("${value.runtimeType}")', ); } } -} \ No newline at end of file +} diff --git a/lib/src/message/src/message_extras.dart b/lib/src/message/src/message_extras.dart index c829209a8..400274bb2 100644 --- a/lib/src/message/src/message_extras.dart +++ b/lib/src/message/src/message_extras.dart @@ -44,9 +44,9 @@ class MessageExtras { @override bool operator ==(Object other) => other is MessageExtras && - const MapEquality().equals(other.map, map) && - other.delta == delta; + const MapEquality().equals(other.map, map) && + other.delta == delta; @override int get hashCode => '${map.hashCode}:${delta.hashCode}'.hashCode; -} \ No newline at end of file +} diff --git a/lib/src/message/src/presence_action.dart b/lib/src/message/src/presence_action.dart index 54f42045f..2ac18be73 100644 --- a/lib/src/message/src/presence_action.dart +++ b/lib/src/message/src/presence_action.dart @@ -21,4 +21,4 @@ enum PresenceAction { /// indicates that presence status of a client in presence member map /// needs to be updated update, -} \ No newline at end of file +} diff --git a/lib/src/message/src/presence_message.dart b/lib/src/message/src/presence_message.dart index a29d9211b..afea8cfc1 100644 --- a/lib/src/message/src/presence_message.dart +++ b/lib/src/message/src/presence_message.dart @@ -66,24 +66,24 @@ class PresenceMessage { @override bool operator ==(Object other) => other is PresenceMessage && - other.id == id && - other.action == action && - other.clientId == clientId && - other.connectionId == connectionId && - other.data == data && - other.encoding == encoding && - other.extras == extras && - other.timestamp == timestamp; + other.id == id && + other.action == action && + other.clientId == clientId && + other.connectionId == connectionId && + other.data == data && + other.encoding == encoding && + other.extras == extras && + other.timestamp == timestamp; @override int get hashCode => '$id:' - '$encoding:' - '$clientId:' - '$timestamp:' - '$connectionId:' - '${data?.toString()}:' - '${action.toString()}:' - '${extras?.toString()}:' + '$encoding:' + '$clientId:' + '$timestamp:' + '$connectionId:' + '${data?.toString()}:' + '${action.toString()}:' + '${extras?.toString()}:' .hashCode; /// https://docs.ably.com/client-lib-development-guide/features/#TP4 @@ -91,11 +91,11 @@ class PresenceMessage { /// TODO(tiholic): decoding and decryption is not implemented as per /// RSL6 and RLS6b as mentioned in TP4 PresenceMessage.fromEncoded( - Map jsonObject, [ - ChannelOptions? channelOptions, - ]) : id = jsonObject['id'] as String?, + Map jsonObject, [ + ChannelOptions? channelOptions, + ]) : id = jsonObject['id'] as String?, action = PresenceAction.values.firstWhere((e) => - e.toString().split('.')[1] == jsonObject['action'] as String?), + e.toString().split('.')[1] == jsonObject['action'] as String?), clientId = jsonObject['clientId'] as String?, connectionId = jsonObject['connectionId'] as String?, _data = MessageData.fromValue(jsonObject['data']), @@ -107,20 +107,20 @@ class PresenceMessage { ), timestamp = jsonObject['timestamp'] != null ? DateTime.fromMillisecondsSinceEpoch( - jsonObject['timestamp'] as int, - ) + jsonObject['timestamp'] as int, + ) : null; /// https://docs.ably.com/client-lib-development-guide/features/#TP4 static List fromEncodedArray( - List> jsonArray, [ - ChannelOptions? channelOptions, - ]) => + List> jsonArray, [ + ChannelOptions? channelOptions, + ]) => jsonArray .map((jsonObject) => PresenceMessage.fromEncoded( - jsonObject, - channelOptions, - )) + jsonObject, + channelOptions, + )) .toList(); @override diff --git a/lib/src/platform/src/ably_message.dart b/lib/src/platform/src/ably_message.dart index 634ea5a2d..e668ea65e 100644 --- a/lib/src/platform/src/ably_message.dart +++ b/lib/src/platform/src/ably_message.dart @@ -28,4 +28,4 @@ class AblyMessage { handle: source.handle, type: source.type, ); -} \ No newline at end of file +} diff --git a/lib/src/platform/src/realtime/realtime_channel.dart b/lib/src/platform/src/realtime/realtime_channel.dart index 02f50eb58..3a9aa8c08 100644 --- a/lib/src/platform/src/realtime/realtime_channel.dart +++ b/lib/src/platform/src/realtime/realtime_channel.dart @@ -218,8 +218,7 @@ class RealtimePlatformChannels @override @protected - RealtimeChannel createChannel(String name) - => RealtimeChannel(realtime, name); + RealtimeChannel createChannel(String name) => RealtimeChannel(realtime, name); @override void release(String name) { diff --git a/lib/src/platform/src/rest/rest.dart b/lib/src/platform/src/rest/rest.dart index 2551d177c..423cc5b3e 100644 --- a/lib/src/platform/src/rest/rest.dart +++ b/lib/src/platform/src/rest/rest.dart @@ -17,8 +17,7 @@ Map get restInstances => _restInstancesUnmodifiableView ??= UnmodifiableMapView(_restInstances); /// Ably's Rest client -class Rest extends PlatformObject - implements RestInterface { +class Rest extends PlatformObject implements RestInterface { /// instantiates with [ClientOptions] and a String [key] /// /// creates client options from key if [key] is provided diff --git a/lib/src/push_notifications/push_notifications.dart b/lib/src/push_notifications/push_notifications.dart index b3707d9ef..df3c48edd 100644 --- a/lib/src/push_notifications/push_notifications.dart +++ b/lib/src/push_notifications/push_notifications.dart @@ -11,4 +11,4 @@ export 'src/push_channel.dart'; export 'src/push_channel_params.dart'; export 'src/push_channel_subscription.dart'; export 'src/push_channel_subscription_params.dart'; -export 'src/push_channel_subscriptions.dart'; \ No newline at end of file +export 'src/push_channel_subscriptions.dart'; diff --git a/lib/src/push_notifications/src/admin/push_admin.dart b/lib/src/push_notifications/src/admin/push_admin.dart index 1b7e94d6a..842054432 100644 --- a/lib/src/push_notifications/src/admin/push_admin.dart +++ b/lib/src/push_notifications/src/admin/push_admin.dart @@ -18,4 +18,4 @@ abstract class PushAdmin { /// https://docs.ably.com/client-lib-development-guide/features/#RSH1a Future publish(Map recipient, Map payload); -} \ No newline at end of file +} diff --git a/lib/src/push_notifications/src/admin/push_device_registrations.dart b/lib/src/push_notifications/src/admin/push_device_registrations.dart index 6b9931f2b..2dfed00ab 100644 --- a/lib/src/push_notifications/src/admin/push_device_registrations.dart +++ b/lib/src/push_notifications/src/admin/push_device_registrations.dart @@ -17,8 +17,8 @@ abstract class PushDeviceRegistrations { /// /// https://docs.ably.com/client-lib-development-guide/features/#RSH1b2 Future> list( - DeviceRegistrationParams params, - ); + DeviceRegistrationParams params, + ); /// Save and register device. /// @@ -37,4 +37,4 @@ abstract class PushDeviceRegistrations { /// /// https://docs.ably.com/client-lib-development-guide/features/#RSH1b5 Future removeWhere(DeviceRegistrationParams params); -} \ No newline at end of file +} diff --git a/lib/src/push_notifications/src/device_details.dart b/lib/src/push_notifications/src/device_details.dart index e111b6a0d..ca9387763 100644 --- a/lib/src/push_notifications/src/device_details.dart +++ b/lib/src/push_notifications/src/device_details.dart @@ -41,4 +41,4 @@ abstract class DeviceDetails { /// /// https://docs.ably.com/client-lib-development-guide/features/#PCD7 DevicePushDetails? push; -} \ No newline at end of file +} diff --git a/lib/src/push_notifications/src/device_platform.dart b/lib/src/push_notifications/src/device_platform.dart index f0029f1c5..a0cb9f317 100644 --- a/lib/src/push_notifications/src/device_platform.dart +++ b/lib/src/push_notifications/src/device_platform.dart @@ -11,4 +11,4 @@ enum DevicePlatform { /// indicates a browser browser, -} \ No newline at end of file +} diff --git a/lib/src/push_notifications/src/device_push_state.dart b/lib/src/push_notifications/src/device_push_state.dart index 6f93101a7..ec0db93ac 100644 --- a/lib/src/push_notifications/src/device_push_state.dart +++ b/lib/src/push_notifications/src/device_push_state.dart @@ -11,4 +11,4 @@ enum DevicePushState { /// indicates the device push state failed failed, -} \ No newline at end of file +} diff --git a/lib/src/push_notifications/src/device_registration_params.dart b/lib/src/push_notifications/src/device_registration_params.dart index c019e4949..062ee80f1 100644 --- a/lib/src/push_notifications/src/device_registration_params.dart +++ b/lib/src/push_notifications/src/device_registration_params.dart @@ -17,4 +17,4 @@ abstract class DeviceRegistrationParams { /// filter by device state DevicePushState? state; -} \ No newline at end of file +} diff --git a/lib/src/push_notifications/src/push_channel_params.dart b/lib/src/push_notifications/src/push_channel_params.dart index 210065f51..2a05bd638 100644 --- a/lib/src/push_notifications/src/push_channel_params.dart +++ b/lib/src/push_notifications/src/push_channel_params.dart @@ -6,4 +6,4 @@ import '../push_notifications.dart'; abstract class PushChannelsParams { /// limit results for each page int? limit; -} \ No newline at end of file +} diff --git a/lib/src/push_notifications/src/push_channel_subscription.dart b/lib/src/push_notifications/src/push_channel_subscription.dart index 62a8b6982..1f8fb8eac 100644 --- a/lib/src/push_notifications/src/push_channel_subscription.dart +++ b/lib/src/push_notifications/src/push_channel_subscription.dart @@ -17,4 +17,4 @@ abstract class PushChannelSubscription { /// /// https://docs.ably.com/client-lib-development-guide/features/#PCS3 String? clientId; -} \ No newline at end of file +} diff --git a/lib/src/push_notifications/src/push_channel_subscription_params.dart b/lib/src/push_notifications/src/push_channel_subscription_params.dart index 34e686bb3..01ac7dc26 100644 --- a/lib/src/push_notifications/src/push_channel_subscription_params.dart +++ b/lib/src/push_notifications/src/push_channel_subscription_params.dart @@ -16,4 +16,4 @@ abstract class PushChannelSubscriptionParams { /// limit results for each page int? limit; -} \ No newline at end of file +} diff --git a/lib/src/push_notifications/src/push_channel_subscriptions.dart b/lib/src/push_notifications/src/push_channel_subscriptions.dart index a5e61b63e..3085a087e 100644 --- a/lib/src/push_notifications/src/push_channel_subscriptions.dart +++ b/lib/src/push_notifications/src/push_channel_subscriptions.dart @@ -7,15 +7,15 @@ abstract class PushChannelSubscriptions { /// /// https://docs.ably.com/client-lib-development-guide/features/#RSH1c1 Future> list( - PushChannelSubscriptionParams params, - ); + PushChannelSubscriptionParams params, + ); /// List channels with at least one subscribed device. /// /// https://docs.ably.com/client-lib-development-guide/features/#RSH1c2 Future> listChannels( - PushChannelsParams params, - ); + PushChannelsParams params, + ); /// Save push channel subscription for a device or client ID. /// diff --git a/lib/src/realtime/realtime.dart b/lib/src/realtime/realtime.dart index 8ca65d671..295e19720 100644 --- a/lib/src/realtime/realtime.dart +++ b/lib/src/realtime/realtime.dart @@ -10,4 +10,4 @@ export 'src/connection_state_change.dart'; export 'src/presence.dart'; export 'src/realtime.dart'; export 'src/realtime_history_params.dart'; -export 'src/realtime_presence_params.dart'; \ No newline at end of file +export 'src/realtime_presence_params.dart'; diff --git a/lib/src/realtime/src/channel_event.dart b/lib/src/realtime/src/channel_event.dart index 63d931184..321c97988 100644 --- a/lib/src/realtime/src/channel_event.dart +++ b/lib/src/realtime/src/channel_event.dart @@ -24,4 +24,4 @@ enum ChannelEvent { /// specifies that a channel state is updated update, -} \ No newline at end of file +} diff --git a/lib/src/realtime/src/channel_mode.dart b/lib/src/realtime/src/channel_mode.dart index 7725dec31..2ca61ff48 100644 --- a/lib/src/realtime/src/channel_mode.dart +++ b/lib/src/realtime/src/channel_mode.dart @@ -15,4 +15,4 @@ enum ChannelMode { /// specifies that channel can subscribe to presence events presenceSubscribe, -} \ No newline at end of file +} diff --git a/lib/src/realtime/src/channel_state.dart b/lib/src/realtime/src/channel_state.dart index f31640be7..9eaf29d8c 100644 --- a/lib/src/realtime/src/channel_state.dart +++ b/lib/src/realtime/src/channel_state.dart @@ -22,4 +22,4 @@ enum ChannelState { /// channel failed to connect failed, -} \ No newline at end of file +} diff --git a/lib/src/realtime/src/channel_state_event.dart b/lib/src/realtime/src/channel_state_event.dart index 0f588c7a4..abda245ad 100644 --- a/lib/src/realtime/src/channel_state_event.dart +++ b/lib/src/realtime/src/channel_state_event.dart @@ -37,10 +37,10 @@ class ChannelStateChange { /// initializes with [resumed] set to false ChannelStateChange( - this.current, - this.previous, - this.event, { - this.reason, - this.resumed = false, - }); + this.current, + this.previous, + this.event, { + this.reason, + this.resumed = false, + }); } diff --git a/lib/src/realtime/src/connection_event.dart b/lib/src/realtime/src/connection_event.dart index 7346efd32..08358201c 100644 --- a/lib/src/realtime/src/connection_event.dart +++ b/lib/src/realtime/src/connection_event.dart @@ -31,4 +31,4 @@ enum ConnectionEvent { /// specifies that a connection is updated update, -} \ No newline at end of file +} diff --git a/lib/src/realtime/src/connection_state.dart b/lib/src/realtime/src/connection_state.dart index 694063de1..d5d78b8ae 100644 --- a/lib/src/realtime/src/connection_state.dart +++ b/lib/src/realtime/src/connection_state.dart @@ -24,4 +24,4 @@ enum ConnectionState { /// specifies that a connection to ably is failed failed, -} \ No newline at end of file +} diff --git a/lib/src/realtime/src/connection_state_change.dart b/lib/src/realtime/src/connection_state_change.dart index 42e1c87b5..065b7ce3d 100644 --- a/lib/src/realtime/src/connection_state_change.dart +++ b/lib/src/realtime/src/connection_state_change.dart @@ -39,10 +39,10 @@ class ConnectionStateChange { /// initializes without any defaults ConnectionStateChange( - this.current, - this.previous, - this.event, { - this.reason, - this.retryIn, - }); + this.current, + this.previous, + this.event, { + this.reason, + this.retryIn, + }); } diff --git a/lib/src/realtime/src/realtime_history_params.dart b/lib/src/realtime/src/realtime_history_params.dart index 5d03b89ba..11f5ff4d5 100644 --- a/lib/src/realtime/src/realtime_history_params.dart +++ b/lib/src/realtime/src/realtime_history_params.dart @@ -21,9 +21,9 @@ class RealtimeHistoryParams extends RestHistoryParams { int limit = 100, this.untilAttach, }) : super( - start: start, - end: end, - direction: direction, - limit: limit, - ); + start: start, + end: end, + direction: direction, + limit: limit, + ); } diff --git a/lib/src/rest/rest.dart b/lib/src/rest/rest.dart index a215417d7..423953914 100644 --- a/lib/src/rest/rest.dart +++ b/lib/src/rest/rest.dart @@ -4,4 +4,4 @@ export 'src/options.dart'; export 'src/presence.dart'; export 'src/rest.dart'; export 'src/rest_history_params.dart'; -export 'src/rest_presence_params.dart'; \ No newline at end of file +export 'src/rest_presence_params.dart'; diff --git a/lib/src/rest/src/channel_options.dart b/lib/src/rest/src/channel_options.dart index 28722e8d4..3aa810e6c 100644 --- a/lib/src/rest/src/channel_options.dart +++ b/lib/src/rest/src/channel_options.dart @@ -7,4 +7,4 @@ class ChannelOptions { /// create channel options with a cipher ChannelOptions(this.cipher); -} \ No newline at end of file +} diff --git a/lib/src/rest/src/options.dart b/lib/src/rest/src/options.dart index e69de29bb..8b1378917 100644 --- a/lib/src/rest/src/options.dart +++ b/lib/src/rest/src/options.dart @@ -0,0 +1 @@ + diff --git a/lib/src/stats/stats_connection_types.dart b/lib/src/stats/stats_connection_types.dart index 1b7e2f2e9..3589cd12f 100644 --- a/lib/src/stats/stats_connection_types.dart +++ b/lib/src/stats/stats_connection_types.dart @@ -13,4 +13,4 @@ abstract class StatsConnectionTypes { /// TLS connection count. StatsResourceCount? tls; -} \ No newline at end of file +} diff --git a/lib/src/stats/stats_internal_granularity.dart b/lib/src/stats/stats_internal_granularity.dart index 738e2fe70..81918c688 100644 --- a/lib/src/stats/stats_internal_granularity.dart +++ b/lib/src/stats/stats_internal_granularity.dart @@ -11,4 +11,4 @@ enum StatsIntervalGranularity { /// indicates units in months month, -} \ No newline at end of file +} diff --git a/lib/src/stats/stats_message_count.dart b/lib/src/stats/stats_message_count.dart index de803b55a..cdbe89d3d 100644 --- a/lib/src/stats/stats_message_count.dart +++ b/lib/src/stats/stats_message_count.dart @@ -7,4 +7,4 @@ abstract class StatsMessageCount { /// Total data transferred for all messages in bytes. int? data; -} \ No newline at end of file +} diff --git a/lib/src/stats/stats_message_traffic.dart b/lib/src/stats/stats_message_traffic.dart index cf444fc80..6ff5478ea 100644 --- a/lib/src/stats/stats_message_traffic.dart +++ b/lib/src/stats/stats_message_traffic.dart @@ -17,4 +17,4 @@ abstract class StatsMessageTraffic { /// Count of messages delivered using WebHooks. StatsMessageTypes? webhook; -} \ No newline at end of file +} diff --git a/lib/src/stats/stats_message_types.dart b/lib/src/stats/stats_message_types.dart index 608938213..9348467d1 100644 --- a/lib/src/stats/stats_message_types.dart +++ b/lib/src/stats/stats_message_types.dart @@ -13,4 +13,4 @@ abstract class StatsMessageTypes { /// Count of presence messages. StatsMessageCount? presence; -} \ No newline at end of file +} diff --git a/lib/src/stats/stats_request_count.dart b/lib/src/stats/stats_request_count.dart index 970d6b8d4..5bd0d8a1e 100644 --- a/lib/src/stats/stats_request_count.dart +++ b/lib/src/stats/stats_request_count.dart @@ -11,4 +11,4 @@ abstract class StatsRequestCount { /// Requests succeeded. int? succeeded; -} \ No newline at end of file +} diff --git a/test/realtime/channel_test.dart b/test/realtime/channel_test.dart index 403f0209a..7f9979adc 100644 --- a/test/realtime/channel_test.dart +++ b/test/realtime/channel_test.dart @@ -8,7 +8,6 @@ import 'package:pedantic/pedantic.dart'; import '../mock_method_call_manager.dart'; - void main() { TestWidgetsFlutterBinding.ensureInitialized(); From 915b28f23916c1488034a84fa4a5247a8ddcca25 Mon Sep 17 00:00:00 2001 From: Ben Butterworth <24711048+ben-xD@users.noreply.github.com> Date: Thu, 8 Jul 2021 13:57:21 +0100 Subject: [PATCH 09/16] Simplify import --- lib/src/message/src/message.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/src/message/src/message.dart b/lib/src/message/src/message.dart index 3a67000a3..e440169c6 100644 --- a/lib/src/message/src/message.dart +++ b/lib/src/message/src/message.dart @@ -1,8 +1,7 @@ import 'package:meta/meta.dart'; -import '../../rest/src/channel_options.dart'; -import 'message_data.dart'; -import 'message_extras.dart'; +import '../../rest/rest.dart'; +import '../message.dart'; /// An individual message to be sent/received by Ably /// From 144eea6a3bb71d730385556b30946344a40015f4 Mon Sep 17 00:00:00 2001 From: Ben Butterworth <24711048+ben-xD@users.noreply.github.com> Date: Fri, 9 Jul 2021 09:09:34 +0100 Subject: [PATCH 10/16] Revert renaming template files because not all systems are case sensitive --- bin/codegen.dart | 8 ++++---- ...rm_constants.dart.dart => platformconstants.dart.dart} | 0 2 files changed, 4 insertions(+), 4 deletions(-) rename bin/templates/{platform_constants.dart.dart => platformconstants.dart.dart} (100%) diff --git a/bin/codegen.dart b/bin/codegen.dart index c1d9021f3..d58448ddb 100644 --- a/bin/codegen.dart +++ b/bin/codegen.dart @@ -1,10 +1,10 @@ import 'dart:io'; import 'codegen_context.dart' show context; -import 'templates/PlatformConstants.h.dart' as objc_header_template; -import 'templates/PlatformConstants.java.dart' as java_template; -import 'templates/PlatformConstants.m.dart' as objc_impl_template; -import 'templates/platform_constants.dart.dart' as dart_template; +import 'templates/platformconstants.dart.dart' as dart_template; +import 'templates/platformconstants.h.dart' as objc_header_template; +import 'templates/platformconstants.java.dart' as java_template; +import 'templates/platformconstants.m.dart' as objc_impl_template; typedef Template = String Function(Map context); diff --git a/bin/templates/platform_constants.dart.dart b/bin/templates/platformconstants.dart.dart similarity index 100% rename from bin/templates/platform_constants.dart.dart rename to bin/templates/platformconstants.dart.dart From 706b80de4f045f95dd21bcbe84bffd9c45c677f9 Mon Sep 17 00:00:00 2001 From: Ben Butterworth <24711048+ben-xD@users.noreply.github.com> Date: Fri, 9 Jul 2021 13:13:35 +0100 Subject: [PATCH 11/16] Update references to file in readme after file rename --- CONTRIBUTING.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3d1268f61..eda645d8a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -75,14 +75,14 @@ Some files in the project are generated to maintain sync between ## Implementing new codec types -1. Add new type along with value in `_types` list at [bin/codegencontext.dart](bin/codegencontext.dart) -2. Add an object definition with object name and its properties to `objects` list at [bin/codegencontext.dart](bin/codegencontext.dart) +1. Add new type along with value in `_types` list at [bin/codegen_context.dart](bin/codegen_context.dart) +2. Add an object definition with object name and its properties to `objects` list at [bin/codegen_context.dart](bin/codegen_context.dart) This will create `Tx` under which all properties are accessible. Generate platform constants and continue -3. update `getCodecType` in [lib.src.codec.Codec](lib/src/native/platform_utilities/codec.dart) so new codec type is returned based on runtime type -4. update `codecPair` in [lib.src.codec.Codec](lib/src/native/platform_utilities/codec.dart) so new encoder/decoder is assigned for new type +3. update `getCodecType` in [Codec.dart](lib/src/platform/src/codec.dart) so new codec type is returned based on runtime type +4. update `codecPair` in [Codec.dart](lib/src/platform/src/codec.dart) so new encoder/decoder is assigned for new type 5. update `writeValue` in [android.src.main.java.io.ably.flutter.plugin.AblyMessageCodec](android/src/main/java/io/ably/flutter/plugin/AblyMessageCodec.java) so new codec type is obtained from runtime type 6. update `codecMap` in [android.src.main.java.io.ably.flutter.plugin.AblyMessageCodec](android/src/main/java/io/ably/flutter/plugin/AblyMessageCodec.java) @@ -94,7 +94,7 @@ Generate platform constants and continue ## Implementing new platform methods -1. Add new method name in `_platformMethods` list at [bin/codegencontext.dart](bin/codegencontext.dart) +1. Add new method name in `_platformMethods` list at [bin/codegen_context.dart](bin/codegen_context.dart) Generate platform constants and use wherever required From 0053a9ba0e8d854ba4e2872dc06f03214819e634 Mon Sep 17 00:00:00 2001 From: Ben Butterworth <24711048+ben-xD@users.noreply.github.com> Date: Mon, 9 Aug 2021 19:22:43 +0100 Subject: [PATCH 12/16] Remove empty file --- lib/src/rest/src/options.dart | 1 - 1 file changed, 1 deletion(-) delete mode 100644 lib/src/rest/src/options.dart diff --git a/lib/src/rest/src/options.dart b/lib/src/rest/src/options.dart deleted file mode 100644 index 8b1378917..000000000 --- a/lib/src/rest/src/options.dart +++ /dev/null @@ -1 +0,0 @@ - From 2a8c97b8c6c65bc005f6ff708d4e54cb024638b9 Mon Sep 17 00:00:00 2001 From: Ben Butterworth <24711048+ben-xD@users.noreply.github.com> Date: Mon, 9 Aug 2021 19:23:51 +0100 Subject: [PATCH 13/16] Rename typo in filename --- ..._internal_granularity.dart => stats_interval_granularity.dart} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename lib/src/stats/{stats_internal_granularity.dart => stats_interval_granularity.dart} (100%) diff --git a/lib/src/stats/stats_internal_granularity.dart b/lib/src/stats/stats_interval_granularity.dart similarity index 100% rename from lib/src/stats/stats_internal_granularity.dart rename to lib/src/stats/stats_interval_granularity.dart From 9405f417392609df9a6c7168d00552f95c2fe832 Mon Sep 17 00:00:00 2001 From: Ben Butterworth <24711048+ben-xD@users.noreply.github.com> Date: Mon, 9 Aug 2021 19:24:36 +0100 Subject: [PATCH 14/16] Reformat file with extra comma --- test/mock_method_call_manager.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/mock_method_call_manager.dart b/test/mock_method_call_manager.dart index 7c7f73802..f88d7ce8b 100644 --- a/test/mock_method_call_manager.dart +++ b/test/mock_method_call_manager.dart @@ -68,9 +68,9 @@ class MockMethodCallManager { // PlatformChannel. if (!isAuthenticated && clientOptions.authUrl == 'hasAuthCallback') { await AblyMethodCallHandler(Platform.methodChannel) - .onRealtimeAuthCallback(AblyMessage( - TokenParams(timestamp: DateTime.now()), - handle: handle)); + .onRealtimeAuthCallback( + AblyMessage(TokenParams(timestamp: DateTime.now()), handle: handle), + ); isAuthenticated = true; throw PlatformException( code: ErrorCodes.authCallbackFailure.toString(), From 50034c940046ad131ed49624673bc5f2031cd545 Mon Sep 17 00:00:00 2001 From: Ben Butterworth <24711048+ben-xD@users.noreply.github.com> Date: Tue, 10 Aug 2021 08:51:15 +0100 Subject: [PATCH 15/16] Removed file import --- lib/src/rest/rest.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/src/rest/rest.dart b/lib/src/rest/rest.dart index 423953914..9e46d5d52 100644 --- a/lib/src/rest/rest.dart +++ b/lib/src/rest/rest.dart @@ -1,6 +1,5 @@ export 'src/channel_options.dart'; export 'src/channels.dart'; -export 'src/options.dart'; export 'src/presence.dart'; export 'src/rest.dart'; export 'src/rest_history_params.dart'; From bb99e088ee51eb8ef8d75aa97e9851d0f8094167 Mon Sep 17 00:00:00 2001 From: Ben Butterworth <24711048+ben-xD@users.noreply.github.com> Date: Tue, 10 Aug 2021 08:51:35 +0100 Subject: [PATCH 16/16] Use Ably developer team in Xcode project --- example/ios/Podfile.lock | 2 +- example/ios/Runner.xcodeproj/project.pbxproj | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 7afcfe760..6ee4539f9 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -33,7 +33,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Ably: 863d35bb6a6aa5fc537304ef19f85cfa51d1bbde - ably_flutter: b37439c7b63f4b3e44f4674c5bfb56452d193ad0 + ably_flutter: 31894faaa659febdd3a57cdc03c1fc2c218d2c95 AblyDeltaCodec: 6123f31df5b04a0f5452968505a46ba16a9eb689 Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c msgpack: a14de9216d29cfd0a7aff5af5150601a27e899a4 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 0038c0985..8018149b1 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -171,7 +171,7 @@ TargetAttributes = { 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; - DevelopmentTeam = S38X8U35U5; + DevelopmentTeam = XXY98AVDR6; }; }; }; @@ -377,7 +377,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = S38X8U35U5; + DEVELOPMENT_TEAM = XXY98AVDR6; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -506,7 +506,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = S38X8U35U5; + DEVELOPMENT_TEAM = XXY98AVDR6; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -530,7 +530,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = S38X8U35U5; + DEVELOPMENT_TEAM = XXY98AVDR6; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)",