diff --git a/.pubnub.yml b/.pubnub.yml index 7a42d8fc..99b2eb19 100644 --- a/.pubnub.yml +++ b/.pubnub.yml @@ -1,5 +1,24 @@ --- changelog: + - + changes: + - + text: "Subscribe loop is now written using async generator and should be easier to debug." + type: improvement + - + text: "Exports now are more comprehensive and clear, documentation clarity has been improved." + type: improvement + - + text: "Removes additional query params from AWS calls." + type: bug + - + text: "Fixes a bunch of issues with incorrect arguments passed in." + type: bug + - + text: "Adds additional diagnostics to the networking module." + type: bug + date: Oct 8, 20 + version: v3.0.0 - changes: - @@ -310,4 +329,4 @@ supported-platforms: platforms: - "Dart SDK >=2.6.0 <3.0.0" version: "PubNub Dart SDK" -version: "2.0.1" +version: "3.0.0" diff --git a/CHANGELOG.md b/CHANGELOG.md index 176e4eb6..a306e1a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +## [v3.0.0](https://github.com/pubnub/dart/releases/tag/v3.0.0) +October 8 2020 + +[Full Changelog](https://github.com/pubnub/dart/compare/v2.0.1...v3.0.0) + +- ⭐️️ Subscribe loop is now written using async generator and should be easier to debug. +- ⭐️️ Exports now are more comprehensive and clear, documentation clarity has been improved. +- 🐛 Removes additional query params from AWS calls. +- 🐛 Fixes a bunch of issues with incorrect arguments passed in. +- 🐛 Adds additional diagnostics to the networking module. + ## [v2.0.1](https://github.com/pubnub/dart/releases/tag/v2.0.1) September 7 2020 diff --git a/README.md b/README.md index 0a4cb34d..82971830 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ You can import it in one of two ways: ```dart // Import all PubNub objects into your namespace -import 'package:pubnub/pubnub.dart'; +import 'package:pubnub/core.dart'; // Or import PubNub into a named namespace import 'package:pubnub/pubnub.dart' as pn; diff --git a/analysis_options.yaml b/analysis_options.yaml index 39cc08c2..e6de7709 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -2,4 +2,4 @@ include: package:pedantic/analysis_options.yaml analyzer: errors: - # todo: ignore \ No newline at end of file + todo: ignore \ No newline at end of file diff --git a/dartdoc_options.yaml b/dartdoc_options.yaml new file mode 100644 index 00000000..cc38ef3e --- /dev/null +++ b/dartdoc_options.yaml @@ -0,0 +1,29 @@ +dartdoc: + exclude: ["core"] + categories: + "Basic Features": + markdown: "doc/Basic.md" + name: "Basic Features" + "Objects": + markdown: "doc/Objects.md" + name: "Objects" + "Files": + markdown: "doc/Files.md" + name: "Files" + "Push": + markdown: "doc/Push.md" + name: "Push" + "Access Manager": + markdown: "doc/AccessManager.md" + name: "Access Manager" + "Exceptions": + markdown: "doc/Exceptions.md" + name: "Exceptions" + "Results": + markdown: "doc/Results.md" + name: "Results" + "Modules": + name: "Modules" + categoryOrder: ["Basic Features", "Objects", "Files", "Push", "Access Manager", "Exceptions", "Results"] + examplePathPrefix: "./examples" + showUndocumentedCategories: true diff --git a/doc/AccessManager.md b/doc/AccessManager.md new file mode 100644 index 00000000..e69de29b diff --git a/doc/Basic.md b/doc/Basic.md new file mode 100644 index 00000000..e69de29b diff --git a/doc/Exceptions.md b/doc/Exceptions.md new file mode 100644 index 00000000..e69de29b diff --git a/doc/Files.md b/doc/Files.md new file mode 100644 index 00000000..e69de29b diff --git a/doc/Objects.md b/doc/Objects.md new file mode 100644 index 00000000..e69de29b diff --git a/doc/Push.md b/doc/Push.md new file mode 100644 index 00000000..e69de29b diff --git a/doc/Results.md b/doc/Results.md new file mode 100644 index 00000000..e69de29b diff --git a/example/logging.dart b/example/logging.dart index 1c232eef..85054822 100644 --- a/example/logging.dart +++ b/example/logging.dart @@ -1,9 +1,10 @@ import 'package:pubnub/pubnub.dart'; +import 'package:pubnub/logging.dart'; // This is the same example as the `example.dart` file, but shows how to enable extra logging void main() async { // Create a root logger - var logger = StreamLogger.root('root', logLevel: Level.all); + var logger = StreamLogger.root('root', logLevel: Level.info); // Subscribe to messages with a default printer var sub = logger.stream.listen( @@ -17,6 +18,8 @@ void main() async { var _ = await provideLogger(logger, () async { var subscription = await pubnub.subscribe(channels: {'test'}); + await Future.delayed(Duration(seconds: 1)); + await pubnub.publish('test', {'message': 'My message'}); print(await subscription.messages.first); @@ -43,5 +46,4 @@ void main() async { await sub.cancel(); await logger.dispose(); - print('disposed!'); } diff --git a/example/supervisor.dart b/example/supervisor.dart index 52d404db..274761f3 100644 --- a/example/supervisor.dart +++ b/example/supervisor.dart @@ -1,6 +1,5 @@ -import 'dart:io'; - import 'package:pubnub/pubnub.dart'; +import 'package:pubnub/logging.dart'; import 'package:pubnub/networking.dart'; void main() async { @@ -10,26 +9,26 @@ void main() async { logger.stream.listen( LogRecord.createPrinter(r'[$time] (${level.name}) $scope $message')); - await provideLogger(logger, () async { - // Create PubNub instance with default keyset. - var pubnub = PubNub( - networking: NetworkingModule( - retryPolicy: RetryPolicy.exponential(maxRetries: 10)), - defaultKeyset: - Keyset(subscribeKey: 'demo', publishKey: 'demo', uuid: UUID('demo')), - ); + // Create PubNub instance with default keyset. + var pubnub = PubNub( + networking: + NetworkingModule(retryPolicy: RetryPolicy.exponential(maxRetries: 10)), + defaultKeyset: + Keyset(subscribeKey: 'demo', publishKey: 'demo', uuid: UUID('demo')), + ); - print( - 'Network reconnection test. Please wait few seconds for further instructions...'); + print( + 'Network reconnection test. Please wait few seconds for further instructions...'); - var sub = await pubnub.subscribe(channels: {'test2'}); + var sub = await pubnub.subscribe(channels: {'test2'}); - await Future.delayed(Duration(seconds: 5)); + await Future.delayed(Duration(seconds: 5)); - print('Subscribed. Disconnect your network for few seconds.'); + print('Subscribed. Disconnect your network for few seconds.'); - await Future.delayed(Duration(seconds: 5)); + await Future.delayed(Duration(seconds: 5)); + await provideLogger(logger, () async { var f = pubnub.publish('test2', {'myMessage': 'it works!'}); print( @@ -44,7 +43,5 @@ void main() async { await sub.dispose(); print('Done!'); - - exit(0); }); } diff --git a/lib/core.dart b/lib/core.dart new file mode 100644 index 00000000..dc7b56f4 --- /dev/null +++ b/lib/core.dart @@ -0,0 +1,14 @@ +export 'src/core/core.dart'; + +export 'src/core/keyset.dart'; +export 'src/core/endpoint.dart'; +export 'src/core/message_type.dart'; +export 'src/core/exceptions.dart'; +export 'src/core/timetoken.dart'; +export 'src/core/uuid.dart'; + +export 'src/core/crypto/crypto.dart'; +export 'src/core/logging/logging.dart'; +export 'src/core/net/net.dart'; +export 'src/core/supervisor/supervisor.dart'; +export 'src/core/parser.dart'; diff --git a/lib/crypto.dart b/lib/crypto.dart new file mode 100644 index 00000000..59783dd5 --- /dev/null +++ b/lib/crypto.dart @@ -0,0 +1,10 @@ +/// Default cryptography module used by PubNub SDK. +/// +/// Uses `package:crypto` and `package:encrypt` under the hood. +/// +/// {@category Modules} +library pubnub.crypto; + +export 'src/crypto/crypto.dart' show CryptoConfiguration, CryptoModule; +export 'src/crypto/encryption_mode.dart' + show EncryptionMode, EncryptionModeExtension; diff --git a/lib/logging.dart b/lib/logging.dart new file mode 100644 index 00000000..69f8aeab --- /dev/null +++ b/lib/logging.dart @@ -0,0 +1,7 @@ +/// Default logging module used by PubNub SDK. +/// +/// {@category Modules} +library pubnub.logging; + +export 'src/logging/logging.dart' show LogRecord, StreamLogger; +export 'src/core/logging/logging.dart' show Level, provideLogger, injectLogger; diff --git a/lib/networking.dart b/lib/networking.dart index 99677154..d71ee59f 100644 --- a/lib/networking.dart +++ b/lib/networking.dart @@ -1,2 +1,10 @@ -export 'src/net/net.dart'; -export 'src/net/meta/meta.dart'; +/// Default networking module used by PubNub SDK. +/// +/// Uses `package:dio` under the hood. +/// +/// {@category Modules} +library pubnub.networking; + +export 'src/net/net.dart' show NetworkingModule; +export 'src/net/meta/meta.dart' + show ExponentialRetryPolicy, LinearRetryPolicy, RetryPolicy; diff --git a/lib/pubnub.dart b/lib/pubnub.dart index 5dc0d362..c7b9eb55 100644 --- a/lib/pubnub.dart +++ b/lib/pubnub.dart @@ -4,44 +4,101 @@ /// The best starting point to take a look around is the [PubNub] class that combines all available features. library pubnub; -export './src/core/core.dart'; -export './src/dx/_utils/ensure.dart' show InvariantException; -export './src/crypto/crypto.dart' show CryptoModule, CryptoConfiguration; +// PubNub +export 'src/default.dart'; -export './src/crypto/encryption_mode.dart' - show EncryptionMode, EncryptionModeExtension; - -export './src/dx/files/files.dart' show FileDx; -export './src/dx/batch/batch.dart' show BatchDx; -export './src/dx/objects/objects.dart' show ObjectsDx; -export './src/dx/channel/channel_group.dart' show ChannelGroup, ChannelGroupDx; +// Core +export 'src/core/timetoken.dart' show Timetoken, TimetokenDateTimeExtentions; +export 'src/core/uuid.dart' show UUID; +export 'src/core/keyset.dart' show Keyset, KeysetStore, KeysetException; +export 'src/core/message_type.dart' show MessageType; +export 'src/core/exceptions.dart' + show + InvalidArgumentsException, + MaximumRetriesException, + MalformedResponseException, + MethodDisabledException, + NotImplementedException, + PubNubException, + PublishException, + UnknownException; -export './src/dx/channel/channel.dart' show Channel; -export './src/dx/push/push.dart' show Device; -export './src/dx/_endpoints/publish.dart' show PublishResult; -export 'src/dx/_endpoints/files.dart' +// DX +export 'src/dx/_utils/utils.dart' show InvariantException; +export 'src/dx/batch/batch.dart' + show + BatchDx, + BatchHistoryResult, + BatchHistoryResultEntry, + CountMessagesResult; +export 'src/dx/channel/channel.dart' + show + Channel, + ChannelHistory, + ChannelHistoryOrder, + Message, + PaginatedChannelHistory; +export 'src/dx/channel/channel_group.dart' show + ChannelGroupDx, + ChannelGroupChangeChannelsResult, + ChannelGroupDeleteResult, + ChannelGroupListChannelsResult; +export 'src/dx/files/files.dart' + show + FileDx, + FileInfo, + FileMessage, + FileKeysetExtension, PublishFileMessageResult, - ListFilesResult, DeleteFileResult, - FileDetail, - DownloadFileResult; -export './src/dx/_endpoints/presence.dart' - show HeartbeatResult, LeaveResult, HereNowResult, StateInfo; -export './src/dx/_endpoints/channel_group.dart' + DownloadFileResult, + ListFilesResult, + FileDetail; +export 'src/dx/message_action/message_action.dart' show - ChannelGroupChangeChannelsResult, - ChannelGroupListChannelsResult, - ChannelGroupDeleteResult; -export './src/dx/_endpoints/history.dart' + FetchMessageActionsResult, + AddMessageActionResult, + DeleteMessageActionResult, + MessageAction; +export 'src/dx/objects/objects.dart' show - FetchHistoryResult, - BatchHistoryResult, - BatchHistoryResultEntry, - CountMessagesResult, - DeleteMessagesResult; -export './src/dx/_endpoints/signal.dart' show SignalResult; -export './src/dx/_endpoints/push.dart' + ObjectsDx, + ChannelIdInfo, + ChannelMemberMetadata, + ChannelMemberMetadataInput, + ChannelMembersResult, + ChannelMetadataDetails, + ChannelMetadataInput, + GetAllChannelMetadataResult, + GetAllUuidMetadataResult, + GetChannelMetadataResult, + GetUuidMetadataResult, + MembershipMetadata, + MembershipMetadataInput, + MembershipsResult, + RemoveChannelMetadataResult, + RemoveUuidMetadataResult, + SetChannelMetadataResult, + SetUuidMetadataResult, + UuIdInfo, + UuidMetadataDetails, + UuidMetadataInput; +export 'src/dx/pam/pam.dart' + show Resource, ResourceType, ResourceTypeExtension, Token, TokenRequest; +export 'src/dx/presence/presence.dart' + show + GetUserStateResult, + HeartbeatResult, + HereNowResult, + LeaveResult, + SetUserStateResult, + WhereNowResult, + ChannelOccupancy, + StateInfo, + PresenceKeysetExtension; +export 'src/dx/publish/publish.dart' show PublishResult; +export 'src/dx/push/push.dart' show PushGateway, PushGatewayExtension, @@ -50,31 +107,14 @@ export './src/dx/_endpoints/push.dart' AddPushChannelsResult, ListPushChannelsResult, RemoveDeviceResult, - RemovePushChannelsResult; -export './src/dx/_endpoints/message_action.dart' - show - MessageAction, - FetchMessageActionsResult, - AddMessageActionResult, - DeleteMessageActionResult; - -export './src/dx/subscribe/subscription.dart' show Subscription; -export './src/dx/subscribe/extensions/keyset.dart' - show SubscribeKeysetExtension, PresenceKeysetExtension; -export './src/dx/subscribe/envelope.dart' - show Envelope, PresenceAction, PresenceEvent, MessageType; -export './src/dx/channel/channel_history.dart' - show PaginatedChannelHistory, ChannelHistory; -export 'src/dx/files/files.dart' show FileInfo, FileMessage; -export './src/dx/channel/message.dart' show Message; - -export './src/dx/_endpoints/objects/objects_types.dart'; - -export './src/dx/pam/pam.dart' - show Resource, ResourceType, ResourceTypeExtension, TokenRequest, Token; - -export './src/dx/objects/objects_types.dart'; + RemovePushChannelsResult, + Device; +export 'src/dx/signal/signal.dart' show SignalResult; +export 'src/dx/supervisor/supervisor.dart' show Signals; -export './src/logging/logging.dart' show StreamLogger, LogRecord; +// Subscribe -export './src/default.dart'; +export 'src/subscribe/subscription.dart' show Subscription; +export 'src/subscribe/extensions/keyset.dart' show SubscribeKeysetExtension; +export 'src/subscribe/envelope.dart' + show Envelope, PresenceEvent, PresenceAction; diff --git a/lib/src/core/core.dart b/lib/src/core/core.dart index 2437a3ad..13978210 100644 --- a/lib/src/core/core.dart +++ b/lib/src/core/core.dart @@ -6,27 +6,23 @@ import 'crypto/crypto.dart'; import 'supervisor/supervisor.dart'; import 'keyset.dart'; -export 'net/net.dart'; -export 'parser.dart'; -export 'logging/logging.dart'; -export 'crypto/crypto.dart'; -export 'supervisor/supervisor.dart'; -export 'keyset.dart'; -export 'endpoint.dart'; -export 'uuid.dart'; -export 'timetoken.dart'; -export 'exceptions.dart'; - class Core { - /// Allows to have multiple [Keyset] associated with one instance of [PubNub]. + /// Allows to have multiple [Keyset] associated with one instance. KeysetStore keysets = KeysetStore(); + /// Internal module responsible for networking. INetworkingModule networking; + + /// Internal module responsible for parsing. IParserModule parser; + + /// Internal module responsible for cryptography. ICryptoModule crypto; + + /// Internal module responsible for supervising. SupervisorModule supervisor = SupervisorModule(); - static String version = '2.0.1'; + static String version = '3.0.0'; Core( {Keyset defaultKeyset, diff --git a/lib/src/core/crypto/cipher_key.dart b/lib/src/core/crypto/cipher_key.dart index 2954002c..478b699f 100644 --- a/lib/src/core/crypto/cipher_key.dart +++ b/lib/src/core/crypto/cipher_key.dart @@ -1,5 +1,4 @@ import 'dart:convert' show base64, utf8; - import 'package:convert/convert.dart' show hex; class CipherKey { diff --git a/lib/src/core/crypto/crypto.dart b/lib/src/core/crypto/crypto.dart index 63dcaa55..e4bed5ff 100644 --- a/lib/src/core/crypto/crypto.dart +++ b/lib/src/core/crypto/crypto.dart @@ -4,10 +4,14 @@ import 'cipher_key.dart'; export 'cipher_key.dart'; +/// Exception thrown when encryption or decryption fails. +/// +/// {@category Exceptions} class CryptoException extends PubNubException { CryptoException([String message]) : super(message); } +/// @nodoc abstract class ICryptoModule { void register(Core core); diff --git a/lib/src/core/endpoint.dart b/lib/src/core/endpoint.dart index 6d650ff5..c3d9fc6d 100644 --- a/lib/src/core/endpoint.dart +++ b/lib/src/core/endpoint.dart @@ -1,7 +1,13 @@ import 'net/net.dart'; +/// Represents all the data necessary to make a request to the API. +/// +/// @nodoc abstract class Parameters { Request toRequest(); } +/// Represents the response from an API. +/// +/// @nodoc abstract class Result {} diff --git a/lib/src/core/exceptions.dart b/lib/src/core/exceptions.dart index 09b287d5..944c8fbd 100644 --- a/lib/src/core/exceptions.dart +++ b/lib/src/core/exceptions.dart @@ -1,3 +1,6 @@ +/// An exception thrown by the PubNub SDK. +/// +/// {@category Exceptions} class PubNubException implements Exception { String message; StackTrace stackTrace; @@ -12,10 +15,23 @@ class PubNubException implements Exception { } } +/// An exception thrown when a disabled API has been requested. +/// +/// {@category Exceptions} class MethodDisabledException extends PubNubException { MethodDisabledException(String message) : super(message); } +/// An exception thrown when some argument is invalid. +/// +/// This may be due to: +/// - an invalid subscribe key. +/// - missing or invalid timetoken or channelsTimetoken (values must be greater than 0). +/// - mismatched number of channels and timetokens. +/// - invalid characters in a channel name. +/// - other invalid request data. +/// +/// {@category Exceptions} class InvalidArgumentsException extends PubNubException { static final String _message = '''Invalid Arguments. This may be due to: - an invalid subscribe key, @@ -27,12 +43,18 @@ class InvalidArgumentsException extends PubNubException { InvalidArgumentsException() : super(_message); } +/// An exception thrown when something unexpected happens in the SDK. +/// +/// {@category Exceptions} class UnknownException extends PubNubException { static final String _message = 'An unknown error has occurred'; UnknownException() : super(_message); } +/// An exception thrown when the API has returned an unexpected response. +/// +/// {@category Exceptions} class MalformedResponseException extends PubNubException { static final String _message = 'Endpoint has returned unforeseen or malformed response'; @@ -40,16 +62,25 @@ class MalformedResponseException extends PubNubException { MalformedResponseException() : super(_message); } +/// An exception thrown when a method is not yet implemented. +/// +/// {@category Exceptions} class NotImplementedException extends PubNubException { static final String _message = 'This feature is not yet implemented'; NotImplementedException() : super(_message); } +/// An exception thrown when publish fails. +/// +/// {@category Exceptions} class PublishException extends PubNubException { PublishException(String message) : super(message); } +/// An exception thrown when maximum amount of retries has been reached. +/// +/// {@category Exceptions} class MaximumRetriesException extends PubNubException { static final String _message = 'Maximum number of retries has been reached.'; diff --git a/lib/src/core/keyset.dart b/lib/src/core/keyset.dart index 8817b7c4..7ff9d6be 100644 --- a/lib/src/core/keyset.dart +++ b/lib/src/core/keyset.dart @@ -4,20 +4,27 @@ import 'exceptions.dart'; import 'crypto/cipher_key.dart'; import 'uuid.dart'; +/// An exception that happens during keyset creation or resolution. +/// +/// {@category Exceptions} class KeysetException extends PubNubException { KeysetException(String message) : super(message); } +/// Represents a collection of [Keyset]. +/// +/// Keyset names must be unique and only one of them can be default. class KeysetStore { final Map _store = {}; String _defaultName; - /// Returns a list of all keysets in this store + /// Returns a list of all keysets in this store. List get keysets => _store.values.toList(); /// Adds a [keyset] named [name] to the store. /// /// If [useAsDefault] is true, then it will be used as a default keyset. + /// If a default keyset already exists, it will no longer be the default. Keyset add(Keyset keyset, {@required String name, bool useAsDefault = false}) { if (_store.containsKey(name)) { @@ -32,6 +39,7 @@ class KeysetStore { return keyset; } + /// Removes a keyset from this store. Keyset remove(String name) { if (name == _defaultName) { _defaultName = null; @@ -94,6 +102,7 @@ class KeysetStore { } } + /// Convinience method that returns a keyset based on the [keyset] and [using] combination. Keyset obtain(Keyset keyset, String using) { keyset ??= get(using, defaultIfNameIsNull: true); @@ -102,6 +111,8 @@ class KeysetStore { } /// Represents a configuration for a given subscribe key. +/// +/// {@category Basic Features} class Keyset { /// Subscribe key. final String subscribeKey; diff --git a/lib/src/core/logging/dummy_logger.dart b/lib/src/core/logging/dummy_logger.dart deleted file mode 100644 index 7a0bbe74..00000000 --- a/lib/src/core/logging/dummy_logger.dart +++ /dev/null @@ -1,9 +0,0 @@ -import 'logging.dart'; - -class DummyLogger extends ILogger { - @override - DummyLogger get(String scope) => this; - - @override - void log(int level, message) {} -} diff --git a/lib/src/core/logging/logging.dart b/lib/src/core/logging/logging.dart index 07688922..469a5826 100644 --- a/lib/src/core/logging/logging.dart +++ b/lib/src/core/logging/logging.dart @@ -1,7 +1,5 @@ import 'dart:async'; -import 'dummy_logger.dart'; - final _pubnubLoggerModuleKey = #pubnub.logging; /// Provides a [logger] to the code inside [body]. @@ -12,6 +10,16 @@ Future provideLogger(ILogger logger, Future Function() body) async { return result; } +/// @nodoc +class DummyLogger extends ILogger { + @override + DummyLogger get(String scope) => this; + + @override + void log(int level, message) {} +} + +/// @nodoc class LazyLogger implements ILogger { final String _id; final ILogger Function() _obtainLogger; diff --git a/lib/src/core/message_type.dart b/lib/src/core/message_type.dart new file mode 100644 index 00000000..0c0c1779 --- /dev/null +++ b/lib/src/core/message_type.dart @@ -0,0 +1,23 @@ +/// Represents the type of a message. +/// +/// {@category Basic Features} +enum MessageType { normal, signal, objects, messageAction, file } + +/// @nodoc +extension MessageTypeExtension on MessageType { + static MessageType fromInt(int messageType) => const { + null: MessageType.normal, + 1: MessageType.signal, + 2: MessageType.objects, + 3: MessageType.messageAction, + 4: MessageType.file + }[messageType]; + + int toInt() => const { + MessageType.normal: null, + MessageType.signal: 1, + MessageType.objects: 2, + MessageType.messageAction: 3, + MessageType.file: 4, + }[this]; +} diff --git a/lib/src/core/net/exceptions.dart b/lib/src/core/net/exceptions.dart index 8ba1d4ac..94af458e 100644 --- a/lib/src/core/net/exceptions.dart +++ b/lib/src/core/net/exceptions.dart @@ -1,4 +1,5 @@ -import 'package:pubnub/pubnub.dart'; +import '../exceptions.dart'; +import 'response.dart'; class PubNubRequestTimeoutException extends PubNubException { dynamic additionalData; diff --git a/lib/src/core/net/net.dart b/lib/src/core/net/net.dart index 5667e707..cd40bf5b 100644 --- a/lib/src/core/net/net.dart +++ b/lib/src/core/net/net.dart @@ -1,16 +1,11 @@ import '../core.dart'; import 'request_handler.dart'; -export 'request.dart' show Request; -export 'request_type.dart' show RequestType, RequestTypeExtension; -export 'request_handler.dart' show IRequestHandler; -export 'response.dart' show IResponse; -export 'exceptions.dart' - show - PubNubRequestCancelException, - PubNubRequestFailureException, - PubNubRequestOtherException, - PubNubRequestTimeoutException; +export 'request.dart'; +export 'request_type.dart'; +export 'request_handler.dart'; +export 'response.dart'; +export 'exceptions.dart'; abstract class INetworkingModule { void register(Core core); diff --git a/lib/src/core/net/request.dart b/lib/src/core/net/request.dart index acd6fd02..256c6551 100644 --- a/lib/src/core/net/request.dart +++ b/lib/src/core/net/request.dart @@ -1,5 +1,6 @@ import 'request_type.dart'; +/// @nodoc class Request { RequestType type; Uri uri; diff --git a/lib/src/core/net/request_handler.dart b/lib/src/core/net/request_handler.dart index 2b8d3f7c..eb9ec0b5 100644 --- a/lib/src/core/net/request_handler.dart +++ b/lib/src/core/net/request_handler.dart @@ -21,7 +21,7 @@ abstract class IRequestHandler { path: requestUri.path != '' ? requestUri.path : null, port: requestUri.hasPort ? requestUri.port : defaultUri.port, queryParameters: { - ...defaultUri.queryParameters, + if (requestUri.host == '') ...defaultUri.queryParameters, ...requestUri.queryParameters }, userInfo: requestUri.userInfo); diff --git a/lib/src/core/net/request_type.dart b/lib/src/core/net/request_type.dart index b49363c8..99bdf577 100644 --- a/lib/src/core/net/request_type.dart +++ b/lib/src/core/net/request_type.dart @@ -1,3 +1,4 @@ +/// @nodoc enum RequestType { get, post, patch, subscribe, delete, file } const _sendTimeoutRequestDefault = { @@ -27,6 +28,7 @@ const _connectTimeoutRequestDefault = { RequestType.file: 30000, }; +/// @nodoc extension RequestTypeExtension on RequestType { static const methods = { RequestType.get: 'GET', diff --git a/lib/src/core/net/response.dart b/lib/src/core/net/response.dart index 8746ff59..0f24e696 100644 --- a/lib/src/core/net/response.dart +++ b/lib/src/core/net/response.dart @@ -1,3 +1,4 @@ +/// @nodoc abstract class IResponse { int get statusCode; Map> get headers; diff --git a/lib/src/core/parser.dart b/lib/src/core/parser.dart index 6ec75f55..9bdc15b7 100644 --- a/lib/src/core/parser.dart +++ b/lib/src/core/parser.dart @@ -1,12 +1,16 @@ import 'core.dart'; import 'exceptions.dart'; +/// Exception thrown when parsing fails. +/// +/// {@category Exceptions} class ParserException extends PubNubException { dynamic originalException; ParserException([String message, this.originalException]) : super(message); } +/// @nodoc abstract class IParserModule { void register(Core core); diff --git a/lib/src/core/supervisor/event.dart b/lib/src/core/supervisor/event.dart index c979bbb8..1f90b1ab 100644 --- a/lib/src/core/supervisor/event.dart +++ b/lib/src/core/supervisor/event.dart @@ -1,5 +1,8 @@ +/// @nodoc abstract class SupervisorEvent {} +/// @nodoc class NetworkIsDownEvent extends SupervisorEvent {} +/// @nodoc class NetworkIsUpEvent extends SupervisorEvent {} diff --git a/lib/src/core/supervisor/fiber.dart b/lib/src/core/supervisor/fiber.dart index 42307252..27ecbb4f 100644 --- a/lib/src/core/supervisor/fiber.dart +++ b/lib/src/core/supervisor/fiber.dart @@ -1,45 +1,24 @@ import 'dart:async'; - import 'package:meta/meta.dart'; import 'package:pedantic/pedantic.dart'; +import '../logging/logging.dart'; import '../core.dart'; +import 'event.dart'; +import 'resolution.dart'; final _logger = injectLogger('pubnub.core.fiber'); -abstract class Resolution { - const Resolution(); - - factory Resolution.fail() => FailResolution(); - factory Resolution.delay(Duration delay) => DelayResolution(delay); - factory Resolution.retry() => RetryResolution(); - factory Resolution.networkStatus(bool isUp) => NetworkStatusResolution(isUp); -} - -class FailResolution extends Resolution {} - -class DelayResolution extends Resolution { - final Duration delay; - - const DelayResolution(this.delay); -} - -class RetryResolution extends Resolution {} - -class NetworkStatusResolution extends Resolution { - final bool isUp; - - const NetworkStatusResolution(this.isUp); -} - -typedef FiberAction = Future Function(); +typedef _FiberAction = Future Function(); class Fiber { static int _id = 0; final int id; final Core _core; - final FiberAction action; + final _FiberAction action; + + final bool isSubscribe = false; final _completer = Completer(); Future get future => _completer.future; diff --git a/lib/src/core/supervisor/resolution.dart b/lib/src/core/supervisor/resolution.dart new file mode 100644 index 00000000..24045314 --- /dev/null +++ b/lib/src/core/supervisor/resolution.dart @@ -0,0 +1,24 @@ +abstract class Resolution { + const Resolution(); + + factory Resolution.fail() => FailResolution(); + factory Resolution.delay(Duration delay) => DelayResolution(delay); + factory Resolution.retry() => RetryResolution(); + factory Resolution.networkStatus(bool isUp) => NetworkStatusResolution(isUp); +} + +class FailResolution extends Resolution {} + +class DelayResolution extends Resolution { + final Duration delay; + + const DelayResolution(this.delay); +} + +class RetryResolution extends Resolution {} + +class NetworkStatusResolution extends Resolution { + final bool isUp; + + const NetworkStatusResolution(this.isUp); +} diff --git a/lib/src/core/supervisor/signals.dart b/lib/src/core/supervisor/signals.dart new file mode 100644 index 00000000..6e6a3702 --- /dev/null +++ b/lib/src/core/supervisor/signals.dart @@ -0,0 +1,50 @@ +import 'dart:async'; + +import 'package:pubnub/core.dart'; +import 'event.dart'; + +final _logger = injectLogger('pubnub.core.supervisor.signals'); + +class Signals { + final StreamController _controller = + StreamController.broadcast(); + Stream get _stream => _controller.stream; + + bool _isNetworkAvailable = true; + + /// @nodoc + void notify(SupervisorEvent event) { + if (_isNetworkAvailable == true && event is NetworkIsDownEvent) { + _logger.verbose('Signaled that network is down.'); + _isNetworkAvailable = false; + + _controller.add(event); + } + + if (_isNetworkAvailable == false && event is NetworkIsUpEvent) { + _logger.verbose('Signaled that network is up.'); + _isNetworkAvailable = true; + + _controller.add(event); + } + } + + /// Will emit `void` when detected that network is down. + Stream get networkIsDown => + _stream.where((event) => event is NetworkIsDownEvent); + + /// Will emit `void` when detected that network is up. + Stream get networkIsUp => + _stream.where((event) => event is NetworkIsUpEvent); + + /// Will emit `bool` whether the network is connected or not when its status changes. + Stream get networkIsConnected => _stream.map((event) { + if (event is NetworkIsUpEvent) { + return true; + } else if (event is NetworkIsDownEvent) { + return false; + } + + return _isNetworkAvailable; + }); +} diff --git a/lib/src/core/supervisor/supervisor.dart b/lib/src/core/supervisor/supervisor.dart index 536c9a0a..53e88e99 100644 --- a/lib/src/core/supervisor/supervisor.dart +++ b/lib/src/core/supervisor/supervisor.dart @@ -1,13 +1,12 @@ -import 'dart:async'; - -import '../core.dart'; import 'event.dart'; import 'fiber.dart'; +import 'resolution.dart'; +import 'signals.dart'; export 'event.dart'; export 'fiber.dart'; - -final _logger = injectLogger('pubnub.core.supervisor'); +export 'resolution.dart'; +export 'signals.dart'; abstract class Diagnostic { const Diagnostic(); @@ -23,12 +22,7 @@ class SupervisorModule { final Set _handlers = {}; final Set _strategies = {}; - final StreamController _events = - StreamController.broadcast(); - - bool _isNetworkUp = true; - - Stream get events => _events.stream; + final Signals signals = Signals(); void registerDiagnostic(DiagnosticHandler handler) { _handlers.add(handler); @@ -38,17 +32,7 @@ class SupervisorModule { _strategies.add(strategy); } - void notify(SupervisorEvent event) { - if (_isNetworkUp && event is NetworkIsDownEvent) { - _isNetworkUp = false; - _logger.warning('Detected that network is down.'); - _events.add(event); - } else if (_isNetworkUp == false && event is NetworkIsUpEvent) { - _isNetworkUp = true; - _logger.warning('Detected that network is up.'); - _events.add(event); - } - } + void notify(SupervisorEvent event) => signals.notify(event); Diagnostic runDiagnostics(Fiber fiber, Exception exception) { return _handlers diff --git a/lib/src/core/timetoken.dart b/lib/src/core/timetoken.dart index 7c7514ea..b84d8b3e 100644 --- a/lib/src/core/timetoken.dart +++ b/lib/src/core/timetoken.dart @@ -1,15 +1,19 @@ import 'endpoint.dart'; -/// Class representing a Timetoken value returned and required by most PubNub APIs. +/// Represents a timetoken value returned and required by most PubNub APIs. +/// +/// {@category Basic Features} class Timetoken implements Result { /// The actual value of the Timetoken. It's a number of nanoseconds since the epoch. final int value; const Timetoken(this.value); + /// Returns a string representation of this Timetoken. @override String toString() => '$value'; + /// Timetokens are compared based on their [value]. @override bool operator ==(dynamic other) { if (other is Timetoken) { @@ -21,20 +25,21 @@ class Timetoken implements Result { /// Converts Timetoken to a [DateTime]. /// - /// Beware, as it drops the granurality to microseconds. + /// Beware, as it drops the granularity to microseconds. DateTime toDateTime() { return DateTime.fromMicrosecondsSinceEpoch((value / 10).round()); } /// Creates a Timetoken from [DateTime]. /// - /// Beware, as the maximum granurality of the timestamp obtained this + /// Beware, as the maximum granularity of the timestamp obtained this /// way is a microsecond. factory Timetoken.fromDateTime(DateTime dateTime) { return Timetoken(dateTime.microsecondsSinceEpoch * 10); } } +/// @nodoc extension TimetokenDateTimeExtentions on DateTime { /// Convert DateTime to Timetoken. Timetoken toTimetoken() { diff --git a/lib/src/core/uuid.dart b/lib/src/core/uuid.dart index 41f37b11..e95275ab 100644 --- a/lib/src/core/uuid.dart +++ b/lib/src/core/uuid.dart @@ -1,9 +1,11 @@ -/// Class representing an UUID. +/// Represents an UUID. +/// +/// {@category Basic Features} class UUID { /// The actual value of an UUID. final String value; - /// Some state that may be associated with this UUID. It is used + /// State that may be associated with this UUID. It is used /// in certain contexts. final Map state; diff --git a/lib/src/crypto/crypto.dart b/lib/src/crypto/crypto.dart index 2372880b..3658bc58 100644 --- a/lib/src/crypto/crypto.dart +++ b/lib/src/crypto/crypto.dart @@ -3,12 +3,18 @@ import 'package:crypto/crypto.dart' show sha256; import 'dart:convert' show base64, utf8; import 'dart:typed_data' show Uint8List; -import '../core/core.dart'; +import 'package:pubnub/core.dart'; import 'encryption_mode.dart'; +/// Configuration used in cryptography. class CryptoConfiguration { + /// Encryption mode used. final EncryptionMode encryptionMode; + + /// Whether key should be encrypted. final bool encryptKey; + + /// Whether a random IV should be used. final bool useRandomInitializationVector; const CryptoConfiguration( @@ -17,9 +23,14 @@ class CryptoConfiguration { this.useRandomInitializationVector = false}); } +/// Default cryptography module used in PubNub SDK. class CryptoModule implements ICryptoModule { final CryptoConfiguration defaultConfiguration; + /// Default configuration is: + /// * `encryptionMode` set to [EncryptionMode.CBC]. + /// * `encryptKey` set to `true`. + /// * `useRandomInitializationVector` set to `false`. CryptoModule({this.defaultConfiguration = const CryptoConfiguration()}); crypto.Key _getKey(CipherKey cipherKey, CryptoConfiguration configuration) { @@ -31,6 +42,9 @@ class CryptoModule implements ICryptoModule { } } + /// Decrypts [input] with [key] based on [configuration]. + /// + /// If [configuration] is `null`, then [CryptoModule.defaultConfiguration] is used. @override dynamic decrypt(CipherKey key, String input, {CryptoConfiguration configuration}) { @@ -53,6 +67,9 @@ class CryptoModule implements ICryptoModule { } } + /// Encrypts [input] with [key] based on [configuration]. + /// + /// If [configuration] is `null`, then [CryptoModule.defaultConfiguration] is used. @override String encrypt(CipherKey key, dynamic input, {CryptoConfiguration configuration}) { @@ -75,6 +92,9 @@ class CryptoModule implements ICryptoModule { } } + /// Decrypts [input] based on the [key] and [configuration]. + /// + /// If [configuration] is `null`, then [CryptoModule.defaultConfiguration] is used. @override List decryptFileData(CipherKey key, List input, {CryptoConfiguration configuration}) { @@ -90,6 +110,9 @@ class CryptoModule implements ICryptoModule { } } + /// Encrypts [input] based on the [key] and [configuration]. + /// + /// If [configuration] is `null`, then [CryptoModule.defaultConfiguration] is used. @override List encryptFileData(CipherKey key, List input, {CryptoConfiguration configuration}) { @@ -106,6 +129,7 @@ class CryptoModule implements ICryptoModule { } } + /// @nodoc @override void register(Core core) {} } diff --git a/lib/src/crypto/encryption_mode.dart b/lib/src/crypto/encryption_mode.dart index 00a8ff89..b620f2f8 100644 --- a/lib/src/crypto/encryption_mode.dart +++ b/lib/src/crypto/encryption_mode.dart @@ -1,7 +1,9 @@ import 'package:encrypt/encrypt.dart' show AESMode; +/// Encryption mode of AES used in crypto. enum EncryptionMode { CBC, ECB } +/// @nodoc extension EncryptionModeExtension on EncryptionMode { AESMode value() { switch (this) { diff --git a/lib/src/default.dart b/lib/src/default.dart index 3207be89..4c00990b 100644 --- a/lib/src/default.dart +++ b/lib/src/default.dart @@ -1,12 +1,12 @@ -import 'core/core.dart'; +import '../core.dart'; import 'net/net.dart'; import 'parser/parser.dart'; import 'crypto/crypto.dart'; +import 'subscribe/subscribe.dart'; import 'dx/time.dart'; import 'dx/publish/publish.dart'; -import 'dx/subscribe/subscribe.dart'; import 'dx/signal/signal.dart'; import 'dx/batch/batch.dart'; import 'dx/channel/channel.dart'; @@ -18,6 +18,7 @@ import 'dx/presence/presence.dart'; import 'dx/files/files.dart'; import 'dx/objects/objects_types.dart'; import 'dx/objects/objects.dart'; +import 'dx/supervisor/supervisor.dart'; /// PubNub library. /// @@ -36,6 +37,8 @@ import 'dx/objects/objects.dart'; /// * if `keyset` is null, it will try to obtain a keyset named `using`, /// * if `using` is null, it will try to obtain the default keyset, /// * if default keyset is not defined, it will throw an error. +/// +/// {@category Basic Features} class PubNub extends Core with TimeDx, @@ -45,19 +48,19 @@ class PubNub extends Core MessageActionDx, PushNotificationDx, PamDx, - PresenceDx { - /// [BatchDx] contains methods that allow running batch operations on channels, + PresenceDx, + SupervisorDx { + /// Contains methods that allow running batch operations on channels, /// channel groups and other features. BatchDx batch; - /// [ChannelGroupDx] contains methods that allow manipulating channel groups. + /// Contains methods that allow manipulating channel groups. ChannelGroupDx channelGroups; - /// [ObjectsDx] contains methods to manage channel, uuid metadata and - /// UUID's membership and Channel's members + /// Contains methods to manage channel and uuids metadata and their relationships. ObjectsDx objects; - /// [FileDx] contains that allow managing files + /// Contains methods that allow managing files. FileDx files; /// Current version of this library. @@ -81,25 +84,16 @@ class PubNub extends Core /// Returns a representation of a channel. /// - /// Useful if you only need to work on one channel. + /// Useful if you need to work on only one channel. Channel channel(String name, {Keyset keyset, String using}) { keyset ??= keysets.get(using, defaultIfNameIsNull: true); return Channel(this, keyset, name); } - /// Returns a representation of a channel group. + /// Creates [UUIDMetadata] and sets metadata for given [uuid] in the database. /// - /// Useful if you need to work on a bunch of channels at the same time. - ChannelGroup channelGroup(String name, {Keyset keyset, String using}) { - keyset ??= keysets.get(using, defaultIfNameIsNull: true); - - return ChannelGroup(this, keyset, name); - } - - /// Creates [UUIDMetadata], sets metadata for given `uuid` to the database - /// * If `uuid` argument is null then it picks `uuid` of `keyset` - /// Returned [UUIDMetadata] instance is further useful to manage it's membership metadata + /// If [uuid] is null, then it uses [Keyset.uuid]. Future uuidMetadata( {String uuid, String name, @@ -121,12 +115,12 @@ class PubNub extends Core uuid: uuid, keyset: keyset); if (result.metadata != null) { - uuidMetadata = UUIDMetadata(this, keyset, result.metadata.id); + uuidMetadata = UUIDMetadata(objects, keyset, result.metadata.id); } return uuidMetadata; } - /// Creates and returns a new instance of [ChannelMetadata] (from Objects API). + /// Creates [ChannelMetadata] and sets metadata for given [channel] in the database. Future channelMetadata(String channelId, {String name, String description, @@ -143,7 +137,7 @@ class PubNub extends Core keyset: keyset); if (result.metadata != null) { - channelMetadata = ChannelMetadata(this, keyset, result.metadata.id); + channelMetadata = ChannelMetadata(objects, keyset, result.metadata.id); } return channelMetadata; } diff --git a/lib/src/dx/_endpoints/channel_group.dart b/lib/src/dx/_endpoints/channel_group.dart index 8807d116..ae43244e 100644 --- a/lib/src/dx/_endpoints/channel_group.dart +++ b/lib/src/dx/_endpoints/channel_group.dart @@ -1,4 +1,4 @@ -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; import 'package:pubnub/src/dx/_utils/utils.dart'; class ChannelGroupListChannelsParams extends Parameters { @@ -25,10 +25,17 @@ class ChannelGroupListChannelsParams extends Parameters { } } +/// Result of list channel groups channels endpoint call. +/// +/// {@category Results} class ChannelGroupListChannelsResult extends Result { + /// Channel group name. String name; + + /// Channel group channels. Set channels; + /// @nodoc ChannelGroupListChannelsResult.fromJson(Map object) { var result = DefaultResult.fromJson(object); var payload = result.otherKeys['payload']; @@ -70,7 +77,11 @@ class ChannelGroupChangeChannelsParams extends Parameters { } } +/// Result of add or remove channels of channel group endpoint call. +/// +/// {@category Results} class ChannelGroupChangeChannelsResult extends Result { + /// @nodoc ChannelGroupChangeChannelsResult.fromJson(Map object); } @@ -99,6 +110,10 @@ class ChannelGroupDeleteParams extends Parameters { } } +/// Result of remove channel group endpoint call. +/// +/// {@category Results} class ChannelGroupDeleteResult extends Result { + /// @nodoc ChannelGroupDeleteResult.fromJson(Map object); } diff --git a/lib/src/dx/_endpoints/files.dart b/lib/src/dx/_endpoints/files.dart index ba50b182..e3441d05 100644 --- a/lib/src/dx/_endpoints/files.dart +++ b/lib/src/dx/_endpoints/files.dart @@ -1,4 +1,5 @@ -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; +import 'package:pubnub/pubnub.dart'; typedef decryptFunction = List Function(CipherKey key, List data); @@ -40,7 +41,7 @@ class GenerateFileUploadUrlResult extends Result { ..formFields = (object['file_upload_request']['form_fields'] as List) .fold({}, (previousValue, element) { - previousValue[element['name']] = element['value']; + previousValue[element['key']] = element['value']; return previousValue; }); } @@ -105,14 +106,19 @@ class PublishFileMessageParams extends Parameters { } } +/// Result of publish file message endpoint call. +/// +/// {@category Results} +/// {@category Files} class PublishFileMessageResult extends Result { bool isError; String description; int timetoken; - dynamic fileInfo; + FileInfo fileInfo; PublishFileMessageResult(); + /// @nodoc factory PublishFileMessageResult.fromJson(dynamic object) { return PublishFileMessageResult() ..timetoken = int.tryParse(object[2]) @@ -132,11 +138,17 @@ class DownloadFileParams extends Parameters { } } +/// Result of download file endpoint call. +/// +/// {@category Results} +/// {@category Files} class DownloadFileResult extends Result { + /// Content of the file. dynamic fileContent; DownloadFileResult._(); + /// @nodoc factory DownloadFileResult.fromJson(dynamic object, {CipherKey cipherKey, Function decryptFunction}) { if (cipherKey != null) { @@ -178,17 +190,26 @@ class ListFilesParams extends Parameters { } } +/// Result of list files endpoint call. +/// +/// {@category Results} +/// {@category Files} class ListFilesResult extends Result { List _filesDetail; String _next; int _count; + /// List of file details. List get filesDetail => _filesDetail; + + /// Next page ID. Used for pagination. String get next => _next; + int get count => _count; ListFilesResult._(); + /// @nodoc factory ListFilesResult.fromJson(dynamic object) => ListFilesResult._() .._filesDetail = (object['data'] as List) ?.map((e) => e == null ? null : FileDetail.fromJson(e)) @@ -197,6 +218,10 @@ class ListFilesResult extends Result { .._count = object['count'] as int; } +/// Represents a file uploaded to PubNub. +/// +/// {@category Results} +/// {@category Files} class FileDetail { String _name; String _id; @@ -241,8 +266,13 @@ class DeleteFileParams extends Parameters { } } +/// Result of delete file endpoint call. +/// +/// {@category Results} +/// {@category Files} class DeleteFileResult extends Result { DeleteFileResult._(); + /// @nodoc factory DeleteFileResult.fromJson(dynamic object) => DeleteFileResult._(); } diff --git a/lib/src/dx/_endpoints/history.dart b/lib/src/dx/_endpoints/history.dart index 678ee042..debee678 100644 --- a/lib/src/dx/_endpoints/history.dart +++ b/lib/src/dx/_endpoints/history.dart @@ -1,7 +1,5 @@ -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; import 'package:pubnub/src/dx/_utils/utils.dart'; -import 'package:pubnub/src/dx/subscribe/envelope.dart' - show MessageType, fromInt; typedef decryptFunction = List Function(CipherKey key, String data); @@ -51,6 +49,9 @@ class FetchHistoryParams extends Parameters { } } +/// Result of paginated history endpoint call. +/// +/// {@category Results} class FetchHistoryResult extends Result { List messages; @@ -124,22 +125,36 @@ class BatchHistoryParams extends Parameters { } } +/// Message from batch history endpoint. +/// +/// {@category Results} class BatchHistoryResultEntry { + /// Contents of the message. dynamic message; + + /// Original timetoken of the message. Timetoken timetoken; + + /// UUID of the sender. String uuid; + + /// Type of the message. MessageType messageType; + + /// If `includeMessageActions` was true, this will contain message actions. + /// Otherwise, it will be `null`. Map actions; BatchHistoryResultEntry._(); + /// @nodoc factory BatchHistoryResultEntry.fromJson(Map object, {CipherKey cipherKey, Function decryptFunction}) { return BatchHistoryResultEntry._() ..timetoken = Timetoken(int.tryParse(object['timetoken'])) ..uuid = object['uuid'] ..messageType = (object['message_type'] is int) - ? fromInt(object['message_type']) + ? MessageTypeExtension.fromInt(object['message_type']) : null ..message = cipherKey == null ? object['message'] @@ -148,12 +163,19 @@ class BatchHistoryResultEntry { } } +/// Result of batch history endpoint call. +/// +/// {@category Results} class BatchHistoryResult extends Result { + /// Map of channels to a list of messages represented by [BatchHistoryResultEntry]. Map> channels; + + /// @nodoc MoreHistory more; BatchHistoryResult(); + /// @nodoc factory BatchHistoryResult.fromJson(Map object, {CipherKey cipherKey, Function decryptFunction}) { var result = DefaultResult.fromJson(object); @@ -223,11 +245,16 @@ class CountMessagesParams extends Parameters { } } +/// Result of count messages endpoint call. +/// +/// {@category Results} class CountMessagesResult extends Result { + /// Map of channels to message counts. Map channels; CountMessagesResult._(); + /// @nodoc factory CountMessagesResult.fromJson(Map object) { var result = DefaultResult.fromJson(object); diff --git a/lib/src/dx/_endpoints/message_action.dart b/lib/src/dx/_endpoints/message_action.dart index fadd3cd7..53fba40f 100644 --- a/lib/src/dx/_endpoints/message_action.dart +++ b/lib/src/dx/_endpoints/message_action.dart @@ -1,4 +1,4 @@ -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; class FetchMessageActionsParams extends Parameters { Keyset keyset; @@ -33,46 +33,57 @@ class FetchMessageActionsParams extends Parameters { } } +/// Result of fetch message actions endpoint call. +/// +/// {@category Results} class FetchMessageActionsResult extends Result { - int _status; List _actions; dynamic _moreActions; - Map _error; dynamic get moreActions => _moreActions; - Map get error => _error; - - int get status => _status; - set status(int status) => _status = status; + /// List of message actions. List get actions => _actions ?? []; set actions(List actions) => _actions = actions; + /// @nodoc FetchMessageActionsResult(); + /// @nodoc factory FetchMessageActionsResult.fromJson(dynamic object) { return FetchMessageActionsResult() - .._status = object['status'] as int .._actions = (object['data'] as List) ?.map((e) => e == null ? null : MessageAction.fromJson(e)) ?.toList() .._moreActions = - object['more'] != null ? MoreAction.fromJson(object['more']) : null - .._error = object['error']; + object['more'] != null ? MoreAction.fromJson(object['more']) : null; } } +/// Represents a message action. +/// +/// {@category Results} class MessageAction { + /// Type of this message action. String type; + + /// Value of this message action. String value; + + /// Timetoken of when this message action has been added. String actionTimetoken; + + /// Timetoken of the parent message. String messageTimetoken; + + /// UUID of who added this message action. String uuid; - MessageAction(); + MessageAction._(); + /// @nodoc factory MessageAction.fromJson(dynamic object) { - return MessageAction() + return MessageAction._() ..type = object['type'] as String ..value = object['value'] as String ..actionTimetoken = "${object['actionTimetoken']}" @@ -81,6 +92,7 @@ class MessageAction { } } +/// @nodoc class MoreAction { String url; String start; @@ -131,6 +143,9 @@ class AddMessageActionParams extends Parameters { } } +/// Result of add message actions endpoint call. +/// +/// {@category Results} class AddMessageActionResult extends Result { int _status; MessageAction _data; @@ -185,6 +200,9 @@ class DeleteMessageActionParams extends Parameters { } } +/// Result of delete message actions endpoint call. +/// +/// {@category Results} class DeleteMessageActionResult extends Result { int _status; dynamic _data; diff --git a/lib/src/dx/_endpoints/objects/channel_metadata.dart b/lib/src/dx/_endpoints/objects/channel_metadata.dart index 8a6db9b2..a5ddc8ab 100644 --- a/lib/src/dx/_endpoints/objects/channel_metadata.dart +++ b/lib/src/dx/_endpoints/objects/channel_metadata.dart @@ -1,4 +1,4 @@ -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; class GetAllChannelMetadataParams extends Parameters { Keyset keyset; @@ -38,6 +38,10 @@ class GetAllChannelMetadataParams extends Parameters { } } +/// Represents channel metadata. +/// +/// {@category Results} +/// {@category Objects} class ChannelMetadataDetails { String _id; String _name; @@ -65,6 +69,10 @@ class ChannelMetadataDetails { .._eTag = object['eTag'] as String; } +/// Result of get all channels metadata endpoint call. +/// +/// {@category Results} +/// {@category Objects} class GetAllChannelMetadataResult extends Result { List _metadataList; int _totalCount; @@ -115,6 +123,10 @@ class GetChannelMetadataParams extends Parameters { } } +/// Result of get channels metadata endpoint call. +/// +/// {@category Results} +/// {@category Objects} class GetChannelMetadataResult extends Result { ChannelMetadataDetails _metadata; ChannelMetadataDetails get metadata => _metadata; @@ -161,6 +173,10 @@ class SetChannelMetadataParams extends Parameters { } } +/// Result of set channels metadata endpoint call. +/// +/// {@category Results} +/// {@category Objects} class SetChannelMetadataResult extends Result { ChannelMetadataDetails _metadata; SetChannelMetadataResult._(); @@ -197,6 +213,10 @@ class RemoveChannelMetadataParams extends Parameters { } } +/// Result of remove channels metadata endpoint call. +/// +/// {@category Results} +/// {@category Objects} class RemoveChannelMetadataResult extends Result { RemoveChannelMetadataResult._(); diff --git a/lib/src/dx/_endpoints/objects/membership_metadata.dart b/lib/src/dx/_endpoints/objects/membership_metadata.dart index 3eb65fc9..ed59524b 100644 --- a/lib/src/dx/_endpoints/objects/membership_metadata.dart +++ b/lib/src/dx/_endpoints/objects/membership_metadata.dart @@ -1,4 +1,4 @@ -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; import 'package:pubnub/src/dx/_endpoints/objects/channel_metadata.dart' show ChannelMetadataDetails; @@ -53,6 +53,10 @@ class GetMembershipsMetadataParams extends Parameters { } } +/// Represents membership metadata. +/// +/// {@category Results} +/// {@category Objects} class MembershipMetadata { ChannelMetadataDetails _channel; dynamic _custom; @@ -73,6 +77,10 @@ class MembershipMetadata { .._eTag = object['eTag'] as String; } +/// Result of memberships endpoint calls. +/// +/// {@category Results} +/// {@category Objects} class MembershipsResult extends Result { List _metadataList; int _totalCount; @@ -197,6 +205,10 @@ class GetChannelMembersParams extends Parameters { } } +/// Represents channel member metadata. +/// +/// {@category Results} +/// {@category Objects} class ChannelMemberMetadata { UuidMetadataDetails _uuid; dynamic _custom; @@ -218,6 +230,10 @@ class ChannelMemberMetadata { .._eTag = object['eTag'] as String; } +/// Result of channel members endpoint calls. +/// +/// {@category Results} +/// {@category Objects} class ChannelMembersResult extends Result { List _metadataList; int _totalCount; diff --git a/lib/src/dx/_endpoints/objects/uuid_metadata.dart b/lib/src/dx/_endpoints/objects/uuid_metadata.dart index 989be2a7..2f589932 100644 --- a/lib/src/dx/_endpoints/objects/uuid_metadata.dart +++ b/lib/src/dx/_endpoints/objects/uuid_metadata.dart @@ -1,4 +1,4 @@ -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; class GetAllUuidMetadataParams extends Parameters { Keyset keyset; @@ -37,6 +37,10 @@ class GetAllUuidMetadataParams extends Parameters { } } +/// Represents UUID metadata. +/// +/// {@category Results} +/// {@category Objects} class UuidMetadataDetails { String _id; String _name; @@ -69,14 +73,22 @@ class UuidMetadataDetails { .._eTag = json['eTag'] as String; } +/// Result of get all UUID metadata endpoint call. +/// +/// {@category Results} +/// {@category Objects} class GetAllUuidMetadataResult extends Result { List _metadataList; int _totalCount; String _next; String _prev; + /// List of UUIDs. List get metadataList => _metadataList; + + /// Total count of UUIDs. int get totalCount => _totalCount; + String get next => _next; String get prev => _prev; @@ -118,9 +130,14 @@ class GetUuidMetadataParams extends Parameters { } } +/// Result of get UUID metadata endpoint call. +/// +/// {@category Results} +/// {@category Objects} class GetUuidMetadataResult extends Result { UuidMetadataDetails _metadata; + /// UUID metadata. UuidMetadataDetails get metadata => _metadata; GetUuidMetadataResult(); @@ -159,11 +176,16 @@ class SetUuidMetadataParams extends Parameters { } } +/// Result of set UUID metadata endpoint call. +/// +/// {@category Results} +/// {@category Objects} class SetUuidMetadataResult extends Result { UuidMetadataDetails _metadata; SetUuidMetadataResult._(); + /// UUID metadata. UuidMetadataDetails get metadata => _metadata; factory SetUuidMetadataResult.fromJson(dynamic object) => @@ -192,6 +214,10 @@ class RemoveUuidMetadataParams extends Parameters { } } +/// Result of remove UUID metadata endpoint call. +/// +/// {@category Results} +/// {@category Objects} class RemoveUuidMetadataResult extends Result { RemoveUuidMetadataResult._(); diff --git a/lib/src/dx/_endpoints/pam.dart b/lib/src/dx/_endpoints/pam.dart index ece24b13..475bb530 100644 --- a/lib/src/dx/_endpoints/pam.dart +++ b/lib/src/dx/_endpoints/pam.dart @@ -1,4 +1,4 @@ -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; import 'package:pubnub/src/dx/_utils/utils.dart'; class PamGrantTokenParams extends Parameters { diff --git a/lib/src/dx/_endpoints/presence.dart b/lib/src/dx/_endpoints/presence.dart index 7c375183..91aff507 100644 --- a/lib/src/dx/_endpoints/presence.dart +++ b/lib/src/dx/_endpoints/presence.dart @@ -1,4 +1,4 @@ -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; import 'package:pubnub/src/dx/_utils/utils.dart'; class HeartbeatParams extends Parameters { @@ -36,9 +36,13 @@ class HeartbeatParams extends Parameters { } } +/// Result of heartbeat endpoint call. +/// +/// {@category Results} class HeartbeatResult extends Result { HeartbeatResult._(); + /// @nodoc factory HeartbeatResult.fromJson(dynamic _object) => HeartbeatResult._(); } @@ -76,11 +80,15 @@ class SetUserStateParams extends Parameters { } } +/// Result of set user state endpoint call. +/// +/// {@category Results} class SetUserStateResult extends Result { String state; SetUserStateResult._(); + /// @nodoc factory SetUserStateResult.fromJson(Map object) { var result = DefaultResult.fromJson(object); @@ -118,11 +126,15 @@ class GetUserStateParams extends Parameters { } } +/// Result of get user state endpoint call. +/// +/// {@category Results} class GetUserStateResult extends Result { String state; GetUserStateResult._(); + /// @nodoc factory GetUserStateResult.fromJson(Map object) { var result = DefaultResult.fromJson(object); @@ -161,11 +173,15 @@ class LeaveParams extends Parameters { } } +/// Result of leave endpoint call. +/// +/// {@category Results} class LeaveResult extends Result { String action; LeaveResult._(); + /// @nodoc factory LeaveResult.fromJson(Map object) { var result = DefaultResult.fromJson(object); @@ -173,6 +189,7 @@ class LeaveResult extends Result { } } +/// Represents an amount of state requested. enum StateInfo { all, onlyUUIDs, none } class HereNowParams extends Parameters { @@ -217,6 +234,9 @@ class HereNowParams extends Parameters { } } +/// Represents current channel participants. +/// +/// {@category Results} class ChannelOccupancy { String channelName; Map uuids; @@ -244,6 +264,9 @@ class ChannelOccupancy { } } +/// Result of here now endpoint call. +/// +/// {@category Results} class HereNowResult extends Result { Map channels = {}; @@ -302,6 +325,9 @@ class WhereNowParams extends Parameters { } } +/// Result of where now endpoint call. +/// +/// {@category Results} class WhereNowResult extends Result { Set channels; diff --git a/lib/src/dx/_endpoints/publish.dart b/lib/src/dx/_endpoints/publish.dart index 83758dbc..9bca69a8 100644 --- a/lib/src/dx/_endpoints/publish.dart +++ b/lib/src/dx/_endpoints/publish.dart @@ -1,4 +1,4 @@ -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; class PublishParams extends Parameters { Keyset keyset; @@ -43,6 +43,9 @@ class PublishParams extends Parameters { } } +/// Result of publish endpoint call. +/// +/// {@category Results} class PublishResult extends Result { bool isError; String description; diff --git a/lib/src/dx/_endpoints/push.dart b/lib/src/dx/_endpoints/push.dart index 56269701..9c745221 100644 --- a/lib/src/dx/_endpoints/push.dart +++ b/lib/src/dx/_endpoints/push.dart @@ -1,10 +1,21 @@ -import 'package:pubnub/src/core/core.dart'; -import 'package:pubnub/src/dx/_utils/utils.dart'; +import 'package:pubnub/core.dart'; +import '../_utils/utils.dart'; +/// Push gateway type. +/// +/// * [apns]and [apns2] uses Apple services. +/// * [fcm] and [gcm] uses Google services. +/// * [mpns] uses Microsoft services. +/// +/// {@category Push} enum PushGateway { apns, fcm, gcm, mpns, apns2 } +/// Environment type. +/// +/// {@category Push} enum Environment { development, production } +/// @nodoc extension EnvironmentExtension on Environment { String value() { switch (this) { @@ -18,6 +29,7 @@ extension EnvironmentExtension on Environment { } } +/// @nodoc extension PushGatewayExtension on PushGateway { String value() { switch (this) { @@ -75,6 +87,10 @@ class ListPushChannelsParams extends Parameters { } } +/// Result of list push channels endpoint call. +/// +/// {@category Results} +/// {@category Push} class ListPushChannelsResult extends Result { List channels; @@ -128,6 +144,10 @@ class AddPushChannelsParams extends Parameters { } } +/// Result of add push channels endpoint call. +/// +/// {@category Results} +/// {@category Push} class AddPushChannelsResult extends Result { int status; String description; @@ -182,6 +202,10 @@ class RemovePushChannelsParams extends Parameters { } } +/// Result of remove push channels endpoint call. +/// +/// {@category Results} +/// {@category Push} class RemovePushChannelsResult extends Result { int status; String description; @@ -236,6 +260,10 @@ class RemoveDeviceParams extends Parameters { } } +/// Result of remove device endpoint call. +/// +/// {@category Results} +/// {@category Push} class RemoveDeviceResult extends Result { int status; String description; diff --git a/lib/src/dx/_endpoints/signal.dart b/lib/src/dx/_endpoints/signal.dart index f608b9b5..30f6e0ee 100644 --- a/lib/src/dx/_endpoints/signal.dart +++ b/lib/src/dx/_endpoints/signal.dart @@ -1,4 +1,4 @@ -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; import 'package:pubnub/src/dx/_utils/utils.dart'; class SignalParams extends Parameters { @@ -30,6 +30,9 @@ class SignalParams extends Parameters { } } +/// Result of signal endpoint call. +/// +/// {@category Results} class SignalResult extends Result { bool isError; String description; diff --git a/lib/src/dx/_endpoints/time.dart b/lib/src/dx/_endpoints/time.dart index eb188130..413a020b 100644 --- a/lib/src/dx/_endpoints/time.dart +++ b/lib/src/dx/_endpoints/time.dart @@ -1,4 +1,4 @@ -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; class TimeParams extends Parameters { @override diff --git a/lib/src/dx/_utils/default_flow.dart b/lib/src/dx/_utils/default_flow.dart index da80da0e..0dd664f5 100644 --- a/lib/src/dx/_utils/default_flow.dart +++ b/lib/src/dx/_utils/default_flow.dart @@ -1,6 +1,6 @@ import 'package:meta/meta.dart'; -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; import 'package:pubnub/src/dx/_utils/utils.dart'; typedef Serialize = R Function(dynamic object, diff --git a/lib/src/dx/_utils/default_result.dart b/lib/src/dx/_utils/default_result.dart index 47164e76..abea1fb6 100644 --- a/lib/src/dx/_utils/default_result.dart +++ b/lib/src/dx/_utils/default_result.dart @@ -1,4 +1,4 @@ -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; class DefaultResult extends Result { int status; diff --git a/lib/src/dx/_utils/disposable.dart b/lib/src/dx/_utils/disposable.dart deleted file mode 100644 index 72c0fa34..00000000 --- a/lib/src/dx/_utils/disposable.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'dart:async'; - -abstract class Disposable { - bool get isDisposed; - Future get didDispose; - - Future dispose(); -} diff --git a/lib/src/dx/_utils/ensure.dart b/lib/src/dx/_utils/ensure.dart index 4f92ba73..ac058b74 100644 --- a/lib/src/dx/_utils/ensure.dart +++ b/lib/src/dx/_utils/ensure.dart @@ -1,27 +1,37 @@ -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; final _invariantMessages = { 'not-null': (String that, _) => '${that} cannot be null', 'not-empty': (String that, _) => '${that} cannot be empty', 'default': (_, __) => 'invariant has been broken', 'is-equal': (String that, List what) => - '${that} has to equal ${what[0]}' + '${that} has to equal ${what[0]}', + 'invalid-type': (String that, List what) => + '${that} must be either ${what.join(' or ')}.', }; /// Exception thrown when one of the invariants of a method /// is broken. +/// +/// {@category Exceptions} class InvariantException extends PubNubException { + /// @nodoc InvariantException(String messageId, [String what, List args = const []]) : super((_invariantMessages[messageId] ?? _invariantMessages['default'])( what, args)); } +/// @nodoc class Ensure { dynamic value; Ensure(this.value); + static void fail(String reason, [String what, List args]) { + throw InvariantException(reason, what, args); + } + void isNotEmpty([String what]) { if (value != null && value is List && value.length != 0) { return; diff --git a/lib/src/dx/_utils/exceptions.dart b/lib/src/dx/_utils/exceptions.dart index 0ffddfa3..65ff15e5 100644 --- a/lib/src/dx/_utils/exceptions.dart +++ b/lib/src/dx/_utils/exceptions.dart @@ -1,4 +1,4 @@ -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; import 'package:pubnub/src/dx/_utils/utils.dart'; import 'package:xml/xml.dart'; diff --git a/lib/src/dx/_utils/signature.dart b/lib/src/dx/_utils/signature.dart index 3342df56..85110dd3 100644 --- a/lib/src/dx/_utils/signature.dart +++ b/lib/src/dx/_utils/signature.dart @@ -2,7 +2,7 @@ import 'dart:collection'; import 'dart:convert'; import 'package:crypto/crypto.dart'; -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; String _encodeQueryParameters(Map queryParameters) => SplayTreeMap .from(queryParameters) diff --git a/lib/src/dx/batch/batch.dart b/lib/src/dx/batch/batch.dart index 29001f4c..22139210 100644 --- a/lib/src/dx/batch/batch.dart +++ b/lib/src/dx/batch/batch.dart @@ -1,14 +1,22 @@ -import 'package:pubnub/pubnub.dart'; -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; import 'package:pubnub/src/dx/_utils/utils.dart'; import 'package:pubnub/src/dx/_endpoints/history.dart'; +export 'package:pubnub/src/dx/_endpoints/history.dart' + show BatchHistoryResult, BatchHistoryResultEntry, CountMessagesResult; + +/// Groups common **batch** features together. +/// +/// Available as [PubNub.batch]. class BatchDx { final Core _core; + /// @nodoc BatchDx(this._core); - /// Fetch messages for multiple channels using one REST call. + /// Fetch messages for multiple channels using one call. + /// + /// If [includeMessageActions] is `true`, then you can only pass in one channel in [channels]. Future fetchMessages(Set channels, {Keyset keyset, String using, @@ -64,14 +72,12 @@ class BatchDx { return fetchMessagesResult; } - /// Get multiple channels' message count using one REST call. + /// Get multiple channels' message count using one call. /// - /// [channels] can either be a [Map] or [Set]: - /// * if you want to count messages in all channels up to a common timetoken, - /// pass in a [Set] and a named parameter [timetoken]. - /// * if you want to specify separate timetoken for each channel, - /// pass in a [Map]. Additionally, if a value in the map is null, - /// it will use a timetoken from a named parameter [timetoken]. + /// [channels] can either be a `Map` or `Set`: + /// * if you want to count messages in all channels up to a common timetoken, pass in a `Set` and a named parameter [timetoken]. + /// * if you want to specify separate timetoken for each channel, pass in a `Map`. + /// Additionally, if a value in the map is null, it will use a timetoken from a named parameter [timetoken]. Future countMessages(dynamic channels, {Keyset keyset, String using, Timetoken timetoken}) { keyset ??= _core.keysets.get(using, defaultIfNameIsNull: true); @@ -88,7 +94,8 @@ class BatchDx { channelsTimetoken: channels.map((key, value) => MapEntry(key, value ?? timetoken))); } else { - throw InvalidArgumentsException(); + Ensure.fail('invalid-type', 'channels', + ['Set', 'Map']); } return defaultFlow( diff --git a/lib/src/dx/channel/channel.dart b/lib/src/dx/channel/channel.dart index 94db06bf..b8eba682 100644 --- a/lib/src/dx/channel/channel.dart +++ b/lib/src/dx/channel/channel.dart @@ -1,32 +1,28 @@ -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; import 'package:pubnub/src/default.dart'; -import 'package:pubnub/src/dx/_endpoints/presence.dart'; -import 'package:pubnub/src/dx/_endpoints/publish.dart'; -import 'package:pubnub/src/dx/_endpoints/message_action.dart'; -import 'package:pubnub/src/dx/subscribe/subscription.dart'; +import '../_endpoints/presence.dart'; +import '../_endpoints/publish.dart'; +import '../_endpoints/message_action.dart'; import 'channel_history.dart'; +export 'channel_history.dart' + show PaginatedChannelHistory, ChannelHistory, ChannelHistoryOrder; +export 'message.dart' show Message; + +/// Represents a single channel. +/// +/// It shouldn't be instantiated directly, instead call [PubNub.channel]. +/// +/// {@category Basic Features} class Channel { final PubNub _core; final Keyset _keyset; String name; + /// @nodoc Channel(this._core, this._keyset, this.name); - /// Returns a subscription to channel [name]. Returns [Subscription] instance. - /// - /// If you set [withPresence] to true, it will also subscribe to presence events. - Subscription subscription({bool withPresence = false}) { - return _core.subscription( - keyset: _keyset, channels: {name}, withPresence: withPresence); - } - - Future subscribe({bool withPresence = false}) { - return _core.subscribe( - keyset: _keyset, channels: {name}, withPresence: withPresence); - } - /// Publishes [message] to a channel [name]. /// /// You can override the default account configuration on message diff --git a/lib/src/dx/channel/channel_group.dart b/lib/src/dx/channel/channel_group.dart index 1dec25d0..b59e5b36 100644 --- a/lib/src/dx/channel/channel_group.dart +++ b/lib/src/dx/channel/channel_group.dart @@ -1,61 +1,18 @@ -import 'package:pubnub/src/core/core.dart'; -import 'package:pubnub/src/default.dart'; -import 'package:pubnub/src/dx/subscribe/subscription.dart'; -import 'package:pubnub/src/dx/_utils/utils.dart'; +import 'package:pubnub/core.dart'; -import 'package:pubnub/src/dx/_endpoints/channel_group.dart'; +import '../_utils/utils.dart'; +import '../_endpoints/channel_group.dart'; -/// Representation of a channel group. -class ChannelGroup { - final PubNub _core; - final Keyset _keyset; - String name; - - /// Contains methods that manipulate and get channels in this channel group. - final ChannelSet channels; - - ChannelGroup(this._core, this._keyset, this.name) - : channels = ChannelSet(_core, _keyset, name); - - /// Return subscription to this channel group. - Subscription subscription({bool withPresence}) => _core.subscription( - keyset: _keyset, channelGroups: {name}, withPresence: withPresence); - - Future subscribe({bool withPresence}) => _core.subscribe( - keyset: _keyset, channelGroups: {name}, withPresence: withPresence); -} - -class ChannelSet { - final PubNub _core; - final Keyset _keyset; - final String _name; - - ChannelSet(this._core, this._keyset, this._name); - - /// List all channels in this channel group. - Future> list() async { - var result = await _core.channelGroups.listChannels(_name, keyset: _keyset); - - return result.channels; - } - - /// Add [channels] to this channel group. - Future add(Set channels) async { - await _core.channelGroups.addChannels(_name, channels, keyset: _keyset); - } - - /// Remove [channels] from this channel group. - Future remove(Set channels) async { - await _core.channelGroups.removeChannels(_name, channels, keyset: _keyset); - } -} +export '../_endpoints/channel_group.dart'; +/// Groups **channel group** methods together. class ChannelGroupDx { final Core _core; + /// @nodoc ChannelGroupDx(this._core); - /// List all channels in a channel [group]. + /// Lists all channels in a channel [group]. Future listChannels(String group, {Keyset keyset, String using}) { keyset ??= _core.keysets.get(using, defaultIfNameIsNull: true); @@ -67,7 +24,7 @@ class ChannelGroupDx { ChannelGroupListChannelsResult.fromJson(object)); } - /// Add [channels] to the channel [group]. + /// Adds [channels] to the channel [group]. Future addChannels( String group, Set channels, {Keyset keyset, String using}) { @@ -80,7 +37,7 @@ class ChannelGroupDx { ChannelGroupChangeChannelsResult.fromJson(object)); } - /// Remove [channels] from a channel [group]. + /// Removes [channels] from a channel [group]. Future removeChannels( String group, Set channels, {Keyset keyset, String using}) { @@ -94,7 +51,7 @@ class ChannelGroupDx { ChannelGroupChangeChannelsResult.fromJson(object)); } - /// Remove ALL channels from a channel [group]. + /// Removes ALL channels from a channel [group]. Future delete(String group, {Keyset keyset, String using}) { keyset ??= _core.keysets.get(using, defaultIfNameIsNull: true); diff --git a/lib/src/dx/channel/channel_history.dart b/lib/src/dx/channel/channel_history.dart index 05dbbf1c..3bb9f008 100644 --- a/lib/src/dx/channel/channel_history.dart +++ b/lib/src/dx/channel/channel_history.dart @@ -1,4 +1,4 @@ -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; import 'package:pubnub/src/default.dart'; import 'package:pubnub/src/dx/_utils/utils.dart'; import 'package:pubnub/src/dx/_endpoints/history.dart'; @@ -6,8 +6,10 @@ import 'package:pubnub/src/dx/_endpoints/history.dart'; import 'channel.dart'; import 'message.dart'; +/// Order of messages based on timetoken. enum ChannelHistoryOrder { ascending, descending } +/// @nodoc extension ChannelHistoryOrderExtension on ChannelHistoryOrder { T choose({T ascending, T descending}) { switch (this) { @@ -21,6 +23,7 @@ extension ChannelHistoryOrderExtension on ChannelHistoryOrder { } } +/// Represents history of messages in a channel that can be counted, fetched or removed. class ChannelHistory { final PubNub _core; final Channel _channel; @@ -42,6 +45,7 @@ class ChannelHistory { List get messages => _messages; List _messages = []; + /// @nodoc ChannelHistory(this._core, this._channel, this._keyset, this.from, this.to); /// Returns a number of messages after [from]. @@ -106,6 +110,9 @@ class ChannelHistory { } } +/// Represents readonly history of messages in a channel. +/// +/// It fetches messages in chunks only when requested using [more] call. class PaginatedChannelHistory { final PubNub _core; final Channel _channel; @@ -131,6 +138,7 @@ class PaginatedChannelHistory { int get chunkSize => _chunkSize; final int _chunkSize; + /// @nodoc PaginatedChannelHistory( this._core, this._channel, this._keyset, this._order, this._chunkSize); diff --git a/lib/src/dx/channel/message.dart b/lib/src/dx/channel/message.dart index f373a667..97202a7e 100644 --- a/lib/src/dx/channel/message.dart +++ b/lib/src/dx/channel/message.dart @@ -1,11 +1,18 @@ -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; +/// Represents a message in [ChannelHistory] and [PaginatedChannelHistory]. +/// +/// {@category Results} class Message { + /// Contents of the message. dynamic contents; + + /// Original timetoken. Timetoken timetoken; Message(); + /// @nodoc factory Message.fromJson(Map object) { return Message() ..contents = object['message'] diff --git a/lib/src/dx/files/extensions/keyset.dart b/lib/src/dx/files/extensions/keyset.dart index 9be1cb3a..57ce3883 100644 --- a/lib/src/dx/files/extensions/keyset.dart +++ b/lib/src/dx/files/extensions/keyset.dart @@ -1,5 +1,6 @@ import 'package:pubnub/src/core/keyset.dart'; +/// @nodoc extension FileKeysetExtension on Keyset { int get fileMessagePublishRetryLimit => settings['#fileMessagePublishRetryLimit'] ?? 5; diff --git a/lib/src/dx/files/files.dart b/lib/src/dx/files/files.dart index 95a705a7..f3704db1 100644 --- a/lib/src/dx/files/files.dart +++ b/lib/src/dx/files/files.dart @@ -1,4 +1,4 @@ -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; import 'package:pubnub/src/dx/_utils/utils.dart'; import 'package:pubnub/src/dx/_endpoints/files.dart'; @@ -7,9 +7,26 @@ import 'extensions/keyset.dart'; export 'schema.dart'; export 'extensions/keyset.dart'; +export '../_endpoints/files.dart' + show + PublishFileMessageResult, + DeleteFileResult, + DownloadFileResult, + FileUploadResult, + GenerateFileUploadUrlResult, + ListFilesResult, + FileDetail; +/// Groups **file** methods together. +/// +/// Available as [PubNub.files]. +/// Introduced with [File API](https://www.pubnub.com/docs/platform/messages/files). +/// +/// {@category Files} class FileDx { final Core _core; + + /// @nodoc FileDx(this._core); /// This method allows to send a [file] to a [channel] with an optional [fileMessage]. @@ -17,7 +34,7 @@ class FileDx { /// > Ensure that your [Keyset] has a `publishKey` defined. /// /// If you provide a [cipherKey], the file will be encrypted with it. - /// If its missing, then a `cipherKey` from [Keyset] will be used. + /// If its missing, then [Keyset.cipherKey] will be used. /// If no `cipherKey` is provided, then the file won't be encrypted. /// /// If the upload was successful, but publishing the file event to the [channel] wasn't, @@ -33,7 +50,7 @@ class FileDx { /// saving using [storeFileMessage] flag - `true` to save and `false` to discard. /// Leave this option unset if you want to use the default. /// - /// * Provide [fileMessageMeta] for additional information + /// * Provide [fileMessageMeta] for additional information. Future sendFile( String channel, String fileName, List file, {CipherKey cipherKey, @@ -61,14 +78,11 @@ class FileDx { file = _core.crypto.encryptFileData(cipherKey ?? keyset.cipherKey, file); } - // TODO: Decide what to do here - // form['file'] = _fileManager.createMultipartFile(_fileManager.read(file), - // fileName: fileName); - var fileInfo = FileInfo( uploadDetails.fileId, uploadDetails.fileName, - getFileUrl(channel, uploadDetails.fileId, uploadDetails.fileName) + getFileUrl(channel, uploadDetails.fileId, uploadDetails.fileName, + keyset: keyset) .toString(), ); @@ -76,10 +90,12 @@ class FileDx { var retryCount = keyset.fileMessagePublishRetryLimit; + var params = FileUploadParams( + uploadDetails.uploadUri, {...uploadDetails.formFields, 'file': file}); + var s3Response = await defaultFlow( core: _core, - params: FileUploadParams(uploadDetails.uploadUri, - {...uploadDetails.formFields, 'file': file}), + params: params, deserialize: false, serialize: (object, [_]) => FileUploadResult.fromJson(object)); @@ -101,19 +117,22 @@ class FileDx { publishFileResult.isError = true; } if (!publishFileResult.isError) { + publishFileResult.fileInfo = fileInfo; return publishFileResult; } --retryCount; } while (retryCount > 0); } - return publishFileResult..fileInfo = publishMessage.file; + + return publishFileResult..fileInfo = fileInfo; } - /// This method allows to publish file Message + /// Allows to publish file message. + /// /// In case `sendFile` method doesn't publish message to [channel], this method /// can be used to explicitly publish message /// - /// Provide [cipherKey] to encrypt `message` it takes precedence over `keyset`'s cipherKey + /// Provide [cipherKey] to encrypt the message. It takes precedence over [Keyset.cipherKey]. /// /// You can override the default account configuration on message /// saving using [storeMessage] flag - `true` to save and `false` to discard. @@ -153,10 +172,9 @@ class FileDx { } /// This method allows to download the file with [fileId] and [fileName] from channel [channel] - /// It returns file content in bytes format `List` + /// It returns file content in bytes format `List`. /// - /// Provide [cipherKey] to override default configuration of PubNub object's `keyset`'s `cipherKey` - /// * It gives priority of [cipherKey] provided in method argument over `keyset`'s `cipherKey` + /// Provided [cipherKey] overrides [Keyset.cipherKey]. /// /// If [keyset] is not provided, then it tries to obtain a keyset [using] name. /// If that fails, then it uses the default keyset. @@ -178,11 +196,11 @@ class FileDx { decryptFunction: decrypter)); } - /// This method gives list of all files information of channel [channel] + /// Lists all files in a [channel]. /// - /// You can limit this list by providing [limit] parameter + /// You can limit this list by providing [limit] parameter. /// - /// Pagination can be managed by using [next] parameter + /// Pagination can be managed by using [next] parameter. /// /// If [keyset] is not provided, then it tries to obtain a keyset [using] name. /// If that fails, then it uses the default keyset. @@ -197,7 +215,7 @@ class FileDx { serialize: (object, [_]) => ListFilesResult.fromJson(object)); } - /// It deletes file with [fileId], [fileName] from channel [channel] + /// Deletes file with [fileId] and [fileName] from [channel]. /// /// If [keyset] is not provided, then it tries to obtain a keyset [using] name. /// If that fails, then it uses the default keyset. @@ -212,12 +230,11 @@ class FileDx { serialize: (object, [_]) => DeleteFileResult.fromJson(object)); } - /// It gives you the `Uri` to download the file with [fileId], [fileName] from channel [channel] + /// Returns [Uri] to download the file with [fileId] and [fileName] from [channel]. /// - /// You can your returned Url to download the file content by giving GET request + /// You can download the file by making a GET request to returned Uri. /// - /// * Ensure to manage decryption part since the returned content may be encrypted - /// if file is uploaded in encrypted format + /// > If the file is encrypted, you will have to decrypt it on your own. /// /// If [keyset] is not provided, then it tries to obtain a keyset [using] name. /// If that fails, then it uses the default keyset. @@ -225,7 +242,7 @@ class FileDx { Uri getFileUrl(String channel, String fileId, String fileName, {Keyset keyset, String using}) { keyset ??= _core.keysets.get(using, defaultIfNameIsNull: true); - return Uri(scheme: 'https', host: 'ps.pndsn.com', pathSegments: [ + var pathSegments = [ 'v1', 'files', keyset.subscribeKey, @@ -234,13 +251,28 @@ class FileDx { 'files', fileId, fileName - ]); + ]; + var queryParams = { + 'pnsdk': 'PubNub-Dart/${Core.version}', + if (keyset.secretKey != null) + 'timestamp': '${Time().now().millisecondsSinceEpoch ~/ 1000}', + if (keyset.authKey != null) 'auth': keyset.authKey + }; + if (keyset.secretKey != null) { + queryParams.addAll( + {'signature': computeSignature(keyset, pathSegments, queryParams)}); + } + + return Uri( + scheme: 'https', + host: 'ps.pndsn.com', + pathSegments: pathSegments, + queryParameters: queryParams); } - /// This method helps to encrypt the file content in bytes format + /// Encrypts file content in bytes format. /// - /// Provide [cipherKey] to override default configuration of PubNub object's `keyset`'s `cipherKey` - /// * It gives priority of [cipherKey] provided in method argument over `keyset`'s `cipherKey` + /// Provide [cipherKey] to override [Keyset.cipherKey]. /// /// If [keyset] is not provided, then it tries to obtain a keyset [using] name. /// If that fails, then it uses the default keyset. @@ -251,10 +283,9 @@ class FileDx { return _core.crypto.encryptFileData(cipherKey ?? keyset.cipherKey, bytes); } - /// This method helps to decrypt the file content in bytes format + /// Decrypts file content in bytes format. /// - /// Provide [cipherKey] to override default configuration of PubNub object's `keyset`'s `cipherKey` - /// * it gives priority of [cipherKey] provided in method argument over `keyset`'s `cipherKey` + /// Provide [cipherKey] to override [Keyset.cipherKey]. /// /// If [keyset] is not provided, then it tries to obtain a keyset [using] name. /// If that fails, then it uses the default keyset. diff --git a/lib/src/dx/files/schema.dart b/lib/src/dx/files/schema.dart index 05ef5057..fda5a8fe 100644 --- a/lib/src/dx/files/schema.dart +++ b/lib/src/dx/files/schema.dart @@ -1,3 +1,6 @@ +/// Represents file information. +/// +/// {@category Files} class FileInfo { String id; String name; @@ -10,6 +13,9 @@ class FileInfo { } } +/// Represents message with attached file. +/// +/// {@category Files} class FileMessage { FileInfo file; dynamic message; diff --git a/lib/src/dx/message_action/message_action.dart b/lib/src/dx/message_action/message_action.dart index 8f7adda9..7b3062c5 100644 --- a/lib/src/dx/message_action/message_action.dart +++ b/lib/src/dx/message_action/message_action.dart @@ -1,7 +1,9 @@ -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; -import 'package:pubnub/src/dx/_utils/utils.dart'; -import 'package:pubnub/src/dx/_endpoints/message_action.dart'; +import '../_utils/utils.dart'; +import '../_endpoints/message_action.dart'; + +export '../_endpoints/message_action.dart'; mixin MessageActionDx on Core { /// Returns all message actions of a given [channel]. @@ -48,7 +50,6 @@ mixin MessageActionDx on Core { } } } while (loopResult.moreActions != null); - fetchMessageActionsResult..status = loopResult.status; return fetchMessageActionsResult; } diff --git a/lib/src/dx/objects/channel_metadata.dart b/lib/src/dx/objects/channel_metadata.dart index 9cdcbd04..02733d61 100644 --- a/lib/src/dx/objects/channel_metadata.dart +++ b/lib/src/dx/objects/channel_metadata.dart @@ -1,14 +1,17 @@ -import 'package:pubnub/pubnub.dart'; -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; +import '../_endpoints/objects/objects_types.dart'; +import 'objects.dart'; +import 'objects_types.dart'; -/// Represents a Channel Metadata object -/// Useful to deal with a specific channel's Metadata +/// Represents a channel metadata. +/// +/// Useful to deal with a specific channel's metadata. class ChannelMetadata { - final PubNub _core; + final ObjectsDx _objects; final Keyset _keyset; final String _id; - ChannelMetadata(this._core, this._keyset, this._id); + ChannelMetadata(this._objects, this._keyset, this._id); /// You can use this method to add a uuidMetadata(membersMetadata) into given channel /// @@ -42,7 +45,7 @@ class ChannelMetadata { bool includeCount, String filter, Set sort}) => - _core.objects.setChannelMembers(_id, channelMembersMetadata, + _objects.setChannelMembers(_id, channelMembersMetadata, limit: limit, start: start, end: end, @@ -85,7 +88,7 @@ class ChannelMetadata { bool includeCount, String filter, Set sort}) => - _core.objects.removeChannelMembers(_id, uuids, + _objects.removeChannelMembers(_id, uuids, keyset: _keyset, limit: limit, start: start, @@ -130,7 +133,7 @@ class ChannelMetadata { Set sort, Keyset keyset, String using}) => - _core.objects.getChannelMembers(_id, + _objects.getChannelMembers(_id, limit: limit, start: start, end: end, diff --git a/lib/src/dx/objects/objects.dart b/lib/src/dx/objects/objects.dart index 0c9543fe..c69f4e27 100644 --- a/lib/src/dx/objects/objects.dart +++ b/lib/src/dx/objects/objects.dart @@ -1,13 +1,26 @@ -import 'package:pubnub/src/core/core.dart'; -import 'package:pubnub/src/dx/_endpoints/objects/uuid_metadata.dart'; -import 'package:pubnub/src/dx/_endpoints/objects/channel_metadata.dart'; -import 'package:pubnub/src/dx/_endpoints/objects/membership_metadata.dart'; +import 'package:pubnub/core.dart'; + import '../_utils/utils.dart'; +import '../_endpoints/objects/uuid_metadata.dart'; +import '../_endpoints/objects/channel_metadata.dart'; +import '../_endpoints/objects/membership_metadata.dart'; import 'schema.dart'; +export '../_endpoints/objects/uuid_metadata.dart'; +export '../_endpoints/objects/channel_metadata.dart'; +export '../_endpoints/objects/membership_metadata.dart'; +export 'schema.dart'; + +/// Groups **objects** methods together. +/// +/// Available as [PubNub.objects]. +/// Introduced with [Objects API](https://www.pubnub.com/docs/platform/channels/metadata). +/// +/// {@category Objects} class ObjectsDx { final Core _core; + /// @nodoc ObjectsDx(this._core); /// Returns a paginated list of all uuidMetadata associated with the given subscription key, diff --git a/lib/src/dx/objects/schema.dart b/lib/src/dx/objects/schema.dart index f2b3f40c..f813e5cd 100644 --- a/lib/src/dx/objects/schema.dart +++ b/lib/src/dx/objects/schema.dart @@ -1,3 +1,6 @@ +/// Represents UUID metadata operations input. +/// +/// {@category Objects} class UuidMetadataInput { String name; String email; @@ -17,6 +20,9 @@ class UuidMetadataInput { }; } +/// Represents channel metadata operations input. +/// +/// {@category Objects} class ChannelMetadataInput { String name; String description; @@ -31,6 +37,9 @@ class ChannelMetadataInput { }; } +/// Represents channel member metadata operations input. +/// +/// {@category Objects} class ChannelMemberMetadataInput { String uuid; Map custom; @@ -43,6 +52,9 @@ class ChannelMemberMetadataInput { }; } +/// Represents membership metadata operations input. +/// +/// {@category Objects} class MembershipMetadataInput { String channelId; Map custom; @@ -55,6 +67,9 @@ class MembershipMetadataInput { }; } +/// Represents UUID input. +/// +/// {@category Objects} class UuIdInfo { String uuid; @@ -65,6 +80,9 @@ class UuIdInfo { }; } +/// Represents channel id input. +/// +/// {@category Objects} class ChannelIdInfo { String channelId; diff --git a/lib/src/dx/objects/uuid_metadata.dart b/lib/src/dx/objects/uuid_metadata.dart index b3ae4b2e..750547d8 100644 --- a/lib/src/dx/objects/uuid_metadata.dart +++ b/lib/src/dx/objects/uuid_metadata.dart @@ -1,17 +1,16 @@ -import 'package:pubnub/pubnub.dart'; -import 'package:pubnub/src/core/core.dart'; -import 'package:pubnub/src/dx/_endpoints/objects/objects_types.dart'; - +import 'package:pubnub/core.dart'; +import '../_endpoints/objects/objects_types.dart'; +import 'objects.dart'; import 'schema.dart'; /// Representation of UuidMetadata object /// Useful while dealing with one specific `uuid` details class UUIDMetadata { - final PubNub _core; + final ObjectsDx _objects; final Keyset _keyset; final String _uuid; - UUIDMetadata(this._core, this._keyset, this._uuid); + UUIDMetadata(this._objects, this._keyset, this._uuid); /// It adds membership metadata for given `uuid` and returns paginated list of memberships metadata /// @@ -45,7 +44,7 @@ class UUIDMetadata { bool includeCount, String filter, Set sort}) => - _core.objects.setMemberships(membershipMetadata, + _objects.setMemberships(membershipMetadata, uuid: _uuid, keyset: _keyset, limit: limit, @@ -89,7 +88,7 @@ class UUIDMetadata { bool includeCount, String filter, Set sort}) => - _core.objects.getMemberships( + _objects.getMemberships( uuid: _uuid, limit: limit, start: start, diff --git a/lib/src/dx/pam/pam.dart b/lib/src/dx/pam/pam.dart index 573df219..99f2716f 100644 --- a/lib/src/dx/pam/pam.dart +++ b/lib/src/dx/pam/pam.dart @@ -1,6 +1,6 @@ import 'package:meta/meta.dart'; -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; import 'package:pubnub/src/dx/_endpoints/pam.dart'; import 'package:pubnub/src/dx/_utils/utils.dart'; diff --git a/lib/src/dx/pam/resource.dart b/lib/src/dx/pam/resource.dart index 618c3cfe..041bbfb9 100644 --- a/lib/src/dx/pam/resource.dart +++ b/lib/src/dx/pam/resource.dart @@ -1,5 +1,9 @@ +/// Represents a resource type. +/// +/// {@category Access Manager} enum ResourceType { space, user, channel, channelGroup } +/// @nodoc extension ResourceTypeExtension on ResourceType { String get value { switch (this) { @@ -17,6 +21,7 @@ extension ResourceTypeExtension on ResourceType { } } +/// @nodoc ResourceType getResourceTypeFromString(String type) { switch (type) { case 'chan': @@ -33,6 +38,8 @@ ResourceType getResourceTypeFromString(String type) { } /// Represents a resource in PAM. +/// +/// {@category Access Manager} class Resource { /// Type of the resource. ResourceType type; diff --git a/lib/src/dx/pam/token.dart b/lib/src/dx/pam/token.dart index e18814b1..3c9bb3f2 100644 --- a/lib/src/dx/pam/token.dart +++ b/lib/src/dx/pam/token.dart @@ -1,9 +1,11 @@ -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; import 'cbor.dart'; import 'resource.dart'; /// Token returned by the PAM. +/// +/// {@category Access Manager} class Token { final String _stringToken; diff --git a/lib/src/dx/pam/token_request.dart b/lib/src/dx/pam/token_request.dart index dbaf5a47..b0d7b19c 100644 --- a/lib/src/dx/pam/token_request.dart +++ b/lib/src/dx/pam/token_request.dart @@ -1,17 +1,23 @@ import 'dart:convert'; -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; import 'package:pubnub/src/dx/_endpoints/pam.dart'; import 'package:pubnub/src/dx/_utils/utils.dart'; import 'resource.dart'; import 'token.dart'; +/// Represents a token request. +/// +/// {@category Access Manager} class TokenRequest { final Core _core; final Keyset _keyset; + /// Time to live in seconds. final int ttl; + + /// Token metadata. final dynamic meta; final List _resources = []; diff --git a/lib/src/dx/presence/presence.dart b/lib/src/dx/presence/presence.dart index 693f7bcc..d45524bc 100644 --- a/lib/src/dx/presence/presence.dart +++ b/lib/src/dx/presence/presence.dart @@ -1,8 +1,25 @@ -import 'package:pubnub/src/core/core.dart'; -import 'package:pubnub/src/dx/_endpoints/presence.dart'; -import 'package:pubnub/src/dx/_utils/utils.dart'; +import 'package:pubnub/core.dart'; -export '../_endpoints/presence.dart' show StateInfo; +import '../_utils/utils.dart'; +import '../_endpoints/presence.dart'; + +export '../_endpoints/presence.dart'; + +/// @nodoc +extension PresenceKeysetExtension on Keyset { + /// Presence timeout. + int get presenceTimeout => settings['#presenceTimeout']; + set presenceTimeout(int value) => settings['#presenceTimeout'] = value; + + /// Interval at which heartbeats should be sent. + int get heartbeatInterval => settings['#heartbeatInterval']; + set heartbeatInterval(int value) => settings['#heartbeatInterval'] = value; + + /// Supress leave events. + bool get suppressLeaveEvents => settings['#suppressLeaveEvents']; + set suppressLeaveEvents(bool value) => + settings['#suppressLeaveEvents'] = value; +} mixin PresenceDx on Core { /// Gets the occupancy information from a list of [channels] and/or [channelGroups]. @@ -29,4 +46,42 @@ mixin PresenceDx on Core { serialize: (object, [_]) => HereNowResult.fromJson(object, channelName: (channels.length == 1) ? channels.first : null)); } + + /// Announce in [channels] and [channelGroups] that a device linked to the UUID in the keyset left. + Future announceLeave({ + Keyset keyset, + String using, + Set channels, + Set channelGroups, + }) { + keyset ??= super.keysets.get(using, defaultIfNameIsNull: true); + + Ensure(keyset).isNotNull('keyset'); + + return defaultFlow( + core: this, + params: LeaveParams(keyset, + channels: channels, channelGroups: channelGroups), + serialize: (object, [_]) => LeaveResult.fromJson(object)); + } + + /// Anounce in [channels] and [channelGroups] that a device linked to the UUID in the keyset is alive. + Future announceHeartbeat( + {Keyset keyset, + String using, + Set channels, + Set channelGroups, + int heartbeat}) { + keyset ??= super.keysets.get(using, defaultIfNameIsNull: true); + + Ensure(keyset).isNotNull('keyset'); + + return defaultFlow( + core: this, + params: HeartbeatParams(keyset, + channels: channels, + channelGroups: channelGroups, + heartbeat: heartbeat), + serialize: (object, [_]) => HeartbeatResult.fromJson(object)); + } } diff --git a/lib/src/dx/publish/publish.dart b/lib/src/dx/publish/publish.dart index 9051abee..0d35cb3f 100644 --- a/lib/src/dx/publish/publish.dart +++ b/lib/src/dx/publish/publish.dart @@ -1,7 +1,9 @@ -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; -import 'package:pubnub/src/dx/_endpoints/publish.dart'; -import 'package:pubnub/src/dx/_utils/utils.dart'; +import '../_utils/utils.dart'; +import '../_endpoints/publish.dart'; + +export '../_endpoints/publish.dart'; final _logger = injectLogger('pubnub.dx.publish'); diff --git a/lib/src/dx/push/push.dart b/lib/src/dx/push/push.dart index 5c6ca7e5..5f4d1929 100644 --- a/lib/src/dx/push/push.dart +++ b/lib/src/dx/push/push.dart @@ -1,26 +1,26 @@ -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; import 'package:pubnub/src/default.dart'; -import 'package:pubnub/src/dx/_utils/utils.dart'; -import 'package:pubnub/src/dx/_endpoints/push.dart'; +import '../_utils/utils.dart'; +import '../_endpoints/push.dart'; + +export '../_endpoints/push.dart'; // Managing device registrations for Push Notification Service mixin PushNotificationDx on Core { - /// It returns list of all channels to which device [deviceId] is registered - /// to receive push notifications + /// It returns list of all channels to which device [deviceId] is registered to receive push notifications. /// - /// [deviceId] is the id/token of the device - /// [gateway] indicates the backend to use for push service - /// it can be - /// * apns or apns2 for apple service - /// * gcm for google service - /// * mpns for microsoft service + /// [deviceId] is the id/token of the device. + /// [gateway] indicates the backend to use for push service: + /// * apns or apns2 for Apple service. + /// * gcm for Google service. + /// * mpns for Microsoft service. /// - /// If [gateway] is [PushGateway.apns2] then [topic] is mandatory to provide - /// [topic] is bundle id of the mobile application - /// [environment] denoting the environment of the mobile application for [PushGateway.apns2] - /// it can be either [Environment.development] or [Environment.production] - /// default value for [environment] is [Environment.development] + /// If [gateway] is [PushGateway.apns2] then [topic] is mandatory to provide. + /// [topic] is bundle id of the mobile application. + /// [environment] denoting the environment of the mobile application for [PushGateway.apns2], it can be either: + /// * [Environment.development] (which is the default value). + /// * [Environment.production]. Future listPushChannels( String deviceId, PushGateway gateway, {String topic, @@ -142,6 +142,7 @@ mixin PushNotificationDx on Core { } } +/// Represents a device used in push endpoints. class Device { final PubNub _core; final Keyset _keyset; diff --git a/lib/src/dx/signal/signal.dart b/lib/src/dx/signal/signal.dart index 890acde9..037216c7 100644 --- a/lib/src/dx/signal/signal.dart +++ b/lib/src/dx/signal/signal.dart @@ -1,7 +1,9 @@ -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; -import 'package:pubnub/src/dx/_utils/utils.dart'; -import 'package:pubnub/src/dx/_endpoints/signal.dart'; +import '../_utils/utils.dart'; +import '../_endpoints/signal.dart'; + +export '../_endpoints/signal.dart'; mixin SignalDx on Core { /// Publishes signal [message] to a [channel]. diff --git a/lib/src/dx/subscribe/extensions/keyset.dart b/lib/src/dx/subscribe/extensions/keyset.dart deleted file mode 100644 index cd97f601..00000000 --- a/lib/src/dx/subscribe/extensions/keyset.dart +++ /dev/null @@ -1,44 +0,0 @@ -import 'package:pubnub/src/core/keyset.dart'; -import 'package:pubnub/src/dx/subscribe/manager/manager.dart'; -import 'package:pubnub/src/dx/subscribe/subscription.dart'; - -extension SubscribeKeysetExtension on Keyset { - String get filterExpression => settings['#filterExpression']; - - /// Set filter expression for Subscribe message filtering. - set filterExpression(String value) => settings['#filterExpression'] = value; - - SubscriptionManager get subscriptionManager => - settings['#subscriptionManager']; - set subscriptionManager(SubscriptionManager s) => - settings['#subscriptionManager'] = s; - - Set get subscriptions => settings['#subscriptions']; - void addSubscription(Subscription s) { - if (settings['#subscriptions'] is! Set) { - settings['#subscriptions'] = {}; - } - - settings['#subscriptions'].add(s); - } - - void removeSubscription(Subscription s) { - if (settings['#subscriptions'] is! Set) { - settings['#subscriptions'] = {}; - } - - settings['#subscriptions'].remove(s); - } -} - -extension PresenceKeysetExtension on Keyset { - int get presenceTimeout => settings['#presenceTimeout']; - set presenceTimeout(int value) => settings['#presenceTimeout'] = value; - - int get heartbeatInterval => settings['#heartbeatInterval']; - set heartbeatInterval(int value) => settings['#heartbeatInterval'] = value; - - bool get suppressLeaveEvents => settings['#suppressLeaveEvents']; - set suppressLeaveEvents(bool value) => - settings['#suppressLeaveEvents'] = value; -} diff --git a/lib/src/dx/subscribe/manager/exceptions.dart b/lib/src/dx/subscribe/manager/exceptions.dart deleted file mode 100644 index 3226ab85..00000000 --- a/lib/src/dx/subscribe/manager/exceptions.dart +++ /dev/null @@ -1,5 +0,0 @@ -class SubscribeCancelledException implements Exception {} - -class SubscribeOutdatedException implements Exception {} - -class SubscribeTimeoutException implements Exception {} diff --git a/lib/src/dx/subscribe/manager/manager.dart b/lib/src/dx/subscribe/manager/manager.dart deleted file mode 100644 index 71c6b9bc..00000000 --- a/lib/src/dx/subscribe/manager/manager.dart +++ /dev/null @@ -1,224 +0,0 @@ -import 'dart:async'; - -import 'package:pubnub/src/core/core.dart'; -import 'package:pubnub/src/dx/_endpoints/subscribe.dart'; -import 'package:pubnub/src/state_machine/state_machine.dart'; - -import 'exceptions.dart'; - -final _logger = injectLogger('pubnub.dx.subscribe.manager'); - -class SubscribeFiber implements Fiber { - @override - int tries; - - SubscribeFiber(this.tries); - - @override - final action = null; - - @override - Future get future => Future.value(null); - - @override - int get id => -1; - - @override - Future run() async {} -} - -class SubscriptionManager { - Core core; - Keyset keyset; - - StreamController _messagesController; - Stream get messages => _messagesController.stream; - - Blueprint> _RequestMachine; - Blueprint> _SubscriptionMachine; - - StateMachine> machine; - - SubscriptionManager(this.core, this.keyset) { - _RequestMachine = Blueprint>() - ..define('send', from: ['initialized'], to: 'pending') - ..define('reject', from: ['pending'], to: 'rejected') - ..define('resolve', from: ['pending'], to: 'resolved') - ..define('timeout', from: ['pending'], to: 'rejected') - ..when('resolved', 'enters').exit(withPayload: true) - ..when('rejected', 'enters').exit(withPayload: true) - ..when('initialized', 'enters').callback((ctx) { - core.networking.handler().then((handler) { - if (ctx.machine.state == null) { - handler.cancel(); - return; - } - - ctx.update({'handler': handler, 'params': ctx.payload}); - ctx.machine.send('send', handler); - }); - }) - ..when('pending', 'enters').callback((ctx) { - (ctx.payload as IRequestHandler) - .response(ctx.context['params'].toRequest()) - .then((response) { - ctx.machine.send('resolve', response); - }).catchError((error) { - ctx.machine.send('reject', error); - }); - }) - ..when(null, 'enters').callback((ctx) { - if (ctx.context != null) { - ctx.context['handler']?.cancel(); - } - }); - - _SubscriptionMachine = Blueprint>() - ..define('fetch', - from: ['state.idle', 'state.fetching'], to: 'state.fetching') - ..define('idle', from: ['state.fetching', 'state.idle'], to: 'state.idle') - ..define('update', - from: ['state.idle', 'state.fetching'], to: 'state.idle') - ..when(null, 'exits').callback((ctx) { - ctx.update(ctx.payload); - }) - ..when('state.idle', 'enters').callback((ctx) { - _logger.silly( - 'TOP: Entering ${ctx.entering} from ${ctx.exiting} because of ${ctx.event} with ${ctx.payload}'); - - if (ctx.event == 'update') { - var newContext = {...ctx.context, ...ctx.payload}; - Completer completer = newContext['completer']; - newContext.remove('completer'); - ctx.update(newContext); - - if ((newContext['channels'].length > 0 || - newContext['channelGroups'].length > 0)) { - if (_messagesController == null || _messagesController.isClosed) { - _logger.silly('TOP: Creating the controller...'); - _messagesController = StreamController.broadcast(); - } - - ctx.machine.send('fetch'); - } else { - if (_messagesController != null) { - _logger.silly('TOP: Disposing the controller...'); - _messagesController.close(); - _messagesController = null; - } - } - - completer?.complete(); - } - }); - - _SubscriptionMachine - ..when('state.fetching').machine( - 'request', - _RequestMachine, - onParentEnter: (m, sm) { - Future.delayed(Duration(seconds: 270), () { - sm.send('timeout', SubscribeTimeoutException()); - }); - }, - onParentExit: (m, sm) { - _logger.silly('SUB: Parent is exiting when my state is ${sm.state}'); - if (sm.state == 'pending' || sm.state == 'initialized') { - _logger.silly('SUB: Cancelling pending request.'); - if (sm.context != null) sm.context['handler']?.cancel(); - } - }, - onBuild: (m, sm) { - _logger.silly('TOP: Entering state.fetching'); - var params = SubscribeParams(keyset, m.context['timetoken'].value, - region: m.context['region'], - channels: m.context['channels'], - channelGroups: m.context['channelGroups']); - - sm.enter('initialized', params); - }, - onEnter: (ctx, m, sm) { - _logger.silly( - 'SUB: Submachine entered ${ctx.exiting} from ${ctx.entering} because ${ctx.event} (with ${ctx.payload}).'); - }, - onExit: (ctx, m, sm) async { - switch (ctx.exiting) { - case 'resolved': - _logger.silly( - 'SUB: Submachine exited ${ctx.exiting} to ${ctx.entering} because ${ctx.event} (with ${ctx.payload}).'); - IResponse response = ctx.payload; - var object = await core.parser.decode(await response.text); - var result = SubscribeResult.fromJson(object); - - await _messagesController - .addStream(Stream.fromIterable(result.messages)); - - m.send('update', { - 'timetoken': result.timetoken, - 'region': result.region, - 'retry': 1 - }); - break; - case 'rejected': - var fiber = SubscribeFiber(m.context['retry'] ?? 1); - _logger.warning( - 'An exception has occured while running a subscribe loop (retry #${fiber.tries}).'); - var diagnostic = - core.supervisor.runDiagnostics(fiber, ctx.payload); - - if (diagnostic == null) { - return _messagesController.addError(ctx.payload); - } - - _logger.silly('Possible reason found: $diagnostic'); - - var resolutions = - core.supervisor.runStrategies(fiber, diagnostic); - - for (var resolution in resolutions) { - if (resolution is FailResolution) { - _messagesController.addError(ctx.payload); - } else if (resolution is DelayResolution) { - await Future.delayed(resolution.delay); - } else if (resolution is RetryResolution) { - m.send('update', {'retry': fiber.tries + 1}); - } - } - break; - } - }, - ); - - machine = _SubscriptionMachine.build(); - - machine.enter('state.idle', { - 'channels': {}, - 'channelGroups': {}, - 'timetoken': Timetoken(0), - 'region': null, - }); - - core.supervisor.events - .where((event) => event is NetworkIsDownEvent) - .listen((_event) { - machine.send('update', {'retry': (machine.context['retry'] ?? 1) + 1}); - }); - } - - Future update( - Map Function(Map ctx) cb) { - var completer = Completer(); - if (machine.state != 'state.idle') { - machine.send('fail', SubscribeOutdatedException()); - } - - machine.send('update', { - ...cb( - machine.context, - ), - 'completer': completer, - }); - - return completer.future; - } -} diff --git a/lib/src/dx/subscribe/subscribe.dart b/lib/src/dx/subscribe/subscribe.dart deleted file mode 100644 index a359124a..00000000 --- a/lib/src/dx/subscribe/subscribe.dart +++ /dev/null @@ -1,96 +0,0 @@ -import 'package:pubnub/src/core/core.dart'; -import 'package:pubnub/src/dx/_utils/utils.dart'; - -import 'extensions/keyset.dart'; -import 'subscription.dart'; -import 'manager/manager.dart'; - -import 'package:pubnub/src/dx/_endpoints/presence.dart'; - -export 'extensions/keyset.dart'; - -mixin SubscribeDx on Core { - /// Subscribes to [channels] and [channelGroups]. Returns [Subscription]. - Subscription subscription( - {Keyset keyset, - String using, - Set channels, - Set channelGroups, - bool withPresence}) { - keyset ??= super.keysets.get(using, defaultIfNameIsNull: true); - - keyset.subscriptionManager ??= SubscriptionManager(this, keyset); - - var subscription = Subscription( - channels ?? {}, channelGroups ?? {}, this, keyset, - withPresence: withPresence ?? false); - - return subscription; - } - - Future subscribe( - {Keyset keyset, - String using, - Set channels, - Set channelGroups, - bool withPresence}) async { - keyset ??= super.keysets.get(using, defaultIfNameIsNull: true); - - keyset.subscriptionManager ??= SubscriptionManager(this, keyset); - - var subscription = Subscription( - channels ?? {}, channelGroups ?? {}, this, keyset, - withPresence: withPresence ?? false); - - await subscription.subscribe(); - - return subscription; - } - - /// Unsubscribes from all channels and channel groups. - void unsubscribeAll() async { - for (var keyset in keysets.keysets) { - for (var sub in [...keyset.subscriptions]) { - await sub.unsubscribe(); - } - } - } - - /// Announce in [channels] and [channelGroups] that a device linked to the UUID in the keyset left. - Future announceLeave({ - Keyset keyset, - String using, - Set channels, - Set channelGroups, - }) { - keyset ??= super.keysets.get(using, defaultIfNameIsNull: true); - - Ensure(keyset).isNotNull('keyset'); - - return defaultFlow( - core: this, - params: LeaveParams(keyset, - channels: channels, channelGroups: channelGroups), - serialize: (object, [_]) => LeaveResult.fromJson(object)); - } - - /// Anounce in [channels] and [channelGroups] that a device linked to the UUID in the keyset is alive. - Future announceHeartbeat( - {Keyset keyset, - String using, - Set channels, - Set channelGroups, - int heartbeat}) { - keyset ??= super.keysets.get(using, defaultIfNameIsNull: true); - - Ensure(keyset).isNotNull('keyset'); - - return defaultFlow( - core: this, - params: HeartbeatParams(keyset, - channels: channels, - channelGroups: channelGroups, - heartbeat: heartbeat), - serialize: (object, [_]) => HeartbeatResult.fromJson(object)); - } -} diff --git a/lib/src/dx/subscribe/subscription.dart b/lib/src/dx/subscribe/subscription.dart deleted file mode 100644 index d24b3148..00000000 --- a/lib/src/dx/subscribe/subscription.dart +++ /dev/null @@ -1,158 +0,0 @@ -import 'dart:async'; - -import 'package:pubnub/src/core/core.dart'; -import 'package:pubnub/src/dx/_utils/disposable.dart'; - -import 'envelope.dart'; -import 'extensions/keyset.dart'; - -final _logger = injectLogger('pubnub.dx.subscribe.subscription'); - -class Subscription extends Disposable { - final Core _core; - final Keyset _keyset; - - /// List of channels that this subscription represents. - Set channels; - - /// List of channel groups that this subscription represents. - Set channelGroups; - - /// Indicates if presence is turned on for this subscription. - final bool withPresence; - - /// A broadcast stream of presence events. Will only emit when [withPresence] - /// is true. - Stream get presence => _streamController.stream - .where((envelope) => - presenceChannels.contains(envelope.channel) || - presenceChannels.contains(envelope.subscriptionPattern) || - presenceChannels.contains(envelope.subscriptionPattern)) - .map((envelope) => PresenceEvent.fromEnvelope(envelope)); - - /// A broadcast stream of messages in this subscription. - Stream get messages => _streamController.stream.where((envelope) => - channels.contains(envelope.channel) || - channels.contains(envelope.subscriptionPattern) || - channelGroups.contains(envelope.subscriptionPattern)); - - /// List of presence channels that this subscription represents. - /// - /// If [withPresence] is false, this should be empty. - Set get presenceChannels => withPresence - ? channels.map((channel) => '${channel}-pnpres').toSet() - : {}; - - /// List of presence channel groups that this subscription represents. - /// - /// If [withPresence] is false, this should be empty. - Set get presenceChannelGroups => withPresence - ? channelGroups.map((channelGroup) => '${channelGroup}-pnpres').toSet() - : {}; - - StreamSubscription _streamSubscription; - - final StreamController _streamController = - StreamController.broadcast(); - - Subscription(this.channels, this.channelGroups, this._core, this._keyset, - {this.withPresence}); - - /// Resubscribe to [channels] and [channelGroups]. - Future subscribe() async { - if (isDisposed) { - _logger.warning('Tried subscribing to a disposed subscription...'); - return; - } - - if (_streamSubscription == null) { - _logger.info('Subscribing to the stream...'); - _keyset.addSubscription(this); - - await _keyset.subscriptionManager.update((state) => { - 'channels': - state['channels'].union(channels).union(presenceChannels), - 'channelGroups': state['channelGroups'] - .union(channelGroups) - .union(presenceChannelGroups), - }); - - var s = _keyset.subscriptionManager.messages.where((envelope) { - return channels.contains(envelope['c']) || - channels.contains(envelope['b']) || - channelGroups.contains(envelope['b']) || - (withPresence && - (presenceChannels.contains(envelope['c']) || - presenceChannelGroups.contains(envelope['b']))); - }).asyncMap((envelope) async { - _logger.silly('Processing envelope: $envelope'); - if ((envelope['e'] == null || envelope['e'] == 4) && - !envelope['c'].endsWith('-pnpres') && - _keyset.cipherKey != null) { - envelope['d'] = await _core.parser - .decode(_core.crypto.decrypt(_keyset.cipherKey, envelope['d'])); - } - return Envelope.fromJson(envelope); - }); - _streamSubscription = s.listen((env) => _streamController.add(env)); - } - } - - /// Unsubscribe from [channels] and [channelGroups]. - Future unsubscribe() async { - if (isDisposed) { - _logger.warning('Tried unsubscribing from a disposed subscription...'); - return; - } - - if (_streamSubscription != null) { - _logger.info('Unsubscribing from the stream...'); - _keyset.removeSubscription(this); - - await _keyset.subscriptionManager.update((state) => { - 'channels': state['channels'] - .difference(channels) - .difference(presenceChannels), - 'channelGroups': state['channelGroups'] - .difference(channelGroups) - .difference(presenceChannelGroups), - }); - - await _streamSubscription.cancel(); - - _streamSubscription = null; - } else { - _logger.warning('Tried unsubscribing from an inactive stream...'); - } - } - - final Completer _didDispose = Completer(); - @override - Future get didDispose => _didDispose.future; - - bool _isDisposed = false; - - /// Whether this subscription is disposed. - /// - /// After a [Subscription] is disposed, you cannot resubscribe to it. - @override - bool get isDisposed => _isDisposed; - - /// Dispose of this subscription. - /// - /// If it's still subscribed, it will unsubscribe first. - /// - /// After disposing, you cannot use a subscription anymore. - @override - Future dispose() async { - _keyset.removeSubscription(this); - - await unsubscribe(); - - await _streamController.close(); - - _logger.verbose('Disposed Subscription.'); - _isDisposed = true; - _didDispose.complete(); - } -} diff --git a/lib/src/dx/supervisor/supervisor.dart b/lib/src/dx/supervisor/supervisor.dart new file mode 100644 index 00000000..349c5bd2 --- /dev/null +++ b/lib/src/dx/supervisor/supervisor.dart @@ -0,0 +1,14 @@ +import 'package:pubnub/core.dart'; + +export '../../core/supervisor/signals.dart'; + +/// @nodoc +mixin SupervisorDx on Core { + /// Internal signals. + Signals get signals => supervisor.signals; + + /// Breaks up pending connections and restarts them. + void reconnect() { + supervisor.notify(NetworkIsDownEvent()); + } +} diff --git a/lib/src/dx/time.dart b/lib/src/dx/time.dart index 994ae57b..a0b6927d 100644 --- a/lib/src/dx/time.dart +++ b/lib/src/dx/time.dart @@ -1,7 +1,7 @@ -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; -import 'package:pubnub/src/dx/_utils/utils.dart'; -import 'package:pubnub/src/dx/_endpoints/time.dart'; +import './_utils/utils.dart'; +import './_endpoints/time.dart'; mixin TimeDx on Core { /// Get current timetoken value from the PubNub network. diff --git a/lib/src/logging/logging.dart b/lib/src/logging/logging.dart index 6a953be1..e15227c1 100644 --- a/lib/src/logging/logging.dart +++ b/lib/src/logging/logging.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; /// A record that contains logged information. class LogRecord { @@ -66,7 +66,7 @@ class LogRecord { /// /// Prints `[$time] (${level.name}) $message`. static void Function(LogRecord) defaultPrinter = - LogRecord.createPrinter(r'[$time] (${level.name}) $message'); + LogRecord.createPrinter(r'[$time] (${level.name}) $scope: $message'); } /// A logger implementation that contains a stream of [LogRecord] records. diff --git a/lib/src/net/meta/diagnostics.dart b/lib/src/net/meta/diagnostics.dart index 35e77953..dc0d8d64 100644 --- a/lib/src/net/meta/diagnostics.dart +++ b/lib/src/net/meta/diagnostics.dart @@ -1,4 +1,4 @@ -import 'package:pubnub/pubnub.dart'; +import 'package:pubnub/core.dart'; class HostIsDownDiagnostic extends Diagnostic { final String host; @@ -13,6 +13,10 @@ class HostLookupFailedDiagnostic extends Diagnostic { const HostLookupFailedDiagnostic(this.host); } +class UnknownHttpExceptionDiagnostic extends Diagnostic { + const UnknownHttpExceptionDiagnostic(); +} + class TimeoutDiagnostic extends Diagnostic { final int timeout; @@ -20,13 +24,20 @@ class TimeoutDiagnostic extends Diagnostic { } final Map netDiagnosticsMap = { - RegExp(r'SocketException: Connection failed \(OS Error: Host is down, errno = 64\), address = ([a-zA-Z0-9\-\.]+), port = ([0-9]+)'): + RegExp(r'^SocketException: OS Error: Software caused connection abort, errno = 103, address = ([a-zA-Z0-9\-\.]+), port = ([0-9]+)$'): (match) => HostIsDownDiagnostic(match.group(1), int.parse(match.group(2))), - RegExp(r"SocketException: Failed host lookup: '([a-zA-Z0-9\-\.]+)' \(OS Error: nodename nor servname provided, or not known, errno = 8\)"): + RegExp(r'^SocketException: Connection failed \(OS Error: Host is down, errno = 64\), address = ([a-zA-Z0-9\-\.]+), port = ([0-9]+)$'): + (match) => + HostIsDownDiagnostic(match.group(1), int.parse(match.group(2))), + RegExp(r"^SocketException: Failed host lookup: '([a-zA-Z0-9\-\.]+)' \(OS Error: nodename nor servname provided, or not known, errno = 8\)$"): + (match) => HostLookupFailedDiagnostic(match.group(1)), + RegExp(r"^SocketException: Failed host lookup: '([a-zA-Z0-9\-\.]+)' \(OS Error: No address associated with hostname, errno = 7\)$"): (match) => HostLookupFailedDiagnostic(match.group(1)), - RegExp(r"Failed host lookup: '([a-zA-Z0-9\-\.]+)'"): (match) => + RegExp(r"^Failed host lookup: '([a-zA-Z0-9\-\.]+)'$"): (match) => HostLookupFailedDiagnostic(match.group(1)), - RegExp(r'Connecting timed out \[([0-9]+)ms\]'): (match) => + RegExp(r'^Connecting timed out \[([0-9]+)ms\]$'): (match) => TimeoutDiagnostic(int.parse(match.group(1))), + RegExp(r'^HttpException: , uri ='): (match) => + UnknownHttpExceptionDiagnostic(), }; diff --git a/lib/src/net/meta/meta.dart b/lib/src/net/meta/meta.dart index 1e957773..b9822a70 100644 --- a/lib/src/net/meta/meta.dart +++ b/lib/src/net/meta/meta.dart @@ -1,4 +1,4 @@ -import 'package:pubnub/pubnub.dart'; +import 'package:pubnub/core.dart'; import 'diagnostics.dart'; @@ -9,14 +9,13 @@ Diagnostic getNetworkDiagnostic(dynamic exception) { if (exception is PubNubRequestOtherException || exception is PubNubRequestTimeoutException) { var otherException = exception.additionalData; + var message = otherException.toString(); - if (otherException?.message != null) { - return netDiagnosticsMap.entries.map((entry) { - return entry.key.hasMatch(otherException.message) - ? entry.value(entry.key.matchAsPrefix(otherException.message)) - : null; - }).firstWhere((element) => element != null, orElse: () => null); - } + return netDiagnosticsMap.entries.map((entry) { + return entry.key.hasMatch(message) + ? entry.value(entry.key.matchAsPrefix(message)) + : null; + }).firstWhere((element) => element != null, orElse: () => null); } return null; diff --git a/lib/src/net/meta/retry_policy.dart b/lib/src/net/meta/retry_policy.dart index 6015a8b3..03701d41 100644 --- a/lib/src/net/meta/retry_policy.dart +++ b/lib/src/net/meta/retry_policy.dart @@ -1,24 +1,44 @@ import 'dart:math'; -import 'package:pubnub/pubnub.dart'; +import 'package:pubnub/core.dart'; +/// Retry policy represents a strategy used when retrying a network request. +/// +/// Can be implemented to create custom retry policy. abstract class RetryPolicy { + /// Maximum retries in non-subscribe calls. final int maxRetries; const RetryPolicy(this.maxRetries); + /// Returns a [Duration] that the SDK should wait before retrying. Duration getDelay(Fiber fiber); - factory RetryPolicy.linear({int backoff, int maxRetries = 5}) => - LinearRetryPolicy(backoff: backoff, maxRetries: maxRetries); - factory RetryPolicy.exponential({int maxRetries = 5}) => - ExponentialRetryPolicy(maxRetries: maxRetries); + /// Linear retry policy. Useful for development. + factory RetryPolicy.linear( + {int backoff = 5, int maxRetries = 5, int maximumDelay = 60000}) => + LinearRetryPolicy( + backoff: backoff, maxRetries: maxRetries, maximumDelay: maximumDelay); + + /// Exponential retry policy. Useful for production. + factory RetryPolicy.exponential( + {int maxRetries = 5, int maximumDelay = 60000}) => + ExponentialRetryPolicy( + maxRetries: maxRetries, maximumDelay: maximumDelay); } +/// Linear retry policy. +/// +/// Amount of delay between retries increases by fixed amount. class LinearRetryPolicy extends RetryPolicy { + /// Backoff amount in milliseconds final int backoff; - const LinearRetryPolicy({this.backoff, int maxRetries}) : super(maxRetries); + /// Maximum amount of milliseconds to wait until retry is executed + final int maximumDelay; + + const LinearRetryPolicy({this.backoff, int maxRetries, this.maximumDelay}) + : super(maxRetries); @override Duration getDelay(Fiber fiber) { @@ -27,12 +47,20 @@ class LinearRetryPolicy extends RetryPolicy { } } +/// Exponential retry policy. +/// +/// Amount of delay between retries doubles up to the maximum amount. class ExponentialRetryPolicy extends RetryPolicy { - const ExponentialRetryPolicy({int maxRetries}) : super(maxRetries); + /// Maximum amount of milliseconds to wait until retry is executed + final int maximumDelay; + + const ExponentialRetryPolicy({int maxRetries, this.maximumDelay}) + : super(maxRetries); @override Duration getDelay(Fiber fiber) { return Duration( - milliseconds: pow(2, fiber.tries - 1) * 1000 + Random().nextInt(1000)); + milliseconds: min(maximumDelay, + pow(2, fiber.tries - 1) * 1000 + Random().nextInt(1000))); } } diff --git a/lib/src/net/meta/strategy.dart b/lib/src/net/meta/strategy.dart index 71ce7c77..1517fdce 100644 --- a/lib/src/net/meta/strategy.dart +++ b/lib/src/net/meta/strategy.dart @@ -1,8 +1,9 @@ -import 'package:pubnub/pubnub.dart'; +import 'package:pubnub/core.dart'; import 'diagnostics.dart'; import 'retry_policy.dart'; +/// @nodoc class NetworkingStrategy extends Strategy { RetryPolicy retryPolicy; @@ -14,13 +15,14 @@ class NetworkingStrategy extends Strategy { return [Resolution.fail()]; } - if (fiber.tries >= retryPolicy?.maxRetries) { + if (!fiber.isSubscribe && fiber.tries >= retryPolicy?.maxRetries) { return [Resolution.fail()]; } if (diagnostic is HostIsDownDiagnostic || diagnostic is HostLookupFailedDiagnostic || - diagnostic is TimeoutDiagnostic) { + diagnostic is TimeoutDiagnostic || + diagnostic is UnknownHttpExceptionDiagnostic) { // Host is down. We should retry after some delay. return [ diff --git a/lib/src/net/net.dart b/lib/src/net/net.dart index 9b91ab19..391f66d6 100644 --- a/lib/src/net/net.dart +++ b/lib/src/net/net.dart @@ -1,21 +1,26 @@ import 'dart:async'; - import 'package:dio/dio.dart'; import 'package:pool/pool.dart'; -import 'package:pubnub/pubnub.dart'; +import 'package:pubnub/core.dart'; import 'meta/meta.dart'; import 'request_handler.dart'; +/// Default module used for networking in PubNub SDK. class NetworkingModule implements INetworkingModule { static int _requestCounter = 0; + /// Retry policy. + /// + /// If retry policy is null, then no retries are attempted. final RetryPolicy retryPolicy; final Pool _pool = Pool(10); final Dio _client = Dio(); + /// You can pass in your own [RetryPolicy]. NetworkingModule({this.retryPolicy}); + /// @nodoc @override Future handler() async { var requestId = _requestCounter++; @@ -24,6 +29,7 @@ class NetworkingModule implements INetworkingModule { client: _client, resource: _pool.request()); } + /// @nodoc @override void register(Core core) { core.supervisor.registerDiagnostic(getNetworkDiagnostic); diff --git a/lib/src/net/request_handler.dart b/lib/src/net/request_handler.dart index 2926064b..54512116 100644 --- a/lib/src/net/request_handler.dart +++ b/lib/src/net/request_handler.dart @@ -1,7 +1,7 @@ import 'package:dio/dio.dart' as dio; import 'package:pool/pool.dart' show PoolResource; -import 'package:pubnub/pubnub.dart'; +import 'package:pubnub/core.dart'; import 'response.dart'; final _logger = injectLogger('pubnub.networking.request_handler'); @@ -102,6 +102,7 @@ class RequestHandler extends IRequestHandler { if (!_cancelToken.isCancelled && !_isReleased) { _cancelToken.cancel(reason); _resource?.release(); + _isReleased = true; _logger.info('($_id) Request cancelled and resource released.'); } } diff --git a/lib/src/net/response.dart b/lib/src/net/response.dart index f099eb42..b60da3ad 100644 --- a/lib/src/net/response.dart +++ b/lib/src/net/response.dart @@ -1,7 +1,7 @@ import 'dart:convert'; import 'package:dio/dio.dart' as dio; -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; class Response extends IResponse { @override diff --git a/lib/src/parser/parser.dart b/lib/src/parser/parser.dart index b4593cc0..80a22c22 100644 --- a/lib/src/parser/parser.dart +++ b/lib/src/parser/parser.dart @@ -1,10 +1,11 @@ import 'dart:convert'; import 'package:xml/xml.dart' show XmlDocument; -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; import '../core/parser.dart'; +/// @nodoc abstract class Parser { Future decode(String input); Future encode(T input); @@ -45,6 +46,7 @@ const Map _parserMap = { 'xml': _XmlParser(), }; +/// @nodoc class ParserModule implements IParserModule { @override Future decode(String input, {String type = 'json'}) async { diff --git a/lib/src/state_machine/blueprint.dart b/lib/src/state_machine/blueprint.dart deleted file mode 100644 index d2dccd58..00000000 --- a/lib/src/state_machine/blueprint.dart +++ /dev/null @@ -1,97 +0,0 @@ -import 'state_machine.dart'; -import 'effect.dart'; - -import 'effects/send.dart'; -import 'effects/exit.dart'; -import 'effects/machine.dart'; -import 'effects/callback.dart'; - -class Definition { - List from; - State to; - String event; - Blueprint machine; -} - -class ReporterDefinition { - String edge; - dynamic state; - Effect effect; -} - -class BlueprintFactory { - Blueprint blueprint; - State state; - List edges; - - BlueprintFactory(this.blueprint, this.state, this.edges); - - void _addEffect(Effect effect) { - for (var edge in edges) { - blueprint._effects.add(ReporterDefinition() - ..edge = edge - ..state = state - ..effect = effect); - } - } - - void callback(Callback callback) => - _addEffect(CallbackEffect(callback)); - void send(String event, {dynamic payload, Duration after}) => _addEffect( - SendEffect(event, payload: payload, after: after)); - void exit({Duration after, bool withPayload}) => _addEffect( - ExitEffect(after: after, withPayload: withPayload)); - void effect(Effect effect) => _addEffect(effect); - void machine( - String name, Blueprint blueprint, - {MachineCallbackWithCtx onEnter, - MachineCallbackWithCtx onExit, - MachineCallback onBuild, - MachineCallback onParentEnter, - MachineCallback onParentExit}) => - _addEffect(MachineEffect( - name, blueprint, - onEnter: onEnter, - onExit: onExit, - onBuild: onBuild, - onParentEnter: onParentEnter, - onParentExit: onParentExit)); -} - -class Blueprint { - final List> _definitions = []; - final List _effects = []; - - Blueprint(); - - StateMachine build([StateMachine parent]) { - var machine = StateMachine(); - if (parent != null) machine.parent = parent; - - _definitions.forEach((def) { - machine.define(def.event, from: def.from, to: def.to); - }); - - _effects.forEach((def) { - machine.when(def.state, def.edge, def.effect); - }); - - return machine; - } - - void define(String event, {List from, State to, Blueprint machine}) { - _definitions.add(Definition() - ..from = from - ..to = to - ..event = event - ..machine = machine); - } - - BlueprintFactory when(State state, [String edge]) { - if (edge == null) { - return BlueprintFactory(this, state, ['enters', 'exits']); - } else { - return BlueprintFactory(this, state, [edge]); - } - } -} diff --git a/lib/src/state_machine/change.dart b/lib/src/state_machine/change.dart deleted file mode 100644 index 13568123..00000000 --- a/lib/src/state_machine/change.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'state_machine.dart'; - -class ContextChange { - ContextType from; - ContextType to; - - ContextChange(this.from, this.to); - - @override - String toString() { - return 'From: $from\nTo: $to'; - } -} - -class TransitionChange { - StateMachine machine; - State from; - State to; - - String event; - Payload payload; - - @override - String toString() { - return 'Transition of {$machine} [$event] (from $from, to $to) with $payload'; - } -} diff --git a/lib/src/state_machine/effect.dart b/lib/src/state_machine/effect.dart deleted file mode 100644 index 137d72b4..00000000 --- a/lib/src/state_machine/effect.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'state_machine.dart'; - -typedef Updater = Context Function(Context ctx); - -abstract class Effect { - void execute( - {State exiting, - State entering, - String event, - dynamic payload, - String edge, - StateMachine machine, - Updater updater}); -} diff --git a/lib/src/state_machine/effects/callback.dart b/lib/src/state_machine/effects/callback.dart deleted file mode 100644 index bbc9a9d4..00000000 --- a/lib/src/state_machine/effects/callback.dart +++ /dev/null @@ -1,37 +0,0 @@ -import '../effect.dart'; -import '../state_machine.dart'; - -class CallbackContext { - State exiting; - State entering; - String event; - dynamic payload; - String edge; - Context context; - StateMachine machine; - Updater update; - - CallbackContext(this.exiting, this.entering, this.event, this.payload, - this.edge, this.context, this.machine, this.update); -} - -typedef Callback = void Function( - CallbackContext ctx); - -class CallbackEffect extends Effect { - covariant Callback callback; - CallbackEffect(this.callback); - - @override - void execute( - {State exiting, - State entering, - String event, - payload, - String edge, - StateMachine machine, - Updater updater}) { - callback(CallbackContext(exiting, entering, event, payload, edge, - machine.context, machine, updater)); - } -} diff --git a/lib/src/state_machine/effects/exit.dart b/lib/src/state_machine/effects/exit.dart deleted file mode 100644 index 57d7dbb9..00000000 --- a/lib/src/state_machine/effects/exit.dart +++ /dev/null @@ -1,33 +0,0 @@ -import '../effect.dart'; -import '../state_machine.dart'; - -class ExitEffect extends Effect { - Duration after; - bool withPayload; - - ExitEffect({this.after, this.withPayload}); - - @override - void execute( - {State exiting, - State entering, - String event, - payload, - String edge, - StateMachine machine, - Updater updater}) { - if (after != null) { - Future.delayed(after, () => _exit(machine, payload)); - } else { - _exit(machine, payload); - } - } - - void _exit(StateMachine machine, dynamic payload) { - if (withPayload == true) { - machine.exit(payload); - } else { - machine.exit(); - } - } -} diff --git a/lib/src/state_machine/effects/machine.dart b/lib/src/state_machine/effects/machine.dart deleted file mode 100644 index 31b85ade..00000000 --- a/lib/src/state_machine/effects/machine.dart +++ /dev/null @@ -1,69 +0,0 @@ -import 'callback.dart'; -import '../effect.dart'; -import '../blueprint.dart'; -import '../state_machine.dart'; - -typedef MachineCallback = void Function( - StateMachine machine, StateMachine submachine); -typedef MachineCallbackWithCtx = void Function( - CallbackContext ctx, - StateMachine machine, - StateMachine submachine); - -class MachineEffect - extends Effect { - String name; - Blueprint blueprint; - - MachineCallback onParentExit; - MachineCallback onParentEnter; - - MachineCallbackWithCtx onExit; - MachineCallbackWithCtx onEnter; - MachineCallback onBuild; - - MachineEffect(this.name, this.blueprint, - {this.onExit, - this.onEnter, - this.onBuild, - this.onParentEnter, - this.onParentExit}); - - @override - void execute( - {State exiting, - State entering, - String event, - payload, - String edge, - StateMachine machine, - Updater updater}) { - if (edge == 'enters') { - var submachine = blueprint.build(machine); - - machine.register(name, submachine); - - if (onParentEnter != null) onParentEnter(machine, submachine); - if (onBuild != null) onBuild(machine, submachine); - - submachine.when(null, 'exits', - CallbackEffect((ctx) { - if (onEnter != null) onEnter(ctx, machine, submachine); - })); - - submachine.when(null, 'enters', - CallbackEffect((ctx) { - if (onExit != null) onExit(ctx, machine, submachine); - })); - } else if (edge == 'exits') { - var submachine = machine.get(name); - if (onParentExit != null) onParentExit(machine, submachine); - - if (submachine.state != null) { - submachine.exit(); - } - - machine.unregister(name, submachine); - } - } -} diff --git a/lib/src/state_machine/effects/send.dart b/lib/src/state_machine/effects/send.dart deleted file mode 100644 index 3cb41588..00000000 --- a/lib/src/state_machine/effects/send.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'dart:async'; - -import '../effect.dart'; -import '../state_machine.dart'; - -class SendEffect extends Effect { - String event; - dynamic payload; - Duration after; - - SendEffect(this.event, {this.payload, this.after}); - - Timer _timer; - - @override - void execute( - {State exiting, - State entering, - String event, - payload, - String edge, - StateMachine machine, - Updater updater}) { - if (event == '_enter') { - if (after != null) { - _timer = Timer(after, () => _send(machine)); - } else { - _send(machine); - } - } else if (event == '_exit') { - if (after != null) { - _timer.cancel(); - } - } - } - - void _send(StateMachine machine) { - machine.send(event, payload); - } -} diff --git a/lib/src/state_machine/state_machine.dart b/lib/src/state_machine/state_machine.dart deleted file mode 100644 index 5b949ccf..00000000 --- a/lib/src/state_machine/state_machine.dart +++ /dev/null @@ -1,124 +0,0 @@ -library pubnub.state_machine; - -import 'dart:async'; - -import 'change.dart'; -import 'effect.dart'; - -export 'blueprint.dart'; -export 'change.dart'; -export 'effect.dart'; - -class StateMachine { - State _currentState; - State get state => _currentState; - - Context _currentContext; - Context get context => _currentContext; - - StateMachine parent; - - final Map, State>> _defs = {}; - final Map>>> _effects = {}; - final Map _submachines = {}; - final Map _subs = {}; - - final StreamController> - _transitionsController = StreamController.broadcast(); - Stream> get transitions => - _transitionsController.stream; - - StateMachine(); - - void register(String name, StateMachine machine) { - _submachines[name] = machine; - _subs[name] = machine.transitions.listen((t) { - _transitionsController.add(t); - }); - } - - void unregister(String name, StateMachine machine) { - _subs[name].cancel(); - _submachines[name] = null; - } - - StateMachine get(String name) => _submachines[name]; - - void _report(State previous, State next, String edge, String event, - [dynamic payload]) { - var actions = _effects[state] ?? {}; - var effects = actions[edge] ?? []; - - for (var effect in effects) { - effect.execute( - exiting: previous, - entering: next, - edge: edge, - event: event, - payload: payload, - machine: this, - updater: ((Context ctx) => _currentContext = ctx), - ); - } - } - - void _reportPass(String event, [dynamic payload]) {} - - void _transition(State to, String event, [dynamic payload]) { - _transitionsController.add(TransitionChange() - ..machine = this - ..event = event - ..from = _currentState - ..to = to - ..payload = payload); - - var previousState = _currentState; - var nextState = to; - - _report(previousState, nextState, 'exits', event, payload); - _currentState = to; - _report(previousState, nextState, 'enters', event, payload); - } - - void enter(State state, [dynamic payload]) => - _transition(state, '_enter', payload); - void exit([dynamic payload]) { - _transition(null, '_exit', payload); - _transitionsController.close(); - } - - bool send(String event, [dynamic payload]) { - var legalStates = _defs[event]; - - if (legalStates == null) { - _reportPass(event, payload); - return false; - } - - var nextStateEntry = legalStates.entries.firstWhere( - (entry) => entry.key.contains(_currentState), - orElse: () => null); - - if (nextStateEntry == null) { - _reportPass(event, payload); - return false; - } - - var nextState = nextStateEntry.value; - - _transition(nextState, event, payload); - - return true; - } - - void define(String event, {List from, State to}) { - _defs[event] ??= {}; - _defs[event][from] = to; - } - - void when(State state, String edge, Effect effect) { - _effects[state] ??= {}; - _effects[state][edge] ??= []; - _effects[state][edge].add(effect); - } -} diff --git a/lib/src/dx/_endpoints/subscribe.dart b/lib/src/subscribe/_endpoints/subscribe.dart similarity index 93% rename from lib/src/dx/_endpoints/subscribe.dart rename to lib/src/subscribe/_endpoints/subscribe.dart index e0c12fae..2690f01b 100644 --- a/lib/src/dx/_endpoints/subscribe.dart +++ b/lib/src/subscribe/_endpoints/subscribe.dart @@ -1,5 +1,6 @@ -import 'package:pubnub/src/core/core.dart'; -import 'package:pubnub/src/dx/subscribe/extensions/keyset.dart'; +import 'package:pubnub/core.dart'; + +import '../extensions/keyset.dart'; class SubscribeParams extends Parameters { Keyset keyset; diff --git a/lib/src/dx/subscribe/envelope.dart b/lib/src/subscribe/envelope.dart similarity index 72% rename from lib/src/dx/subscribe/envelope.dart rename to lib/src/subscribe/envelope.dart index 68eabe1b..745ef4d8 100644 --- a/lib/src/dx/subscribe/envelope.dart +++ b/lib/src/subscribe/envelope.dart @@ -1,25 +1,9 @@ -import 'package:pubnub/src/core/core.dart'; - -enum MessageType { normal, signal, objects, messageAction, file } - -MessageType fromInt(int messageType) => const { - null: MessageType.normal, - 1: MessageType.signal, - 2: MessageType.objects, - 3: MessageType.messageAction, - 4: MessageType.file - }[messageType]; - -extension MessageTypeExtension on MessageType { - int toInt() => const { - MessageType.normal: null, - MessageType.signal: 1, - MessageType.objects: 2, - MessageType.messageAction: 3, - MessageType.file: 4, - }[this]; -} +import 'package:pubnub/core.dart'; +/// Represents a message received from a subscription. +/// +/// {@category Results} +/// {@category Basic Features} class Envelope { String shard; String subscriptionPattern; @@ -37,12 +21,13 @@ class Envelope { dynamic userMeta; dynamic originalMessage; + /// @nodoc Envelope.fromJson(dynamic object) { shard = object['a'] as String; subscriptionPattern = object['b'] as String; channel = object['c'] as String; payload = object['d']; - messageType = fromInt(object['e'] as int); + messageType = MessageTypeExtension.fromInt(object['e'] as int); flags = object['f'] as int; uuid = object['i'] != null ? UUID(object['i'] as String) : null; originalTimetoken = @@ -56,8 +41,10 @@ class Envelope { } } +/// Represents a presence action. enum PresenceAction { join, leave, timeout, stateChange } +/// @nodoc PresenceAction fromString(String action) => const { 'join': PresenceAction.join, 'leave': PresenceAction.leave, @@ -65,6 +52,9 @@ PresenceAction fromString(String action) => const { 'state-change': PresenceAction.stateChange }[action]; +/// Represents an event in presence. +/// +/// {@category Results} class PresenceEvent { Envelope envelope; diff --git a/lib/src/subscribe/extensions/keyset.dart b/lib/src/subscribe/extensions/keyset.dart new file mode 100644 index 00000000..49bc996d --- /dev/null +++ b/lib/src/subscribe/extensions/keyset.dart @@ -0,0 +1,9 @@ +import 'package:pubnub/src/core/keyset.dart'; + +/// @nodoc +extension SubscribeKeysetExtension on Keyset { + String get filterExpression => settings['#filterExpression']; + + /// Set filter expression for subscribe message filtering. + set filterExpression(String value) => settings['#filterExpression'] = value; +} diff --git a/lib/src/subscribe/manager.dart b/lib/src/subscribe/manager.dart new file mode 100644 index 00000000..3fb335b1 --- /dev/null +++ b/lib/src/subscribe/manager.dart @@ -0,0 +1,61 @@ +import 'dart:async'; + +import 'package:pubnub/core.dart'; + +import 'subscribe_loop/subscribe_loop_state.dart'; +import 'subscribe_loop/subscribe_loop.dart'; +import 'subscription.dart'; +import 'envelope.dart'; + +/// @nodoc +class Manager { + final Keyset _keyset; + + final Set _subscriptions = {}; + + SubscribeLoop _loop; + + Stream get envelopes => _loop.envelopes; + + Manager(Core core, this._keyset) { + _loop = SubscribeLoop(core, SubscribeLoopState(_keyset)); + } + + void _updateLoop() { + var channels = _subscriptions.fold>( + {}, + (s, sub) => s + ..addAll({ + ...sub.channels, + if (sub.withPresence == true) ...sub.presenceChannels + })); + var channelGroups = _subscriptions.fold>( + {}, + (s, sub) => s + ..addAll({ + ...sub.channelGroups, + if (sub.withPresence == true) ...sub.presenceChannelGroups + })); + + _loop.update((state) => + state.clone(channels: channels, channelGroups: channelGroups)); + } + + Subscription createSubscription( + {Set channels, Set channelGroups, bool withPresence}) { + var subscription = + Subscription(this, channels, channelGroups, withPresence); + + _subscriptions.add(subscription); + + _updateLoop(); + + return subscription; + } + + Future unsubscribeAll() async { + for (var subscription in _subscriptions) { + await subscription.cancel(); + } + } +} diff --git a/lib/src/subscribe/subscribe.dart b/lib/src/subscribe/subscribe.dart new file mode 100644 index 00000000..ad17a126 --- /dev/null +++ b/lib/src/subscribe/subscribe.dart @@ -0,0 +1,69 @@ +import 'package:pubnub/core.dart'; + +import 'manager.dart'; +import 'subscription.dart'; + +mixin SubscribeDx on Core { + final Map _managers = {}; + + Manager _getOrCreateManager(Keyset keyset) { + if (!_managers.containsKey(keyset)) { + _managers[keyset] = Manager(this, keyset); + } + + return _managers[keyset]; + } + + /// Subscribes to [channels] and [channelGroups]. + /// + /// Returned subscription is automatically resumed. + Subscription subscribe( + {Keyset keyset, + String using, + Set channels, + Set channelGroups, + bool withPresence}) { + keyset ??= keysets.obtain(keyset, using); + + var manager = _getOrCreateManager(keyset); + + var subscription = manager.createSubscription( + channels: channels, + channelGroups: channelGroups, + withPresence: withPresence); + + subscription.resume(); + + return subscription; + } + + /// Obtain a [Subscription] to [channels] and [channelGroups]. + /// + /// You need to manually resume the returned subscription. + Subscription subscription( + {Keyset keyset, + String using, + Set channels, + Set channelGroups, + bool withPresence}) { + keyset ??= keysets.obtain(keyset, using); + + var manager = _getOrCreateManager(keyset); + + var subscription = manager.createSubscription( + channels: channels, + channelGroups: channelGroups, + withPresence: withPresence); + + subscription.resume(); + + return subscription; + } + + /// Cancels all existing subscriptions. + Future unsubscribeAll() async { + for (var manager in _managers.values) { + await manager.unsubscribeAll(); + } + } +} diff --git a/lib/src/subscribe/subscribe_loop/subscribe_fiber.dart b/lib/src/subscribe/subscribe_loop/subscribe_fiber.dart new file mode 100644 index 00000000..6648fadf --- /dev/null +++ b/lib/src/subscribe/subscribe_loop/subscribe_fiber.dart @@ -0,0 +1,26 @@ +import 'package:pubnub/core.dart'; + +/// @nodoc +class SubscribeFiber implements Fiber { + @override + int tries; + + SubscribeFiber(this.tries); + + @override + final action = null; + + @override + final bool isSubscribe = true; + + @override + // TODO: implement future + final Future future = Future.value(null); + + @override + // TODO: implement id + final int id = -1; + + @override + Future run() async {} +} diff --git a/lib/src/subscribe/subscribe_loop/subscribe_loop.dart b/lib/src/subscribe/subscribe_loop/subscribe_loop.dart new file mode 100644 index 00000000..04a496a3 --- /dev/null +++ b/lib/src/subscribe/subscribe_loop/subscribe_loop.dart @@ -0,0 +1,159 @@ +import 'dart:async'; +import 'package:async/async.dart'; + +import 'package:pubnub/core.dart'; +import 'subscribe_loop_state.dart'; +import 'subscribe_fiber.dart'; +import '../envelope.dart'; +import '../_endpoints/subscribe.dart'; + +final _logger = injectLogger('pubnub.subscribe.subscribe_loop'); + +/// @nodoc +typedef UpdateCallback = SubscribeLoopState Function(SubscribeLoopState state); + +/// @nodoc +class UpdateException implements Exception {} + +/// @nodoc +class CancelException implements Exception {} + +/// @nodoc +Future invertFailure(Future future) async { + throw await future; +} + +/// @nodoc +Future withCancel(Future future, Future signal) async { + try { + var result = await Future.any([future, invertFailure(signal)]); + + if (result is T) { + return result; + } else { + throw result; + } + } catch (e) { + rethrow; + } +} + +/// @nodoc +class SubscribeLoop { + SubscribeLoopState _state; + Core core; + + SubscribeLoop(this.core, this._state) { + _messagesController = StreamController.broadcast( + onListen: () => update((state) => state.clone(isActive: true)), + onCancel: () => update((state) => state.clone(isActive: false))); + + _loop().pipe(_messagesController.sink); + + core.supervisor.signals.networkIsConnected + .listen((_) => update((state) => state)); + } + + StreamController _messagesController; + Stream get envelopes => _messagesController.stream; + + final StreamController _queueController = + StreamController.broadcast(); + + void update(UpdateCallback callback) { + var newState = callback(_state); + + _state = newState; + _logger.silly('State has been updated.'); + _queueController.add(CancelException()); + } + + Stream _loop() async* { + IRequestHandler handler; + var tries = 0; + + while (true) { + var queue = StreamQueue(_queueController.stream); + + try { + _logger.silly('Starting new loop iteration.'); + tries += 1; + var state = _state; + + if (!state.shouldRun) { + await queue.peek; + } + + handler = await withCancel(core.networking.handler(), queue.peek); + + var params = SubscribeParams(state.keyset, state.timetoken.value, + region: state.region, + channels: state.channels, + channelGroups: state.channelGroups); + + var response = + await withCancel(handler.response(params.toRequest()), queue.peek); + + core.supervisor.notify(NetworkIsUpEvent()); + + var object = await withCancel( + core.parser.decode(await response.text), queue.peek); + + var result = SubscribeResult.fromJson(object); + + _logger.silly( + 'Result: timetoken ${result.timetoken}, new messages: ${result.messages.length}'); + + yield* Stream.fromIterable(result.messages) + .map((object) => Envelope.fromJson(object)); + + _logger.silly('Updating the state...'); + + tries = 0; + + update((state) => + state.clone(timetoken: result.timetoken, region: result.region)); + } catch (exception) { + var fiber = SubscribeFiber(tries); + + if (handler != null && !handler.isCancelled) { + _logger.silly('Cancelling the handler...'); + handler.cancel(); + } + + if (exception is UpdateException || exception is CancelException) { + continue; + } + + _logger.warning( + 'An exception has occured while running a subscribe fiber (retry #${tries}).'); + var diagnostic = core.supervisor.runDiagnostics(fiber, exception); + + if (diagnostic == null) { + _logger.warning('No diagnostics found.'); + rethrow; + } + + _logger.silly('Possible reason found: $diagnostic'); + + var resolutions = core.supervisor.runStrategies(fiber, diagnostic); + + for (var resolution in resolutions) { + if (resolution is FailResolution) { + rethrow; + } else if (resolution is DelayResolution) { + await Future.delayed(resolution.delay); + } else if (resolution is RetryResolution) { + continue; + } else if (resolution is NetworkStatusResolution) { + core.supervisor.notify( + resolution.isUp ? NetworkIsUpEvent() : NetworkIsDownEvent()); + } + } + } finally { + handler = null; + await queue.cancel(immediate: true); + } + } + } +} diff --git a/lib/src/subscribe/subscribe_loop/subscribe_loop_state.dart b/lib/src/subscribe/subscribe_loop/subscribe_loop_state.dart new file mode 100644 index 00000000..4ab93cd2 --- /dev/null +++ b/lib/src/subscribe/subscribe_loop/subscribe_loop_state.dart @@ -0,0 +1,52 @@ +import 'package:pubnub/core.dart'; + +/// @nodoc +class SubscribeLoopState { + Keyset keyset; + + Timetoken timetoken; + int region; + + bool isActive; + + Set channels; + Set channelGroups; + + SubscribeLoopState(this.keyset, + {this.timetoken = const Timetoken(0), + this.region, + this.channels = const {}, + this.channelGroups = const {}, + this.isActive = false}); + + bool get shouldRun => + isActive && (channels.isNotEmpty || channelGroups.isNotEmpty); + + SubscribeLoopState clone( + {Timetoken timetoken, + int region, + Set channels, + Set channelGroups, + bool isActive}) => + SubscribeLoopState(keyset) + ..timetoken = timetoken ?? this.timetoken + ..region = region ?? this.region + ..channels = channels ?? this.channels + ..channelGroups = channelGroups ?? this.channelGroups + ..isActive = isActive ?? this.isActive; + + @override + bool operator ==(Object other) { + if (other is SubscribeLoopState) { + return isActive == other.isActive && + timetoken.value == other.timetoken.value && + region == other.region && + channels.containsAll(other.channels) && + other.channels.containsAll(channels) && + other.channelGroups.containsAll(channelGroups) && + channelGroups.containsAll(other.channelGroups); + } + + return false; + } +} diff --git a/lib/src/subscribe/subscription.dart b/lib/src/subscribe/subscription.dart new file mode 100644 index 00000000..d0de5362 --- /dev/null +++ b/lib/src/subscribe/subscription.dart @@ -0,0 +1,174 @@ +import 'dart:async'; + +import 'package:pubnub/core.dart'; + +import 'manager.dart'; +import 'envelope.dart'; + +final _logger = injectLogger('pubnub.subscription.subscription'); + +/// Represents a subscription to a set of channels and channel groups. +/// +/// Immutable. Can be paused and resumed multiple times. +/// After [cancel] is called, the subscription cannot be used again. +class Subscription { + final Manager _manager; + final bool _withPresence; + final Set _channels; + final Set _channelGroups; + + /// Whether this subscription receives presence events. + bool get withPresence => _withPresence; + + /// Set of channels that this subscription represents. + Set get channels => {..._channels ?? {}}; + + /// Set of channel groups that this subscription represents. + Set get channelGroups => {..._channelGroups ?? {}}; + + /// Whether this subscription has been cancelled. + bool get isCancelled => _cancelCompleter.isCompleted; + + /// Whether this subscription is currently paused. + bool get isPaused => _envelopeSubscription == null; + + /// Set of presence channels that are generated from set of channels + Set get presenceChannels => + channels.map((channel) => '${channel}-pnpres').toSet(); + + /// Set of presence channel groups that are generated from set of channel groups + Set get presenceChannelGroups => + channelGroups.map((channelGroup) => '${channelGroup}-pnpres').toSet(); + + /// Broadcast stream of messages in this subscription. + Stream get messages => + _envelopesController.stream.where((envelope) => + channels.contains(envelope.channel) || + channels.contains(envelope.subscriptionPattern) || + channelGroups.contains(envelope.subscriptionPattern)); + + /// Broadcast stream of presence events. + /// + /// Will only emit when [withPresence] is true. + Stream get presence => _envelopesController.stream + .where((envelope) => + presenceChannels.contains(envelope.channel) || + presenceChannels.contains(envelope.subscriptionPattern) || + presenceChannelGroups.contains(envelope.subscriptionPattern)) + .map((envelope) => PresenceEvent.fromEnvelope(envelope)); + + final Completer _cancelCompleter = Completer(); + + final StreamController _envelopesController = + StreamController.broadcast(); + + StreamSubscription _envelopeSubscription; + + Subscription( + this._manager, this._channels, this._channelGroups, this._withPresence); + + /// Resume currently paused subscription. + /// + /// If subscription is not paused, then this method is a no-op. + void resume() { + if (isCancelled) { + _logger + .warning('Tried resuming a subscription that is already cancelled.'); + return; + } + + if (!isPaused) { + _logger.silly('Resuming a subscription that is not paused is a no-op.'); + return; + } + + _logger.verbose('Resuming subscription.'); + + _envelopeSubscription = _manager.envelopes.where((envelope) { + // If message was sent to one of our channels. + if (channels.contains(envelope.channel)) { + return true; + } + + // If message was sent to one of our channel patterns. + if (channels.contains(envelope.subscriptionPattern)) { + return true; + } + + // If message was sent to one of our channel groups. + if (channelGroups.contains(envelope.subscriptionPattern)) { + return true; + } + + // If presence is enabled... + + if (withPresence) { + // ...and message was sent to one of our presence channels. + if (presenceChannels.contains(envelope.channel)) { + return true; + } + + // ...and message was sent to one of our presence channel groups. + if (presenceChannelGroups.contains(envelope.subscriptionPattern)) { + return true; + } + } + + // Otherwise this is not our message. + return false; + }).listen((envelope) { + _envelopesController.add(envelope); + }); + } + + /// Pause subscription. + /// + /// Pausing subscription will prevent the [messages] and [presence] streams from emitting messages. + /// Keep in mind that you may miss messages while subscription is paused. + /// If subscription is currently paused, this method is a no-op. + void pause() { + if (isCancelled) { + _logger + .warning('Tried to pause a subscription that is already cancelled.'); + return; + } + + if (isPaused) { + _logger + .silly('Pausing a subscription that is already paused is a no-op.'); + return; + } + + _logger.info('Pausing subscription.'); + + _envelopeSubscription.cancel(); + _envelopeSubscription = null; + } + + /// Cancels the subscription. + /// + /// This disposes internal streams, so the subscription becomes unusable. + Future cancel() async { + if (isCancelled) { + _logger.warning( + 'Tried cancelling a subscription that is already cancelled.'); + return; + } + + _logger.verbose('Subscription cancelled.'); + + await _envelopeSubscription.cancel(); + await _envelopesController.close(); + + _cancelCompleter.complete(); + } + + /// Alias for [cancel]. + Future dispose() => cancel(); + + /// Alias for [resume]. + void subscribe() => resume(); + + /// Alias for [pause]. + void unsubscribe() => pause(); +} diff --git a/pubspec.lock b/pubspec.lock index cbc75142..73c1858e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -30,7 +30,7 @@ packages: source: hosted version: "0.6.4" async: - dependency: transitive + dependency: "direct main" description: name: async url: "https://pub.dartlang.org" diff --git a/pubspec.yaml b/pubspec.yaml index ffe1bb3c..1f91bf9d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: pubnub description: PubNub SDK v5 for Dart lang (with Flutter support) that allows you to create real-time applications -version: 2.0.1 -homepage: https://www.pubnub.com/docs +version: 3.0.0 +homepage: https://www.pubnub.com/docs/platform/home environment: sdk: ">=2.6.0 <3.0.0" @@ -16,6 +16,7 @@ dependencies: convert: ^2.1.0 pedantic: ^1.9.0 xml: ^4.3.0 + async: ^2.4.0 dev_dependencies: test: 1.11.1 test_coverage: 0.4.1 diff --git a/test/core/logging_test.dart b/test/core/logging_test.dart index 421035e0..5ef63dbd 100644 --- a/test/core/logging_test.dart +++ b/test/core/logging_test.dart @@ -1,7 +1,6 @@ import 'package:test/test.dart'; -import 'package:pubnub/src/core/core.dart'; -import 'package:pubnub/src/core/logging/dummy_logger.dart'; +import 'package:pubnub/core.dart'; class FakeLogger extends ILogger { List messages = []; diff --git a/test/crypto/crypto_test.dart b/test/crypto/crypto_test.dart index 7909246f..86a85ad4 100644 --- a/test/crypto/crypto_test.dart +++ b/test/crypto/crypto_test.dart @@ -1,4 +1,4 @@ -import 'package:pubnub/pubnub.dart'; +import 'package:pubnub/core.dart'; import 'package:pubnub/src/crypto/crypto.dart'; import 'package:test/test.dart'; diff --git a/test/dx/channel_test.dart b/test/dx/channel_test.dart index 3eb5a4d5..50cd4486 100644 --- a/test/dx/channel_test.dart +++ b/test/dx/channel_test.dart @@ -1,6 +1,8 @@ -import 'package:pubnub/pubnub.dart'; import 'package:test/test.dart'; +import 'package:pubnub/pubnub.dart'; +import 'package:pubnub/core.dart'; + import 'package:pubnub/src/dx/channel/channel.dart'; import 'package:pubnub/src/dx/channel/channel_history.dart'; diff --git a/test/dx/file_test.dart b/test/dx/file_test.dart index 112a0c9f..1191eb51 100644 --- a/test/dx/file_test.dart +++ b/test/dx/file_test.dart @@ -2,11 +2,12 @@ import 'dart:convert'; import 'package:test/test.dart'; import 'package:pubnub/pubnub.dart'; +import 'package:pubnub/core.dart'; + import 'package:pubnub/src/dx/files/files.dart'; import 'package:pubnub/src/dx/_endpoints/files.dart'; import '../net/fake_net.dart'; - part 'fixtures/files.dart'; void main() { @@ -168,7 +169,7 @@ void main() { expect(result, isA()); }); - test('#getFileUrl', () async { + test('#getFileUrl', () { var result = pubnub.files.getFileUrl('channel', 'fileId', 'fileName'); expect(result, isA()); expect(result.toString(), equals(_getFileUrl)); @@ -182,5 +183,18 @@ void main() { .decryptFile(encryptedData, cipherKey: CipherKey.fromUtf8('secret')); expect(utf8.decode(decryptedData), equals(input)); }); + + test('#getFileUrl with secretKey and auth', () { + pubnub = PubNub( + defaultKeyset: Keyset( + subscribeKey: 'test', + publishKey: 'test', + secretKey: 'test', + authKey: 'test')); + var result = + pubnub.files.getFileUrl('my_channel', 'file-id', 'cat_picture.jpg'); + expect(result.queryParameters, contains('signature')); + expect(result.queryParameters, contains('auth')); + }); }); } diff --git a/test/dx/fixtures/files.dart b/test/dx/fixtures/files.dart index ed86fa13..017441d3 100644 --- a/test/dx/fixtures/files.dart +++ b/test/dx/fixtures/files.dart @@ -35,39 +35,39 @@ final _generateFileUploadUrlResponse = ''' "expiration_date": "2020-04-03T22:44:47Z", "form_fields": [ { - "name": "tagging", + "key": "tagging", "value": "ObjectTTL1000" }, { - "name": "key", + "key": "key", "value": "file-upload/5a3eb38c-483a-4b25-ac01-c4e20deba6d6/test_image.jpg" }, { - "name": "Content-Type", + "key": "Content-Type", "value": "binary/octet-stream" }, { - "name": "X-Amz-Credential", + "key": "X-Amz-Credential", "value": "xxx/20200403/us-west-2/s3/aws4_request" }, { - "name": "X-Amz-Security-Token", + "key": "X-Amz-Security-Token", "value": "lgnwegn2mg202j4g0g2mg04g02gj2" }, { - "name": "X-Amz-Algorithm", + "key": "X-Amz-Algorithm", "value": "AWS4-HMAC-SHA256" }, { - "name": "X-Amz-Date", + "key": "X-Amz-Date", "value": "20200403T212950Z" }, { - "name": "Policy", + "key": "Policy", "value": "CnsgImV4cGlyYXRpb24iOiAiMjAyMC0wNC0wM1QyMToy..." }, { - "name": "X-Amz-Signature", + "key": "X-Amz-Signature", "value": "1fbaad6738c6cd4c7eec2afe4cb2553a1e9cd2be690fdc2ecdc6e26f60a3781a" } ] @@ -106,4 +106,4 @@ final _downloadFileUrl = 'https://ps.pndsn.com/v1/files/test/channels/channel/files/5a3eb38c-483a-4b25-ac01-c4e20deba6d6/cat_file.jpg?pnsdk=PubNub-Dart%2F${PubNub.version}'; final _getFileUrl = - 'https://ps.pndsn.com/v1/files/test/channels/channel/files/fileId/fileName'; + 'https://ps.pndsn.com/v1/files/test/channels/channel/files/fileId/fileName?pnsdk=PubNub-Dart%2F${PubNub.version}'; diff --git a/test/dx/history_test.dart b/test/dx/history_test.dart index 4573aaee..61c691a3 100644 --- a/test/dx/history_test.dart +++ b/test/dx/history_test.dart @@ -1,6 +1,8 @@ import 'package:test/test.dart'; import 'package:pubnub/pubnub.dart'; +import 'package:pubnub/core.dart'; +import 'package:pubnub/src/dx/_endpoints/history.dart'; import '../net/fake_net.dart'; part './fixtures/history.dart'; diff --git a/test/dx/message_action_test.dart b/test/dx/message_action_test.dart index 5fccfa61..83f87727 100644 --- a/test/dx/message_action_test.dart +++ b/test/dx/message_action_test.dart @@ -1,7 +1,8 @@ import 'package:test/test.dart'; import 'package:pubnub/pubnub.dart'; -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; + import 'package:pubnub/src/dx/_utils/utils.dart'; import 'package:pubnub/src/dx/_endpoints/message_action.dart'; @@ -162,9 +163,9 @@ void main() { method: 'GET', path: 'v1/message-actions/test/channel/test?pnsdk=PubNub-Dart%2F${PubNub.version}', - ).then(status: 200, body: _fetchMessageActionError); - var response = await pubnub.fetchMessageActions('test'); - expect(response.status, 400); + ).then(status: 400, body: _fetchMessageActionError); + + expect(pubnub.fetchMessageActions('test'), throwsA(anything)); }); test('delete message action throws when channel is empty', () async { diff --git a/test/dx/objects/channel_metadata_test.dart b/test/dx/objects/channel_metadata_test.dart index cb2e8384..ca284488 100644 --- a/test/dx/objects/channel_metadata_test.dart +++ b/test/dx/objects/channel_metadata_test.dart @@ -1,7 +1,9 @@ import 'package:test/test.dart'; + import 'package:pubnub/pubnub.dart'; -import '../../net/fake_net.dart'; +import 'package:pubnub/core.dart'; +import '../../net/fake_net.dart'; part 'fixtures/channel_metadata.dart'; void main() { diff --git a/test/dx/objects/membership_metadata_test.dart b/test/dx/objects/membership_metadata_test.dart index 5c14c50c..80f50e6e 100644 --- a/test/dx/objects/membership_metadata_test.dart +++ b/test/dx/objects/membership_metadata_test.dart @@ -1,7 +1,9 @@ import 'package:test/test.dart'; + import 'package:pubnub/pubnub.dart'; -import '../../net/fake_net.dart'; +import 'package:pubnub/core.dart'; +import '../../net/fake_net.dart'; part 'fixtures/membership_metadata.dart'; void main() { diff --git a/test/dx/objects/uuid_metadata_test.dart b/test/dx/objects/uuid_metadata_test.dart index 6e17d51c..db12dc69 100644 --- a/test/dx/objects/uuid_metadata_test.dart +++ b/test/dx/objects/uuid_metadata_test.dart @@ -1,7 +1,9 @@ import 'package:test/test.dart'; + import 'package:pubnub/pubnub.dart'; -import '../../net/fake_net.dart'; +import 'package:pubnub/core.dart'; +import '../../net/fake_net.dart'; part 'fixtures/uuid_metadata.dart'; void main() { diff --git a/test/dx/pam_test.dart b/test/dx/pam_test.dart index 8a10dd09..362df2bf 100644 --- a/test/dx/pam_test.dart +++ b/test/dx/pam_test.dart @@ -1,6 +1,6 @@ import 'package:test/test.dart'; -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; import 'package:pubnub/src/default.dart'; import 'package:pubnub/src/dx/_utils/utils.dart'; import 'package:pubnub/src/dx/pam/pam.dart'; diff --git a/test/dx/publish_test.dart b/test/dx/publish_test.dart index 620b351a..6283f049 100644 --- a/test/dx/publish_test.dart +++ b/test/dx/publish_test.dart @@ -1,10 +1,10 @@ import 'package:test/test.dart'; import 'package:pubnub/pubnub.dart'; -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; +import 'package:pubnub/src/dx/_utils/utils.dart'; import '../net/fake_net.dart'; - part './fixtures/publish.dart'; void main() { diff --git a/test/dx/push_test.dart b/test/dx/push_test.dart index 3c7d13cb..001808f5 100644 --- a/test/dx/push_test.dart +++ b/test/dx/push_test.dart @@ -1,5 +1,10 @@ import 'package:test/test.dart'; + import 'package:pubnub/pubnub.dart'; +import 'package:pubnub/core.dart'; +import 'package:pubnub/src/dx/_endpoints/push.dart'; +import 'package:pubnub/src/dx/_utils/utils.dart'; + import '../net/fake_net.dart'; part './fixtures/push.dart'; diff --git a/test/dx/signal_test.dart b/test/dx/signal_test.dart index ba6fa0c1..2d7751d5 100644 --- a/test/dx/signal_test.dart +++ b/test/dx/signal_test.dart @@ -1,9 +1,9 @@ import 'package:test/test.dart'; import 'package:pubnub/pubnub.dart'; +import 'package:pubnub/core.dart'; import '../net/fake_net.dart'; - part './fixtures/signal.dart'; void main() { diff --git a/test/dx/time_test.dart b/test/dx/time_test.dart index e2f82bd0..ad2dd6d8 100644 --- a/test/dx/time_test.dart +++ b/test/dx/time_test.dart @@ -1,6 +1,7 @@ import 'package:test/test.dart'; import 'package:pubnub/pubnub.dart'; +import 'package:pubnub/core.dart'; import '../net/fake_net.dart'; diff --git a/test/dx/utils_test.dart b/test/dx/utils_test.dart index 8166d199..1c7fa0f0 100644 --- a/test/dx/utils_test.dart +++ b/test/dx/utils_test.dart @@ -1,5 +1,5 @@ import 'package:test/test.dart'; -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; import 'package:pubnub/src/dx/_utils/utils.dart'; void main() { diff --git a/test/logging/logging_test.dart b/test/logging/logging_test.dart index 475ee4e2..340373f8 100644 --- a/test/logging/logging_test.dart +++ b/test/logging/logging_test.dart @@ -1,6 +1,6 @@ import 'package:test/test.dart'; -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; import 'package:pubnub/src/logging/logging.dart'; void main() { diff --git a/test/net/fake_net.dart b/test/net/fake_net.dart index ac39dd83..a3813f63 100644 --- a/test/net/fake_net.dart +++ b/test/net/fake_net.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'dart:convert'; -import 'package:pubnub/src/core/core.dart'; +import 'package:pubnub/core.dart'; class MockException extends PubNubException { MockException(String message) : super(message);