diff --git a/lib/spinify.dart b/lib/spinify.dart index 46a3b73..7fdc9f0 100644 --- a/lib/spinify.dart +++ b/lib/spinify.dart @@ -1,15 +1,19 @@ library spinify; +export 'package:fixnum/fixnum.dart'; + export 'src/model/channel_push.dart'; export 'src/model/client_info.dart'; export 'src/model/command.dart'; export 'src/model/config.dart'; +export 'src/model/exception.dart'; export 'src/model/history.dart'; export 'src/model/jwt.dart'; -export 'src/model/metric.dart' show SpinifyMetrics; +export 'src/model/metric.dart'; export 'src/model/presence_stats.dart'; export 'src/model/pushes_stream.dart'; export 'src/model/reply.dart'; +export 'src/model/spinify_interface.dart'; export 'src/model/state.dart'; export 'src/model/states_stream.dart'; export 'src/model/stream_position.dart'; @@ -17,4 +21,6 @@ export 'src/model/subscription.dart'; export 'src/model/subscription_config.dart'; export 'src/model/subscription_state.dart'; export 'src/model/subscription_states_stream.dart'; +export 'src/model/transport_interface.dart'; export 'src/spinify_impl.dart' show Spinify; +export 'src/transport_fake.dart'; diff --git a/lib/spinify_developer.dart b/lib/spinify_developer.dart deleted file mode 100644 index 2479f1e..0000000 --- a/lib/spinify_developer.dart +++ /dev/null @@ -1,7 +0,0 @@ -library spinify.developer; - -export 'spinify.dart'; -export 'src/model/exception.dart'; -export 'src/model/spinify_interface.dart'; -export 'src/model/transport_interface.dart'; -export 'src/transport_fake.dart'; diff --git a/lib/src/model/metric.dart b/lib/src/model/metric.dart index 3ca2503..a7d7b23 100644 --- a/lib/src/model/metric.dart +++ b/lib/src/model/metric.dart @@ -40,13 +40,19 @@ sealed class SpinifyMetrics implements Comparable { /// The current state of the client. abstract final SpinifyState state; - /* - /// The total number of messages & size of bytes sent. - final ({BigInt count, BigInt size}) transferred; + /// The total number of bytes sent. + abstract final BigInt bytesSent; + + /// The total number of bytes received. + abstract final BigInt bytesReceived; + + /// The total number of messages sent. + abstract final BigInt messagesSent; - /// The total number of messages & size of bytes received. - final ({BigInt count, BigInt size}) received; + /// The total number of messages received. + abstract final BigInt messagesReceived; + /* /// The number of subscriptions. final ({ SpinifySubscriptionCount client, @@ -97,9 +103,10 @@ sealed class SpinifyMetrics implements Comparable { String toString() => 'SpinifyMetrics{}'; } -@internal +/// {@macro metrics} @immutable final class SpinifyMetrics$Immutable extends SpinifyMetrics { + /// {@macro metrics} const SpinifyMetrics$Immutable(); @override @@ -134,10 +141,23 @@ final class SpinifyMetrics$Immutable extends SpinifyMetrics { @override DateTime? get lastDisconnectAt => throw UnimplementedError(); + + @override + BigInt get bytesReceived => throw UnimplementedError(); + + @override + BigInt get bytesSent => throw UnimplementedError(); + + @override + BigInt get messagesReceived => throw UnimplementedError(); + + @override + BigInt get messagesSent => throw UnimplementedError(); } -@internal +/// {@macro metrics} final class SpinifyMetrics$Mutable extends SpinifyMetrics { + /// {@macro metrics} SpinifyMetrics$Mutable(); @override @@ -173,5 +193,17 @@ final class SpinifyMetrics$Mutable extends SpinifyMetrics { @override DateTime? lastDisconnectAt; + @override + BigInt bytesReceived = BigInt.zero; + + @override + BigInt bytesSent = BigInt.zero; + + @override + BigInt messagesReceived = BigInt.zero; + + @override + BigInt messagesSent = BigInt.zero; + SpinifyMetrics$Immutable freeze() => const SpinifyMetrics$Immutable(); } diff --git a/lib/src/model/transport_interface.dart b/lib/src/model/transport_interface.dart index 890e98b..c518d91 100644 --- a/lib/src/model/transport_interface.dart +++ b/lib/src/model/transport_interface.dart @@ -1,30 +1,32 @@ import 'command.dart'; import 'config.dart'; +import 'metric.dart'; import 'reply.dart'; /// Create a Spinify transport /// (e.g. WebSocket or gRPC with JSON or Protocol Buffers). -typedef SpinifyTransportBuilder = Future Function( +typedef SpinifyTransportBuilder = Future Function({ /// URL for the connection - String url, + required String url, /// Spinify client configuration - SpinifyConfig config, -); + required SpinifyConfig config, + + /// Metrics + required SpinifyMetrics$Mutable metrics, + + /// Callback for reply messages + required void Function(SpinifyReply reply) onReply, + + /// Callback for disconnect event + required void Function() onDisconnect, +}); /// Spinify transport interface. abstract interface class ISpinifyTransport { /// Send command to the server. Future send(SpinifyCommand command); - /// Set handler for [SpinifyReply] messages. - // ignore: avoid_setters_without_getters - set onReply(void Function(SpinifyReply reply) handler); - - /// Set handler for connection close event. - // ignore: avoid_setters_without_getters - set onDisconnect(void Function() handler); - /// Disconnect from the server. /// Client if not needed anymore. Future disconnect([int? code, String? reason]); diff --git a/lib/src/spinify_impl.dart b/lib/src/spinify_impl.dart index e06e776..1d31971 100644 --- a/lib/src/spinify_impl.dart +++ b/lib/src/spinify_impl.dart @@ -317,9 +317,15 @@ base mixin SpinifyConnectionMixin _setState(SpinifyState$Connecting(url: _metrics.reconnectUrl = url)); // Create new transport. - _transport = await _createTransport(url, config) - ..onReply = _onReply - ..onDisconnect = () => _onDisconnected().ignore(); + _transport = await _createTransport( + url: url, + config: config, + metrics: _metrics, + onReply: _onReply, + onDisconnect: _onDisconnected, + ); + // ..onReply = _onReply + // ..onDisconnect = () => _onDisconnected().ignore(); // Prepare connect request. final SpinifyConnectRequest request; diff --git a/lib/src/transport_fake.dart b/lib/src/transport_fake.dart index 8c198eb..591f0e6 100644 --- a/lib/src/transport_fake.dart +++ b/lib/src/transport_fake.dart @@ -1,17 +1,36 @@ +// ignore_for_file: avoid_setters_without_getters + import 'dart:async'; import 'package:fixnum/fixnum.dart'; import 'model/command.dart'; +import 'model/config.dart'; +import 'model/metric.dart'; import 'model/reply.dart'; import 'model/transport_interface.dart'; /// Create a fake Spinify transport. -Future $createFakeSpinifyTransport( - String url, - Map headers, -) async { - final transport = SpinifyTransportFake(); +Future $createFakeSpinifyTransport({ + /// URL for the connection + required String url, + + /// Spinify client configuration + required SpinifyConfig config, + + /// Metrics + required SpinifyMetrics$Mutable metrics, + + /// Callback for reply messages + required void Function(SpinifyReply reply) onReply, + + /// Callback for disconnect event + required void Function() onDisconnect, +}) async { + final transport = SpinifyTransportFake() + ..metrics = metrics + ..onReply = onReply + ..onDisconnect = onDisconnect; await transport._connect(url); return transport; } @@ -165,13 +184,14 @@ class SpinifyTransportFake implements ISpinifyTransport { }, ); - @override - // ignore: avoid_setters_without_getters + /// Metrics + late SpinifyMetrics$Mutable metrics; + + /// Callback for reply messages set onReply(void Function(SpinifyReply reply) handler) => _onReply = handler; void Function(SpinifyReply reply)? _onReply; - @override - // ignore: avoid_setters_without_getters + /// Callback for disconnect event set onDisconnect(void Function() handler) => _onDisconnect = handler; void Function()? _onDisconnect; diff --git a/lib/src/transport_ws_pb_js.dart b/lib/src/transport_ws_pb_js.dart index 3456831..8aa03f3 100644 --- a/lib/src/transport_ws_pb_js.dart +++ b/lib/src/transport_ws_pb_js.dart @@ -1,12 +1,26 @@ import 'package:meta/meta.dart'; import 'model/config.dart'; +import 'model/metric.dart'; +import 'model/reply.dart'; import 'model/transport_interface.dart'; /// Create a WebSocket Protocol Buffers transport. @internal -Future $create$WS$PB$Transport( - String url, - SpinifyConfig config, -) => +Future $create$WS$PB$Transport({ + /// URL for the connection + required String url, + + /// Spinify client configuration + required SpinifyConfig config, + + /// Metrics + required SpinifyMetrics$Mutable metrics, + + /// Callback for reply messages + required void Function(SpinifyReply reply) onReply, + + /// Callback for disconnect event + required void Function() onDisconnect, +}) => throw UnimplementedError(); diff --git a/lib/src/transport_ws_pb_stub.dart b/lib/src/transport_ws_pb_stub.dart index 3456831..8aa03f3 100644 --- a/lib/src/transport_ws_pb_stub.dart +++ b/lib/src/transport_ws_pb_stub.dart @@ -1,12 +1,26 @@ import 'package:meta/meta.dart'; import 'model/config.dart'; +import 'model/metric.dart'; +import 'model/reply.dart'; import 'model/transport_interface.dart'; /// Create a WebSocket Protocol Buffers transport. @internal -Future $create$WS$PB$Transport( - String url, - SpinifyConfig config, -) => +Future $create$WS$PB$Transport({ + /// URL for the connection + required String url, + + /// Spinify client configuration + required SpinifyConfig config, + + /// Metrics + required SpinifyMetrics$Mutable metrics, + + /// Callback for reply messages + required void Function(SpinifyReply reply) onReply, + + /// Callback for disconnect event + required void Function() onDisconnect, +}) => throw UnimplementedError(); diff --git a/lib/src/transport_ws_pb_vm.dart b/lib/src/transport_ws_pb_vm.dart index 69e1a35..9d4d74b 100644 --- a/lib/src/transport_ws_pb_vm.dart +++ b/lib/src/transport_ws_pb_vm.dart @@ -7,6 +7,7 @@ import 'package:protobuf/protobuf.dart' as pb; import 'model/command.dart'; import 'model/config.dart'; +import 'model/metric.dart'; import 'model/reply.dart'; import 'model/transport_interface.dart'; import 'protobuf/client.pb.dart' as pb; @@ -14,17 +15,35 @@ import 'protobuf/protobuf_codec.dart'; /// Create a WebSocket Protocol Buffers transport. @internal -Future $create$WS$PB$Transport( - String url, - SpinifyConfig config, -) async { +Future $create$WS$PB$Transport({ + /// URL for the connection + required String url, + + /// Spinify client configuration + required SpinifyConfig config, + + /// Metrics + required SpinifyMetrics$Mutable metrics, + + /// Callback for reply messages + required void Function(SpinifyReply reply) onReply, + + /// Callback for disconnect event + required void Function() onDisconnect, +}) async { // ignore: close_sinks final socket = await io.WebSocket.connect( url, headers: config.headers, protocols: {'centrifuge-protobuf'}, ); - final transport = SpinifyTransport$WS$PB$VM(socket, config); + final transport = SpinifyTransport$WS$PB$VM( + socket, + config, + metrics, + onReply, + onDisconnect, + ); // 0 CONNECTING Socket has been created. The connection is not yet open. // 1 OPEN The connection is open and ready to communicate. // 2 CLOSING The connection is in the process of closing. @@ -36,8 +55,13 @@ Future $create$WS$PB$Transport( /// Create a WebSocket Protocol Buffers transport. @internal final class SpinifyTransport$WS$PB$VM implements ISpinifyTransport { - SpinifyTransport$WS$PB$VM(this._socket, SpinifyConfig config) - : _logger = config.logger, + SpinifyTransport$WS$PB$VM( + this._socket, + SpinifyConfig config, + this._metrics, + this._onReply, + this._onDisconnect, + ) : _logger = config.logger, _encoder = switch (config.logger) { null => const ProtobufCommandEncoder(), _ => ProtobufCommandEncoder(config.logger), @@ -49,10 +73,7 @@ final class SpinifyTransport$WS$PB$VM implements ISpinifyTransport { _subscription = _socket.listen( _onData, cancelOnError: false, - onDone: () { - assert(_onDisconnect != null, 'Disconnect handler is not set'); - _onDisconnect?.call(); - }, + onDone: _onDisconnect.call, ); } @@ -62,30 +83,30 @@ final class SpinifyTransport$WS$PB$VM implements ISpinifyTransport { final SpinifyLogger? _logger; late final StreamSubscription _subscription; - void Function(SpinifyReply reply)? _onReply; + /// Metrics + final SpinifyMetrics$Mutable _metrics; - @override - // ignore: avoid_setters_without_getters - set onReply(void Function(SpinifyReply reply) handler) => _onReply = handler; + /// Callback for reply messages + final void Function(SpinifyReply reply) _onReply; - @override - // ignore: avoid_setters_without_getters - set onDisconnect(void Function() handler) => _onDisconnect = handler; - void Function()? _onDisconnect; + /// Callback for disconnect event + final void Function() _onDisconnect; void _onData(Object? bytes) { if (bytes is! List || bytes.isEmpty) { assert(false, 'Data is not byte array'); return; } - assert(_onReply != null, 'Reply handler is not set'); + _metrics + ..bytesReceived += BigInt.from(bytes.length) + ..messagesReceived += BigInt.one; final reader = pb.CodedBufferReader(bytes); while (!reader.isAtEnd()) { try { final message = pb.Reply(); reader.readMessage(message, pb.ExtensionRegistry.EMPTY); final reply = _decoder.convert(message); - _onReply?.call(reply); + _onReply.call(reply); _logger?.call( const SpinifyLogLevel.transport(), 'transport_on_reply', @@ -128,6 +149,9 @@ final class SpinifyTransport$WS$PB$VM implements ISpinifyTransport { ..writeInt32NoTag(length); //..writeRawBytes(commandData); final bytes = writer.toBuffer() + commandData; _socket.add(bytes); + _metrics + ..bytesSent += BigInt.from(bytes.length) + ..messagesSent += BigInt.one; _logger?.call( const SpinifyLogLevel.transport(), 'transport_send', diff --git a/test/unit/spinify_test.dart b/test/unit/spinify_test.dart index df2ac2e..7b9f564 100644 --- a/test/unit/spinify_test.dart +++ b/test/unit/spinify_test.dart @@ -1,4 +1,4 @@ -import 'package:spinify/spinify_developer.dart'; +import 'package:spinify/spinify.dart'; import 'package:test/test.dart'; void main() {