diff --git a/lib/src/application_configuration.dart b/lib/src/application_configuration.dart new file mode 100644 index 0000000000..c7f5f325cd --- /dev/null +++ b/lib/src/application_configuration.dart @@ -0,0 +1,79 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Copyright 2022 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// +import 'dart:io'; + +import 'package:meta/meta.dart'; +import 'package:pub_semver/pub_semver.dart'; + +enum MetadataPersistenceMode { + plainText, + encrypted, +} + +@immutable +class ApplicationConfiguration { + /// The [appId] is the unique id that identifies the Realm application. + final String appId; + + /// The [baseFilePath] is the [Directory] relative to which all local data for this application will be stored. + /// This data includes metadata for users and synchronized Realms. + final Directory? baseFilePath; + + /// The [baseUrl] is the [Uri] used to reach the MongoDB Realm server. + /// [baseUrl] only needs to be set if for some reason your application isn't hosted on realm.mongodb.com. + /// This can be the case if you're testing locally or are using a pre-production environment. + final Uri? baseUrl; + + /// The [defaultRequestTimeout] for HTTP requests performed as part of authentication. + final Duration? defaultRequestTimeout; + + /// The [localAppName] is the friendly name identifying the current client application. + /// This is typically used to differentiate between client applications that use the same + /// MongoDB Realm app. These can be the same conceptual app developed for different platforms, or + /// significantly different client side applications that operate on the same data - e.g. an event managing + /// service that has different clients apps for organizers and attendees. + final String? localAppName; + + /// The [localAppVersion] + final Version? localAppVersion; + + final MetadataPersistenceMode metadataPersistenceMode; + + /// The encryption key for user metadata on this device. + /// This will not change the encryption key for individual Realms, which is set in the [Configuration] + final List? metadataEncryptionKey; + + /// 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; + + ApplicationConfiguration( + this.appId, { + this.baseUrl, + this.baseFilePath, + this.defaultRequestTimeout, + this.localAppName, + this.localAppVersion, + this.metadataPersistenceMode = MetadataPersistenceMode.plainText, + this.metadataEncryptionKey, + HttpClient? httpClient, + }) : httpClient = httpClient ?? HttpClient(); +} diff --git a/lib/src/cli/metrics/metrics_command.dart b/lib/src/cli/metrics/metrics_command.dart index 1d9af251a2..3cba46b0fa 100644 --- a/lib/src/cli/metrics/metrics_command.dart +++ b/lib/src/cli/metrics/metrics_command.dart @@ -216,7 +216,7 @@ Future getInfo(Options options) async { // Sanity check full info, if we have it if (info != null && (version == null || version == info.frameworkVersion) && flutterVersionConstraints.allows(info.frameworkVersion)) { // The returned info match both the projects constraints and the - // flutter version of the lastest flutter command run on the project + // flutter version of the latest flutter command run on the project return info; } @@ -224,6 +224,6 @@ Future getInfo(Options options) async { // secondly the min constraint of the flutter SDK used return FlutterInfo( frameworkVersion: version ?? (await safe(() => (flutterVersionConstraints as VersionRange).min!)) ?? Version.none, - dartSdkVersion: Version.parse(Platform.version.toString().takeUntil(' ')), + dartSdkVersion: Version.parse(Platform.version.takeUntil(' ')), ); } diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index 12781b4055..4e11ef0502 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -26,7 +26,9 @@ import 'dart:typed_data'; // Hide StringUtf8Pointer.toNativeUtf8 and StringUtf16Pointer since these allows silently allocating memory. Use toUtf8Ptr instead import 'package:ffi/ffi.dart' hide StringUtf8Pointer, StringUtf16Pointer; +import 'package:pub_semver/pub_semver.dart'; +import '../application_configuration.dart'; import '../collections.dart'; import '../configuration.dart'; import '../init.dart'; @@ -817,6 +819,33 @@ class _RealmCore { return RealmAppCredentialsHandle._(_realmLib.realm_app_credentials_new_email_password(emailPtr, passwordPtr.ref)); }); } + + AppConfigHandle createAppConfig(ApplicationConfiguration configuration, RealmHttpTransportHandle httpTransport) { + return using((arena) { + final c = configuration; + final app_id = c.appId.toUtf8Ptr(arena); + final handle = AppConfigHandle._(_realmLib.realm_app_config_new(app_id, httpTransport._pointer)); + if (c.baseUrl != null) { + _realmLib.realm_app_config_set_base_url(handle._pointer, c.baseUrl.toString().toUtf8Ptr(arena)); + } + if (c.defaultRequestTimeout != null) { + _realmLib.realm_app_config_set_default_request_timeout(handle._pointer, c.defaultRequestTimeout!.inMilliseconds); + } + if (c.localAppName != null) { + _realmLib.realm_app_config_set_local_app_name(handle._pointer, c.localAppName!.toUtf8Ptr(arena)); + } + if (c.localAppVersion != null) { + final versionString = c.localAppVersion.toString(); + _realmLib.realm_app_config_set_local_app_version(handle._pointer, versionString.toUtf8Ptr(arena)); + } + _realmLib.realm_app_config_set_platform(handle._pointer, Platform.operatingSystem.toUtf8Ptr(arena)); + _realmLib.realm_app_config_set_platform_version(handle._pointer, Platform.operatingSystemVersion.toUtf8Ptr(arena)); + final version = Version.parse(Platform.version); + _realmLib.realm_app_config_set_sdk_version(handle._pointer, version.toString().toUtf8Ptr(arena)); + + return handle; + }); + } } class LastError { @@ -917,6 +946,10 @@ class RealmHttpTransportHandle extends Handle { RealmHttpTransportHandle._(Pointer pointer) : super(pointer, 256); // TODO; What should hint be? } +class AppConfigHandle extends Handle { + AppConfigHandle._(Pointer pointer) : super(pointer, 256); // TODO: What should hint be? +} + extension on List { Pointer toInt8Ptr(Allocator allocator) { final nativeSize = length + 1; diff --git a/lib/src/realm_class.dart b/lib/src/realm_class.dart index 7052cb8bf2..f7a722ea77 100644 --- a/lib/src/realm_class.dart +++ b/lib/src/realm_class.dart @@ -30,6 +30,7 @@ import 'realm_object.dart'; import 'results.dart'; // always expose with `show` to explicitly control the public API surface +export "application_configuration.dart" show ApplicationConfiguration; export 'package:realm_common/realm_common.dart' show Ignored, Indexed, MapTo, PrimaryKey, RealmError, RealmModel, RealmUnsupportedSetError, RealmStateError, RealmCollectionType, RealmPropertyType; export "configuration.dart" show Configuration, RealmSchema, SchemaObject; diff --git a/src/realm_dart.cpp b/src/realm_dart.cpp index acca4ee919..2fb5bfddf5 100644 --- a/src/realm_dart.cpp +++ b/src/realm_dart.cpp @@ -71,6 +71,7 @@ void dummy(void) { realm_results_snapshot(nullptr); realm_app_credentials_new_anonymous(); realm_http_transport_new(nullptr, nullptr, nullptr); + realm_app_config_new(nullptr, nullptr); #if (ANDROID) realm_android_dummy(); #endif diff --git a/test/application_configuration_test.dart b/test/application_configuration_test.dart new file mode 100644 index 0000000000..3e5db18f80 --- /dev/null +++ b/test/application_configuration_test.dart @@ -0,0 +1,39 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Copyright 2022 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// +import 'dart:io'; + +import 'package:pub_semver/pub_semver.dart'; + +import '../lib/realm.dart'; +import 'test.dart'; + +Future main([List? args]) async { + print("Current PID $pid"); + + setupTests(args); + + test('ApplicationConfiguration can be created', () { + ApplicationConfiguration( + 'foo', + baseUrl: Uri.parse('https://not_re.al'), + defaultRequestTimeout: const Duration(seconds: 2), + localAppName: 'bar', + localAppVersion: Version(1, 0, 0), + ); + }); +}