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

App config log level and request timeout #566

Merged
merged 11 commits into from
May 17, 2022
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
* Support ObjectId type. ([#468](https://github.com/realm/realm-dart/pull/468))
* Support Uuid type. ([#470](https://github.com/realm/realm-dart/pull/470))
* Support application login. ([#469](https://github.com/realm/realm-dart/pull/469))
* Support app configuration log level and request timeout.([#566](https://github.com/realm/realm-dart/pull/566))
* Support EmailPassword register user. ([#452](https://github.com/realm/realm-dart/pull/452))
* Support EmailPassword confirm user. ([#478](https://github.com/realm/realm-dart/pull/478))
* Support EmailPassword resend user confirmation email. ([#479](https://github.com/realm/realm-dart/pull/479))
Expand All @@ -32,7 +33,6 @@
* Support user state. ([#525](https://github.com/realm/realm-dart/pull/525))
* Support getting user id and identities. ([#525](https://github.com/realm/realm-dart/pull/525))
* Support user logout. ([#525](https://github.com/realm/realm-dart/pull/525))

### Fixed
* Fixed an issue that would result in the wrong transaction being rolled back if you start a write transaction inside a write transaction. ([#442](https://github.com/realm/realm-dart/issues/442))
* Fixed boolean value persistence ([#474](https://github.com/realm/realm-dart/issues/474))
Expand Down
52 changes: 50 additions & 2 deletions lib/src/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,48 @@ enum MetadataPersistenceMode {
disabled,
}

/// Specifies the criticality level above which messages will be logged
/// by the default sync client logger.
/// {@category Application}
enum LogLevel {
/// Log everything. This will seriously harm the performance of the
/// sync client and should never be used in production scenarios.
all,

/// A version of 'debug' that allows for very high volume output.
/// This may seriously affect the performance of the sync client.
trace,

/// Reveal information that can aid debugging, no longer paying
/// attention to efficiency.
debug,

/// Same as 'Info', but prioritize completeness over minimalism.
detail,

/// Log operational sync client messages, but in a minimalistic fashion to
/// avoid general overhead from logging and to keep volume down.
info,

/// Log errors and warnings.
warn,

/// Log errors only.
error,

/// Log only fatal errors.
fatal,

/// Log nothing.
off,
}

@immutable

/// A class exposing configuration options for an [App]
/// {@category Application}
class AppConfiguration {

/// The [appId] is the unique id that identifies the Realm application.
final String appId;

Expand All @@ -59,6 +96,12 @@ class AppConfiguration {
/// The [defaultRequestTimeout] for HTTP requests. Defaults to 60 seconds.
final Duration defaultRequestTimeout;

/// The maximum duration to allow for a connection to
/// become fully established. This includes the time to resolve the
/// network address, the TCP connect operation, the SSL handshake, and
/// the WebSocket handshake. Defaults to 2 minutes.
final Duration maxConnectionTimeout;

/// The [localAppName] is the friendly name identifying the current client application.
///
/// This is typically used to differentiate between client applications that use the same
Expand All @@ -83,14 +126,17 @@ class AppConfiguration {
/// Setting this will not change the encryption key for individual Realms, which is set in the [Configuration].
final List<int>? metadataEncryptionKey;

/// The [LogLevel] for sync operations.
final LogLevel logLevel;

/// The [HttpClient] that will be used for HTTP requests during authentication.
///
/// You can use this to override the default http client handler and configure settings like proxies,
/// client certificates, and cookies. While these are not required to connect to MongoDB Realm under
/// normal circumstances, they can be useful if client devices are behind corporate firewall or use
/// a more complex networking setup.
final HttpClient httpClient;

/// Instantiates a new [AppConfiguration] with the specified appId.
AppConfiguration(
this.appId, {
Expand All @@ -101,6 +147,8 @@ class AppConfiguration {
this.localAppVersion,
this.metadataEncryptionKey,
this.metadataPersistenceMode = MetadataPersistenceMode.plaintext,
this.logLevel = LogLevel.error,
this.maxConnectionTimeout = const Duration(minutes: 2),
HttpClient? httpClient,
}) : baseUrl = baseUrl ?? Uri.parse('https://realm.mongodb.com'),
baseFilePath = baseFilePath ?? Directory(Configuration.filesPath),
Expand Down Expand Up @@ -144,7 +192,7 @@ class App {
Future<void> removeUser(User user) async {
return await realmCore.removeUser(this, user);
}

/// Switches the [currentUser] to the one specified in [user].
void switchUser(User user) {
realmCore.switchUser(this, user);
Expand Down
3 changes: 2 additions & 1 deletion lib/src/native/realm_core.dart
Original file line number Diff line number Diff line change
Expand Up @@ -836,7 +836,8 @@ class _RealmCore {

_realmLib.realm_sync_client_config_set_base_file_path(handle._pointer, configuration.baseFilePath.path.toUtf8Ptr(arena));
_realmLib.realm_sync_client_config_set_metadata_mode(handle._pointer, configuration.metadataPersistenceMode.index);

_realmLib.realm_sync_client_config_set_log_level(handle._pointer, configuration.logLevel.index);
_realmLib.realm_sync_client_config_set_connect_timeout(handle._pointer, configuration.maxConnectionTimeout.inMicroseconds);
if (configuration.metadataEncryptionKey != null && configuration.metadataPersistenceMode == MetadataPersistenceMode.encrypted) {
_realmLib.realm_sync_client_config_set_metadata_encryption_key(handle._pointer, configuration.metadataEncryptionKey!.toUint8Ptr(arena));
}
Expand Down
2 changes: 1 addition & 1 deletion lib/src/realm_class.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import 'realm_object.dart';
import 'results.dart';

// always expose with `show` to explicitly control the public API surface
export 'app.dart' show AppConfiguration, MetadataPersistenceMode, App;
export 'app.dart' show AppConfiguration, MetadataPersistenceMode, LogLevel, App;
export 'package:realm_common/realm_common.dart'
show
Ignored,
Expand Down
49 changes: 48 additions & 1 deletion test/app_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
//
////////////////////////////////////////////////////////////////////////////////

import 'dart:convert';
import 'dart:io';

import 'package:test/expect.dart';
Expand All @@ -28,12 +29,14 @@ Future<void> main([List<String>? args]) async {

await setupTests(args);

test('AppConfiguration can be created', () {
test('AppConfiguration can be initialized', () {
final a = AppConfiguration('myapp');
expect(a.appId, 'myapp');
expect(a.baseFilePath.path, Configuration.filesPath);
expect(a.baseUrl, Uri.parse('https://realm.mongodb.com'));
expect(a.defaultRequestTimeout, const Duration(minutes: 1));
expect(a.logLevel, LogLevel.error);
expect(a.metadataPersistenceMode, MetadataPersistenceMode.plaintext);

final httpClient = HttpClient(context: SecurityContext(withTrustedRoots: false));
final b = AppConfiguration(
Expand All @@ -43,14 +46,58 @@ Future<void> main([List<String>? args]) async {
defaultRequestTimeout: const Duration(seconds: 2),
localAppName: 'bar',
localAppVersion: "1.0.0",
metadataPersistenceMode: MetadataPersistenceMode.disabled,
logLevel: LogLevel.info,
maxConnectionTimeout: const Duration(minutes: 1),
httpClient: httpClient,
);
expect(b.appId, 'myapp1');
expect(b.baseFilePath.path, Directory.systemTemp.path);
expect(b.baseUrl, Uri.parse('https://not_re.al'));
expect(b.defaultRequestTimeout, const Duration(seconds: 2));
expect(b.logLevel, LogLevel.info);
desistefanova marked this conversation as resolved.
Show resolved Hide resolved
expect(b.metadataPersistenceMode, MetadataPersistenceMode.disabled);
expect(b.maxConnectionTimeout, const Duration(minutes: 1));
expect(b.httpClient, httpClient);
});

test('AppConfiguration can be created with defaults', () {
final appConfig = AppConfiguration('myapp1');
final app = App(appConfig);
expect(app.configuration.appId, 'myapp1');
expect(app.configuration.baseUrl, Uri.parse('https://realm.mongodb.com'));
expect(app.configuration.defaultRequestTimeout, const Duration(minutes: 1));
expect(app.configuration.logLevel, LogLevel.error);
expect(app.configuration.metadataPersistenceMode, MetadataPersistenceMode.plaintext);
expect(app.configuration.maxConnectionTimeout, const Duration(minutes: 2));
expect(app.configuration.httpClient, isNotNull);
});

test('AppConfiguration can be created', () {
final httpClient = HttpClient(context: SecurityContext(withTrustedRoots: false));
final appConfig = AppConfiguration(
'myapp1',
baseFilePath: Directory.systemTemp,
baseUrl: Uri.parse('https://not_re.al'),
defaultRequestTimeout: const Duration(seconds: 2),
localAppName: 'bar',
localAppVersion: "1.0.0",
metadataPersistenceMode: MetadataPersistenceMode.encrypted,
metadataEncryptionKey: base64.decode("ekey"),
logLevel: LogLevel.info,
maxConnectionTimeout: const Duration(minutes: 1),
httpClient: httpClient,
);
final app = App(appConfig);
expect(app.configuration.appId, 'myapp1');
expect(app.configuration.baseFilePath.path, Directory.systemTemp.path);
expect(app.configuration.baseUrl, Uri.parse('https://not_re.al'));
expect(app.configuration.defaultRequestTimeout, const Duration(seconds: 2));
expect(app.configuration.logLevel, LogLevel.info);
expect(app.configuration.metadataPersistenceMode, MetadataPersistenceMode.encrypted);
expect(app.configuration.maxConnectionTimeout, const Duration(minutes: 1));
expect(app.configuration.httpClient, httpClient);
});

test('App can be created', () async {
final configuration = AppConfiguration(generateRandomString(10));
Expand Down