diff --git a/packages/stream_chat/lib/src/client/client.dart b/packages/stream_chat/lib/src/client/client.dart index 050d36f1..41fe2e7a 100644 --- a/packages/stream_chat/lib/src/client/client.dart +++ b/packages/stream_chat/lib/src/client/client.dart @@ -25,12 +25,14 @@ import 'package:stream_chat/src/core/models/member.dart'; import 'package:stream_chat/src/core/models/message.dart'; import 'package:stream_chat/src/core/models/own_user.dart'; import 'package:stream_chat/src/core/models/user.dart'; +import 'package:stream_chat/src/core/platform_detector/platform_detector.dart'; import 'package:stream_chat/src/core/util/utils.dart'; import 'package:stream_chat/src/db/chat_persistence_client.dart'; import 'package:stream_chat/src/event_type.dart'; import 'package:stream_chat/src/location.dart'; import 'package:stream_chat/src/ws/connection_status.dart'; import 'package:stream_chat/src/ws/websocket.dart'; +import 'package:stream_chat/version.dart'; /// Handler function used for logging records. Function requires a single /// [LogRecord] as the only parameter. @@ -42,6 +44,10 @@ final _levelEmojiMapper = { Level.SEVERE: '🚨', }; +final _userAgent = 'stream-chat-dart-client-' + '${CurrentPlatform.name}-' + '${PACKAGE_VERSION.split('+')[0]}'; + /// The official Dart client for Stream Chat, /// a service for building chat applications. /// This library can be used on any Dart project and on both mobile and web apps @@ -80,6 +86,7 @@ class StreamChatClient { location: location, connectTimeout: connectTimeout, receiveTimeout: receiveTimeout, + headers: {'X-Stream-Client': _userAgent}, ); _chatApi = chatApi ?? @@ -99,6 +106,7 @@ class StreamChatClient { tokenManager: _tokenManager, handler: handleEvent, logger: detachedLogger('🔌'), + queryParameters: {'X-Stream-Client': _userAgent}, ); _retryPolicy = retryPolicy ?? diff --git a/packages/stream_chat/lib/src/core/http/stream_http_client.dart b/packages/stream_chat/lib/src/core/http/stream_http_client.dart index 406138c3..c19cdb8a 100644 --- a/packages/stream_chat/lib/src/core/http/stream_http_client.dart +++ b/packages/stream_chat/lib/src/core/http/stream_http_client.dart @@ -10,9 +10,7 @@ import 'package:stream_chat/src/core/http/interceptor/connection_id_interceptor. import 'package:stream_chat/src/core/http/interceptor/logging_interceptor.dart'; import 'package:stream_chat/src/core/http/stream_chat_dio_error.dart'; import 'package:stream_chat/src/core/http/token_manager.dart'; -import 'package:stream_chat/src/core/platform_detector/platform_detector.dart'; import 'package:stream_chat/src/location.dart'; -import 'package:stream_chat/version.dart'; part 'stream_http_client_options.dart'; @@ -33,11 +31,14 @@ class StreamHttpClient { ..options.baseUrl = _options.baseUrl ..options.receiveTimeout = _options.receiveTimeout.inMilliseconds ..options.connectTimeout = _options.connectTimeout.inMilliseconds - ..options.queryParameters = {'api_key': apiKey} + ..options.queryParameters = { + 'api_key': apiKey, + ..._options.queryParameters, + } ..options.headers = { 'Content-Type': 'application/json', - 'X-Stream-Client': _options.userAgent, 'Content-Encoding': 'application/gzip', + ..._options.headers, } ..interceptors.addAll([ if (tokenManager != null) AuthInterceptor(this, tokenManager), diff --git a/packages/stream_chat/lib/src/core/http/stream_http_client_options.dart b/packages/stream_chat/lib/src/core/http/stream_http_client_options.dart index faad46a1..01cdb12e 100644 --- a/packages/stream_chat/lib/src/core/http/stream_http_client_options.dart +++ b/packages/stream_chat/lib/src/core/http/stream_http_client_options.dart @@ -10,6 +10,8 @@ class StreamHttpClientOptions { this.location, this.connectTimeout = const Duration(seconds: 6), this.receiveTimeout = const Duration(seconds: 6), + this.queryParameters = const {}, + this.headers = const {}, }) : _baseUrl = baseUrl ?? _defaultBaseURL; final String _baseUrl; @@ -32,8 +34,20 @@ class StreamHttpClientOptions { /// received timeout, default to 6s final Duration receiveTimeout; - /// Get the current user agent - String get userAgent => 'stream-chat-dart-client-' - '${CurrentPlatform.name}-' - '${PACKAGE_VERSION.split('+')[0]}'; + /// Common query parameters. + /// + /// List values use the default [ListFormat.multiCompatible]. + /// + /// The value can be overridden per parameter by adding a [MultiParam] + /// object wrapping the actual List value and the desired format. + final Map queryParameters; + + /// Http request headers. + /// The keys of initial headers will be converted to lowercase, + /// for example 'Content-Type' will be converted to 'content-type'. + /// + /// The key of Header Map is case-insensitive + /// eg: content-type and Content-Type are + /// regard as the same key. + final Map headers; } diff --git a/packages/stream_chat/lib/src/ws/websocket.dart b/packages/stream_chat/lib/src/ws/websocket.dart index f9d8a210..b26d811e 100644 --- a/packages/stream_chat/lib/src/ws/websocket.dart +++ b/packages/stream_chat/lib/src/ws/websocket.dart @@ -41,11 +41,15 @@ class WebSocket with TimerHelper { this.reconnectionMonitorInterval = 10, this.healthCheckInterval = 20, this.reconnectionMonitorTimeout = 40, + this.queryParameters = const {}, }) : _logger = logger; /// final String apiKey; + /// Additional query parameters to be added to the websocket url + final Map queryParameters; + /// WS base url final String baseUrl; @@ -156,6 +160,7 @@ class WebSocket with TimerHelper { 'api_key': apiKey, 'authorization': token.rawValue, 'stream-auth-type': token.authType.raw, + ...queryParameters, }; final scheme = baseUrl.startsWith('https') ? 'wss' : 'ws'; final host = baseUrl.replaceAll(RegExp(r'(^\w+:|^)\/\/'), ''); diff --git a/packages/stream_chat/test/src/core/http/stream_http_client_options_test.dart b/packages/stream_chat/test/src/core/http/stream_http_client_options_test.dart index a03434fa..02cfc07a 100644 --- a/packages/stream_chat/test/src/core/http/stream_http_client_options_test.dart +++ b/packages/stream_chat/test/src/core/http/stream_http_client_options_test.dart @@ -9,6 +9,8 @@ void main() { expect(options.baseUrl, 'https://chat-us-east-1.stream-io-api.com'); expect(options.connectTimeout, const Duration(seconds: 6)); expect(options.receiveTimeout, const Duration(seconds: 6)); + expect(options.queryParameters, const {}); + expect(options.headers, const {}); }); test('should override all the default set params', () { @@ -16,11 +18,15 @@ void main() { baseUrl: 'base-url', connectTimeout: Duration(seconds: 3), receiveTimeout: Duration(seconds: 3), + headers: {'test': 'test'}, + queryParameters: {'123': '123'}, ); expect(options.location, isNull); expect(options.baseUrl, 'base-url'); expect(options.connectTimeout, const Duration(seconds: 3)); expect(options.receiveTimeout, const Duration(seconds: 3)); + expect(options.headers, {'test': 'test'}); + expect(options.queryParameters, {'123': '123'}); }); group('should create baseUrl according to provided location', () { diff --git a/packages/stream_chat/test/src/core/http/stream_http_client_test.dart b/packages/stream_chat/test/src/core/http/stream_http_client_test.dart index ab4c4984..7860a0d6 100644 --- a/packages/stream_chat/test/src/core/http/stream_http_client_test.dart +++ b/packages/stream_chat/test/src/core/http/stream_http_client_test.dart @@ -96,7 +96,7 @@ void main() { await client.get('path'); } catch (_) {} - verify(() => logger.info(any())).called(16); + verify(() => logger.info(any())).called(greaterThan(0)); }); test('loggingInterceptor should log error', () async { @@ -108,7 +108,7 @@ void main() { await client.get('path'); } catch (_) {} - verify(() => logger.severe(any())).called(8); + verify(() => logger.severe(any())).called(greaterThan(0)); }); test('`.lock` should lock the dio client', () async {