Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor of ClientOptions #348

Merged
merged 8 commits into from
Mar 22, 2022
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ client libraries.
### Supported functionality

- Create a REST or Realtime instance by passing `ClientOptions`:
- `ClientOptions` can be created by passing an API token (`ClientOptions.fromKey`)
- `ClientOptions` can be created by passing an API token (`ClientOptions(key: key)`)
QuintinWillison marked this conversation as resolved.
Show resolved Hide resolved
- `defaultTokenParams`, `authCallback` and `logHandler` are not supported yet
- Get a REST channel and publish messages
- Listen for Realtime connection state changes using a stream subscription
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ Authenticating using [basic authentication/ API key](https://ably.com/documentat
```dart
// Specify your apiKey with `flutter run --dart-define=ABLY_API_KEY=replace_your_api_key`
final String ablyApiKey = const String.fromEnvironment("ABLY_API_KEY");
final clientOptions = ably.ClientOptions.fromKey(ablyApiKey);
final clientOptions = ably.ClientOptions(key: ablyApiKey);
clientOptions.logLevel = ably.LogLevel.verbose; // optional
```

Expand Down
29 changes: 17 additions & 12 deletions example/lib/ui/ably_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,26 @@ class AblyService {

AblyService({required this.apiKeyProvision}) {
realtime = ably.Realtime(
options: ably.ClientOptions.fromKey(apiKeyProvision.key)
..clientId = Constants.clientId
..logLevel = ably.LogLevel.verbose
..environment = apiKeyProvision.source == ApiKeySource.env
options: ably.ClientOptions(
key: apiKeyProvision.key,
clientId: Constants.clientId,
logLevel: ably.LogLevel.verbose,
environment: apiKeyProvision.source == ApiKeySource.env
? null
: Constants.sandboxEnvironment
..autoConnect = false,
: Constants.sandboxEnvironment,
autoConnect: false,
),
);
rest = ably.Rest(
options: ably.ClientOptions.fromKey(apiKeyProvision.key)
..clientId = Constants.clientId
..logLevel = ably.LogLevel.verbose
..environment = apiKeyProvision.source == ApiKeySource.env
? null
: Constants.sandboxEnvironment);
options: ably.ClientOptions(
key: apiKeyProvision.key,
clientId: Constants.clientId,
logLevel: ably.LogLevel.verbose,
environment: apiKeyProvision.source == ApiKeySource.env
? null
: Constants.sandboxEnvironment,
),
);
pushNotificationService = PushNotificationService(realtime, rest);
}
}
Expand Down
44 changes: 30 additions & 14 deletions lib/src/authentication/src/auth_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,6 @@ import 'package:ably_flutter/ably_flutter.dart';
///
/// https://docs.ably.com/client-lib-development-guide/features/#AO1
abstract class AuthOptions {
/// initializes an instance without any defaults
AuthOptions();

/// Convenience constructor, to create an AuthOptions based
/// on the key string obtained from the application dashboard.
/// param [key]: the full key string as obtained from the dashboard
AuthOptions.fromKey(String key) {
if (key.contains(':')) {
this.key = key;
} else {
tokenDetails = TokenDetails(key);
}
}

/// A function which is called when a new token is required.
///
/// The role of the callback is to either generate a signed [TokenRequest]
Expand Down Expand Up @@ -83,6 +69,36 @@ abstract class AuthOptions {

// TODO(tiholic) missing token attribute here
// see: https://docs.ably.com/client-lib-development-guide/features/#AO2h

/// Initializes an instance without any defaults
AuthOptions({
this.authCallback,
this.authUrl,
this.authMethod,
this.key,
this.tokenDetails,
this.authHeaders,
this.authParams,
this.queryTime,
this.useTokenAuth,
}) {
if (key != null && !key!.contains(':')) {
tokenDetails = TokenDetails(key);
key = null;
}
}

/// Convenience constructor, to create an AuthOptions based
/// on the key string obtained from the application dashboard.
/// param [key]: the full key string as obtained from the dashboard
@Deprecated("Use AuthOptions constructor with named 'key' parameter instead")
AuthOptions.fromKey(String key) {
if (key.contains(':')) {
this.key = key;
} else {
tokenDetails = TokenDetails(key);
}
}
}

/// Function-type alias implemented by a function that provides either tokens,
Expand Down
85 changes: 77 additions & 8 deletions lib/src/authentication/src/client_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,6 @@ import 'package:ably_flutter/ably_flutter.dart';
///
/// https://docs.ably.com/client-lib-development-guide/features/#TO1
class ClientOptions extends AuthOptions {
/// Set fields on [ClientOptions] to configure it.
ClientOptions();

/// initializes [ClientOptions] with a key and log level set to info
///
/// See [AuthOptions.fromKey] for more details
ClientOptions.fromKey(String key) : super.fromKey(key);

/// Optional clientId that can be used to specify the identity for this client
///
/// In most cases it is preferable to instead specific a clientId in the token
Expand Down Expand Up @@ -195,6 +187,83 @@ class ClientOptions extends AuthOptions {
/// https://docs.ably.com/client-lib-development-guide/features/#TO3l7
int channelRetryTimeout = 15000;

/// Initializes an instance with defaults
ClientOptions({
QuintinWillison marked this conversation as resolved.
Show resolved Hide resolved
AuthCallback? authCallback,
String? authUrl,
String? authMethod,
String? key,
TokenDetails? tokenDetails,
Map<String, String>? authHeaders,
Map<String, String>? authParams,
bool? queryTime,
bool? useTokenAuth,
this.clientId,
this.logHandler,
LogLevel? logLevel,
this.restHost,
this.realtimeHost,
this.port,
bool? tls,
this.tlsPort,
bool? autoConnect,
bool? useBinaryProtocol,
bool? queueMessages,
bool? echoMessages,
this.recover,
this.environment,
this.fallbackHosts,
this.fallbackHostsUseDefault,
this.defaultTokenParams,
int? disconnectedRetryTimeout,
int? suspendedRetryTimeout,
this.idempotentRestPublishing,
this.transportParams,
int? httpOpenTimeout,
int? httpRequestTimeout,
int? httpMaxRetryCount,
this.realtimeRequestTimeout,
int? fallbackRetryTimeout,
int? channelRetryTimeout,
}) : super(
authCallback: authCallback,
authUrl: authUrl,
authMethod: authMethod,
key: key,
tokenDetails: tokenDetails,
authHeaders: authHeaders,
authParams: authParams,
queryTime: queryTime,
useTokenAuth: useTokenAuth,
) {
/// These default value assignments are only required until
/// [ClientOptions.fromKey] is removed, because then defaults can be set
/// directly in the constructor invocation
this.logLevel = logLevel ?? this.logLevel;
this.tls = tls ?? this.tls;
this.autoConnect = autoConnect ?? this.autoConnect;
this.useBinaryProtocol = useBinaryProtocol ?? this.useBinaryProtocol;
this.queueMessages = queueMessages ?? this.queueMessages;
this.echoMessages = echoMessages ?? this.echoMessages;
this.disconnectedRetryTimeout =
disconnectedRetryTimeout ?? this.disconnectedRetryTimeout;
this.suspendedRetryTimeout =
suspendedRetryTimeout ?? this.suspendedRetryTimeout;
this.httpOpenTimeout = httpOpenTimeout ?? this.httpOpenTimeout;
this.httpRequestTimeout = httpRequestTimeout ?? this.httpRequestTimeout;
this.httpMaxRetryCount = httpMaxRetryCount ?? this.httpMaxRetryCount;
this.fallbackRetryTimeout =
fallbackRetryTimeout ?? this.fallbackRetryTimeout;
this.channelRetryTimeout = channelRetryTimeout ?? this.channelRetryTimeout;
}

/// initializes [ClientOptions] with a key and log level set to info
///
/// See [AuthOptions.fromKey] for more details
@Deprecated(
"Use ClientOptions constructor with named 'key' parameter instead")
ClientOptions.fromKey(String key) : super.fromKey(key);

// TODO(tiholic) unimplemented:
//
// (TO3m) logExceptionReportingUrl
Expand Down
112 changes: 56 additions & 56 deletions lib/src/platform/src/codec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -535,102 +535,102 @@ class Codec extends StandardMessageCodec {
jsonMap,
TxClientOptions.defaultTokenParams,
));
final clientOptions = ClientOptions()
final clientOptions = ClientOptions(
// AuthOptions (super class of ClientOptions)
..authUrl = _readFromJson<String>(
authUrl: _readFromJson<String>(
jsonMap,
TxClientOptions.authUrl,
)
..authMethod = _readFromJson<String>(
),
authMethod: _readFromJson<String>(
jsonMap,
TxClientOptions.authMethod,
)
..key = _readFromJson<String>(
),
key: _readFromJson<String>(
jsonMap,
TxClientOptions.key,
)
..tokenDetails =
(tokenDetails == null) ? null : _decodeTokenDetails(tokenDetails)
..authHeaders = _readFromJson<Map<String, String>>(
),
tokenDetails:
(tokenDetails == null) ? null : _decodeTokenDetails(tokenDetails),
authHeaders: _readFromJson<Map<String, String>>(
jsonMap,
TxClientOptions.authHeaders,
)
..authParams = _readFromJson<Map<String, String>>(
),
authParams: _readFromJson<Map<String, String>>(
jsonMap,
TxClientOptions.authParams,
)
..queryTime = _readFromJson<bool>(
),
queryTime: _readFromJson<bool>(
jsonMap,
TxClientOptions.queryTime,
)
..useTokenAuth = _readFromJson<bool>(
),
useTokenAuth: _readFromJson<bool>(
jsonMap,
TxClientOptions.useTokenAuth,
)
),

// ClientOptions
..clientId = _readFromJson<String>(
clientId: _readFromJson<String>(
jsonMap,
TxClientOptions.clientId,
)
..logLevel = _decodeLogLevel(jsonMap[TxClientOptions.logLevel] as String?)
),
logLevel: _decodeLogLevel(jsonMap[TxClientOptions.logLevel] as String?),
//TODO handle logHandler
..tls = jsonMap[TxClientOptions.tls] as bool
..restHost = _readFromJson<String>(
tls: jsonMap[TxClientOptions.tls] as bool,
restHost: _readFromJson<String>(
jsonMap,
TxClientOptions.restHost,
)
..realtimeHost = _readFromJson<String>(
),
realtimeHost: _readFromJson<String>(
jsonMap,
TxClientOptions.realtimeHost,
)
..port = _readFromJson<int>(
),
port: _readFromJson<int>(
jsonMap,
TxClientOptions.port,
)
..tlsPort = _readFromJson<int>(
),
tlsPort: _readFromJson<int>(
jsonMap,
TxClientOptions.tlsPort,
)
..autoConnect = jsonMap[TxClientOptions.autoConnect] as bool
..useBinaryProtocol = jsonMap[TxClientOptions.useBinaryProtocol] as bool
..queueMessages = jsonMap[TxClientOptions.queueMessages] as bool
..echoMessages = jsonMap[TxClientOptions.echoMessages] as bool
..recover = _readFromJson<String>(
),
autoConnect: jsonMap[TxClientOptions.autoConnect] as bool,
useBinaryProtocol: jsonMap[TxClientOptions.useBinaryProtocol] as bool,
queueMessages: jsonMap[TxClientOptions.queueMessages] as bool,
echoMessages: jsonMap[TxClientOptions.echoMessages] as bool,
recover: _readFromJson<String>(
jsonMap,
TxClientOptions.recover,
)
..environment = _readFromJson<String>(
),
environment: _readFromJson<String>(
jsonMap,
TxClientOptions.environment,
)
..idempotentRestPublishing = _readFromJson<bool>(
),
idempotentRestPublishing: _readFromJson<bool>(
jsonMap,
TxClientOptions.idempotentRestPublishing,
)
..realtimeRequestTimeout =
jsonMap[TxClientOptions.realtimeRequestTimeout] as int?
..httpOpenTimeout = jsonMap[TxClientOptions.httpOpenTimeout] as int
..httpRequestTimeout = jsonMap[TxClientOptions.httpRequestTimeout] as int
..httpMaxRetryCount = jsonMap[TxClientOptions.httpMaxRetryCount] as int
..fallbackHosts = _readFromJson<List<String>>(
),
realtimeRequestTimeout:
jsonMap[TxClientOptions.realtimeRequestTimeout] as int?,
httpOpenTimeout: jsonMap[TxClientOptions.httpOpenTimeout] as int,
httpRequestTimeout: jsonMap[TxClientOptions.httpRequestTimeout] as int,
httpMaxRetryCount: jsonMap[TxClientOptions.httpMaxRetryCount] as int,
fallbackHosts: _readFromJson<List<String>>(
jsonMap,
TxClientOptions.fallbackHosts,
)
..fallbackHostsUseDefault = _readFromJson<bool>(
),
fallbackHostsUseDefault: _readFromJson<bool>(
jsonMap,
TxClientOptions.fallbackHostsUseDefault,
)
..fallbackRetryTimeout =
jsonMap[TxClientOptions.fallbackRetryTimeout] as int
..defaultTokenParams =
(tokenParams == null) ? null : _decodeTokenParams(tokenParams)
..channelRetryTimeout =
jsonMap[TxClientOptions.channelRetryTimeout] as int
..transportParams = _readFromJson<Map<String, String>>(
),
fallbackRetryTimeout:
jsonMap[TxClientOptions.fallbackRetryTimeout] as int,
defaultTokenParams:
(tokenParams == null) ? null : _decodeTokenParams(tokenParams),
channelRetryTimeout: jsonMap[TxClientOptions.channelRetryTimeout] as int,
transportParams: _readFromJson<Map<String, String>>(
jsonMap,
TxClientOptions.transportParams,
);
),
);
return clientOptions;
}

Expand Down
6 changes: 3 additions & 3 deletions lib/src/platform/src/realtime/realtime.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'dart:async';
import 'dart:io' as io show Platform;
import 'dart:collection';
import 'dart:io' as io show Platform;

import 'package:ably_flutter/ably_flutter.dart';
import 'package:ably_flutter/src/platform/platform_internal.dart';
Expand All @@ -20,7 +20,7 @@ class Realtime extends PlatformObject {
ClientOptions? options,
final String? key,
}) : assert(options != null || key != null),
options = options ?? ClientOptions.fromKey(key!),
options = options ?? ClientOptions(key: key!),
super() {
_connection = Connection(this);
_channels = RealtimeChannels(this);
Expand All @@ -29,7 +29,7 @@ class Realtime extends PlatformObject {

/// Create a realtime client from an API key without configuring other parameters
factory Realtime.fromKey(String key) =>
Realtime(options: ClientOptions.fromKey(key));
Realtime(options: ClientOptions(key: key));

@override
Future<int?> createPlatformInstance() async {
Expand Down