From a54b1a8ac46506878dad83d0064cd20db5608604 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kasper=20Overg=C3=A5rd=20Nielsen?= Date: Wed, 30 Mar 2022 22:25:09 +0200 Subject: [PATCH 01/11] Use common GCHandle class for userdata on all callbacks --- ffigen/config.yaml | 2 - ffigen/realm_dart_collections.h | 1 - .../realm_flutter/ios/Classes/RealmPlugin.m | 2 - flutter/realm_flutter/ios/realm.podspec | 1 - lib/src/native/realm_bindings.dart | 167 +++++------------- lib/src/native/realm_core.dart | 96 +++++----- scripts/build-ios.sh | 1 - src/CMakeLists.txt | 2 - src/realm_dart.cpp | 68 +++++-- src/realm_dart.h | 5 + src/realm_dart_collections.cpp | 155 ---------------- src/realm_dart_collections.h | 75 -------- 12 files changed, 152 insertions(+), 423 deletions(-) delete mode 120000 ffigen/realm_dart_collections.h delete mode 100644 src/realm_dart_collections.cpp delete mode 100644 src/realm_dart_collections.h diff --git a/ffigen/config.yaml b/ffigen/config.yaml index 7462ad42e..ebdf2300d 100644 --- a/ffigen/config.yaml +++ b/ffigen/config.yaml @@ -6,13 +6,11 @@ headers: - 'realm.h' - 'realm_dart.h' - 'realm_dart_scheduler.h' - - 'realm_dart_collections.h' - 'realm_android_platform.h' include-directives: #generate only for these headers - 'realm.h' - 'realm_dart.h' - 'realm_dart_scheduler.h' - - 'realm_dart_collections.h' - 'realm_android_platform.h' compiler-opts: - '-DRLM_NO_ANON_UNIONS' diff --git a/ffigen/realm_dart_collections.h b/ffigen/realm_dart_collections.h deleted file mode 120000 index 41f84f488..000000000 --- a/ffigen/realm_dart_collections.h +++ /dev/null @@ -1 +0,0 @@ -../src/realm_dart_collections.h \ No newline at end of file diff --git a/flutter/realm_flutter/ios/Classes/RealmPlugin.m b/flutter/realm_flutter/ios/Classes/RealmPlugin.m index 3406d502c..644e7c2f7 100644 --- a/flutter/realm_flutter/ios/Classes/RealmPlugin.m +++ b/flutter/realm_flutter/ios/Classes/RealmPlugin.m @@ -27,7 +27,6 @@ #endif #import "realm_dart.h" #import "realm_dart_scheduler.h" -#import "realm_dart_collections.h" #import "platform.h" @implementation RealmPlugin + (void)registerWithRegistrar:(NSObject*)registrar { @@ -42,7 +41,6 @@ void dummy(void) { realm_dart_get_files_path(); realm_results_get_object(NULL, 0); realm_list_size(NULL, 0); - realm_dart_results_add_notification_callback(NULL, NULL, NULL, NULL); realm_results_snapshot(NULL); realm_app_credentials_new_anonymous(); } diff --git a/flutter/realm_flutter/ios/realm.podspec b/flutter/realm_flutter/ios/realm.podspec index 9e77e11e2..e30ea676a 100644 --- a/flutter/realm_flutter/ios/realm.podspec +++ b/flutter/realm_flutter/ios/realm.podspec @@ -24,7 +24,6 @@ Pod::Spec.new do |s| s.source_files = 'Classes/**/*', 'src/realm_dart.cpp' 'src/realm_dart_scheduler.cpp' - 'src/realm_dart_collections.cpp' s.public_header_files = 'Classes/**/*.h', s.vendored_frameworks = 'realm_flutter_ios.xcframework' s.dependency 'Flutter' diff --git a/lib/src/native/realm_bindings.dart b/lib/src/native/realm_bindings.dart index 95eb19a39..dd46aae0e 100644 --- a/lib/src/native/realm_bindings.dart +++ b/lib/src/native/realm_bindings.dart @@ -3,7 +3,6 @@ // Generated by `package:ffigen`. import 'dart:ffi' as ffi; -/// A Windows config for ffigen Usage: dart run ffigen --config windows.yaml class RealmLibrary { /// Holds the symbol lookup function. final ffi.Pointer Function(String symbolName) @@ -7450,6 +7449,48 @@ class RealmLibrary { late final _realm_delete_finalizable = _realm_delete_finalizablePtr .asFunction(); + ffi.Pointer gc_handle_new( + Object handle, + ) { + return _gc_handle_new( + handle, + ); + } + + late final _gc_handle_newPtr = + _lookup Function(ffi.Handle)>>( + 'gc_handle_new'); + late final _gc_handle_new = + _gc_handle_newPtr.asFunction Function(Object)>(); + + void gc_handle_delete( + ffi.Pointer handler, + ) { + return _gc_handle_delete( + handler, + ); + } + + late final _gc_handle_deletePtr = + _lookup)>>( + 'gc_handle_delete'); + late final _gc_handle_delete = + _gc_handle_deletePtr.asFunction)>(); + + Object gc_handle_deref( + ffi.Pointer handler, + ) { + return _gc_handle_deref( + handler, + ); + } + + late final _gc_handle_derefPtr = + _lookup)>>( + 'gc_handle_deref'); + late final _gc_handle_deref = + _gc_handle_derefPtr.asFunction)>(); + ffi.Pointer realm_dart_create_scheduler( int isolateId, int port, @@ -7492,123 +7533,6 @@ class RealmLibrary { _lookup>('get_thread_id'); late final _get_thread_id = _get_thread_idPtr.asFunction(); - /// Subscribe for change notifications to a realm results collection. - /// - /// @param results The realm results to subscribe to. - /// @param notification_controller A handle to a Dart NotificationController instance that will be passed to the callback. - /// @param on_change The callback to invoke, if the realm results changes. - /// @return A notification token that can be released to unsubscribe. - /// - /// This is a dart specific wrapper for realm_results_add_notification_callback. - ffi.Pointer - realm_dart_results_add_notification_callback( - ffi.Pointer results, - Object notification_controller, - realm_dart_on_collection_change_func_t callback, - ffi.Pointer scheduler, - ) { - return _realm_dart_results_add_notification_callback( - results, - notification_controller, - callback, - scheduler, - ); - } - - late final _realm_dart_results_add_notification_callbackPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Handle, - realm_dart_on_collection_change_func_t, - ffi.Pointer)>>( - 'realm_dart_results_add_notification_callback'); - late final _realm_dart_results_add_notification_callback = - _realm_dart_results_add_notification_callbackPtr.asFunction< - ffi.Pointer Function( - ffi.Pointer, - Object, - realm_dart_on_collection_change_func_t, - ffi.Pointer)>(); - - /// Subscribe for change notifications to a realm list collection. - /// - /// @param list The realm list to subscribe to. - /// @param notification_controller A handle to a Dart NotificationController instance that will be passed to the callback. - /// @param on_change The callback to invoke, if the realm list changes. - /// @return A notification token that can be released to unsubscribe. - /// - /// This is a dart specific wrapper for realm_list_add_notification_callback. - ffi.Pointer - realm_dart_list_add_notification_callback( - ffi.Pointer list, - Object notification_controller, - realm_dart_on_collection_change_func_t on_change, - ffi.Pointer scheduler, - ) { - return _realm_dart_list_add_notification_callback( - list, - notification_controller, - on_change, - scheduler, - ); - } - - late final _realm_dart_list_add_notification_callbackPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Handle, - realm_dart_on_collection_change_func_t, - ffi.Pointer)>>( - 'realm_dart_list_add_notification_callback'); - late final _realm_dart_list_add_notification_callback = - _realm_dart_list_add_notification_callbackPtr.asFunction< - ffi.Pointer Function( - ffi.Pointer, - Object, - realm_dart_on_collection_change_func_t, - ffi.Pointer)>(); - - /// Subscribe for change notifications to a realm object. - /// - /// @param realm_object The realm object to subscribe to. - /// @param notification_controller A handle to a Dart NotificationController instance that will be passed to the callback. - /// @param on_change The callback to invoke, if the realm list changes. - /// @return A notification token that can be released to unsubscribe. - /// - /// This is a dart specific wrapper for realm_object_add_notification_callback. - ffi.Pointer - realm_dart_object_add_notification_callback( - ffi.Pointer list, - Object notification_controller, - realm_dart_on_object_change_func_t on_change, - ffi.Pointer scheduler, - ) { - return _realm_dart_object_add_notification_callback( - list, - notification_controller, - on_change, - scheduler, - ); - } - - late final _realm_dart_object_add_notification_callbackPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Handle, - realm_dart_on_object_change_func_t, - ffi.Pointer)>>( - 'realm_dart_object_add_notification_callback'); - late final _realm_dart_object_add_notification_callback = - _realm_dart_object_add_notification_callbackPtr.asFunction< - ffi.Pointer Function( - ffi.Pointer, - Object, - realm_dart_on_object_change_func_t, - ffi.Pointer)>(); - ffi.Pointer realm_dart_get_files_path() { return _realm_dart_get_files_path(); } @@ -8553,10 +8477,3 @@ class _Dart_FinalizableHandle extends ffi.Opaque {} /// A port is used to send or receive inter-isolate messages typedef Dart_Port = ffi.Int64; -typedef realm_dart_on_collection_change_func_t = ffi.Pointer< - ffi.NativeFunction< - ffi.Void Function( - ffi.Handle, ffi.Pointer)>>; -typedef realm_dart_on_object_change_func_t = ffi.Pointer< - ffi.NativeFunction< - ffi.Void Function(ffi.Handle, ffi.Pointer)>>; diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index d22fb138b..a6bfd195a 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -554,67 +554,79 @@ class _RealmCore { return _realmLib.realm_list_is_valid(list.handle._pointer); } - static void collection_change_callback(Object object, Pointer data) { - assert(object is NotificationsController, "Notification controller expected"); - - final controller = object as NotificationsController; - - if (data == nullptr) { - //realm_collection_changes data clone is done in native code before this callback is invoked. nullptr data means cloning failed. - controller.onError(RealmError("Invalid notifications data received")); - return; - } + static void collection_change_callback(Pointer userdata, Pointer data) { + final controller = _realmLib.gc_handle_deref(userdata); + if (controller is NotificationsController) { + if (data == nullptr) { + controller.onError(RealmError("Invalid notifications data received")); + return; + } - try { - final changesHandle = RealmCollectionChangesHandle._(data); - controller.onChanges(changesHandle); - } catch (e) { - controller.onError(RealmError("Error handling collection change notifications. Error: $e")); + try { + final changesHandle = RealmCollectionChangesHandle._(_realmLib.realm_clone(data.cast()).cast()); + controller.onChanges(changesHandle); + } catch (e) { + controller.onError(RealmError("Error handling collection change notifications. Error: $e")); + } } } - static void object_change_callback(Object object, Pointer data) { - assert(object is NotificationsController, "Notification controller expected"); - - final controller = object as NotificationsController; - - if (data == nullptr) { - //realm_collection_changes data clone is done in native code before this callback is invoked. nullptr data means cloning failed. - controller.onError(RealmError("Invalid notifications data received")); - return; - } + static void object_change_callback(Pointer userdata, Pointer data) { + final controller = _realmLib.gc_handle_deref(userdata); + if (controller is NotificationsController) { + if (data == nullptr) { + //realm_collection_changes data clone is done in native code before this callback is invoked. nullptr data means cloning failed. + controller.onError(RealmError("Invalid notifications data received")); + return; + } - try { - final changesHandle = RealmObjectChangesHandle._(data); - controller.onChanges(changesHandle); - } catch (e) { - controller.onError(RealmError("Error handling collection change notifications. Error: $e")); + try { + final changesHandle = RealmObjectChangesHandle._(_realmLib.realm_clone(data.cast()).cast()); + controller.onChanges(changesHandle); + } catch (e) { + controller.onError(RealmError("Error handling collection change notifications. Error: $e")); + } } } RealmNotificationTokenHandle subscribeResultsNotifications(RealmResultsHandle handle, NotificationsController controller, SchedulerHandle schedulerHandle) { - final onChangeCallback = Pointer.fromFunction)>(collection_change_callback); - - final pointer = _realmLib.invokeGetPointer( - () => _realmLib.realm_dart_results_add_notification_callback(handle._pointer, controller, onChangeCallback, schedulerHandle._pointer)); + final pointer = _realmLib.invokeGetPointer(() => _realmLib.realm_results_add_notification_callback( + handle._pointer, + _realmLib.gc_handle_new(controller), + nullptr, + nullptr, + Pointer.fromFunction(collection_change_callback), + nullptr, + schedulerHandle._pointer, + )); return RealmNotificationTokenHandle._(pointer); } RealmNotificationTokenHandle subscribeListNotifications(RealmListHandle handle, NotificationsController controller, SchedulerHandle schedulerHandle) { - final onChangeCallback = Pointer.fromFunction)>(collection_change_callback); - - final pointer = _realmLib - .invokeGetPointer(() => _realmLib.realm_dart_list_add_notification_callback(handle._pointer, controller, onChangeCallback, schedulerHandle._pointer)); + final pointer = _realmLib.invokeGetPointer(() => _realmLib.realm_list_add_notification_callback( + handle._pointer, + _realmLib.gc_handle_new(controller), + nullptr, + nullptr, + Pointer.fromFunction(collection_change_callback), + nullptr, + schedulerHandle._pointer, + )); return RealmNotificationTokenHandle._(pointer); } RealmNotificationTokenHandle subscribeObjectNotifications(RealmObjectHandle handle, NotificationsController controller, SchedulerHandle schedulerHandle) { - final onChangeCallback = Pointer.fromFunction)>(object_change_callback); - - final pointer = _realmLib - .invokeGetPointer(() => _realmLib.realm_dart_object_add_notification_callback(handle._pointer, controller, onChangeCallback, schedulerHandle._pointer)); + final pointer = _realmLib.invokeGetPointer(() => _realmLib.realm_object_add_notification_callback( + handle._pointer, + _realmLib.gc_handle_new(controller), + nullptr, + nullptr, + Pointer.fromFunction(object_change_callback), + nullptr, + schedulerHandle._pointer, + )); return RealmNotificationTokenHandle._(pointer); } diff --git a/scripts/build-ios.sh b/scripts/build-ios.sh index ffdd6f7b4..8488704fc 100755 --- a/scripts/build-ios.sh +++ b/scripts/build-ios.sh @@ -113,7 +113,6 @@ mkdir -p _include/realm_dart_ios cp "$PROJECT_ROOT"/src/realm-core/src/realm.h _include/realm_dart_ios/ cp "$PROJECT_ROOT"/src/realm_dart.h _include/realm_dart_ios/ cp "$PROJECT_ROOT"/src/realm_dart_scheduler.h _include/realm_dart_ios/ -cp "$PROJECT_ROOT"/src/realm_dart_collections.h _include/realm_dart_ios/ cp -r "$PROJECT_ROOT"/src/dart-include _include/realm_dart_ios/ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8e012939a..6c2c337a9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,14 +1,12 @@ set(SOURCES realm_dart.cpp realm_dart_scheduler.cpp - realm_dart_collections.cpp dart-include/dart_api_dl.c ) set(HEADERS realm_dart.h realm_dart_scheduler.h - realm_dart_collections.h realm-core/src/realm.h ) diff --git a/src/realm_dart.cpp b/src/realm_dart.cpp index 6452ddec9..967a84b71 100644 --- a/src/realm_dart.cpp +++ b/src/realm_dart.cpp @@ -33,25 +33,25 @@ BOOL APIENTRY DllMain(HMODULE module, DWORD reason, LPVOID reserved) { - return true; + return true; } #endif // defined(_WIN32) RLM_API void realm_initializeDartApiDL(void* data) { - Dart_InitializeApiDL(data); + Dart_InitializeApiDL(data); } void handle_finalizer(void* isolate_callback_data, void* realmPtr) { - realm_release(realmPtr); + realm_release(realmPtr); } RLM_API Dart_FinalizableHandle realm_attach_finalizer(Dart_Handle handle, void* realmPtr, int size) { - return Dart_NewFinalizableHandle_DL(handle, realmPtr, size, handle_finalizer); + return Dart_NewFinalizableHandle_DL(handle, realmPtr, size, handle_finalizer); } RLM_API void realm_delete_finalizable(Dart_FinalizableHandle finalizable_handle, Dart_Handle handle) { - Dart_DeleteFinalizableHandle_DL(finalizable_handle, handle); + Dart_DeleteFinalizableHandle_DL(finalizable_handle, handle); } #if (ANDROID) @@ -60,17 +60,51 @@ void realm_android_dummy(); // Force the linker to link all exports from realm-core C API void dummy(void) { - realm_scheduler_make_default(); - realm_config_new(); - realm_schema_new(nullptr, 0, nullptr); - realm_get_library_version(); - realm_object_create(nullptr, 0); - realm_results_get_object(nullptr, 0); - realm_list_size(nullptr, 0); - realm_results_add_notification_callback(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); - realm_results_snapshot(nullptr); - realm_app_credentials_new_anonymous(); + realm_scheduler_make_default(); + realm_config_new(); + realm_schema_new(nullptr, 0, nullptr); + realm_get_library_version(); + realm_object_create(nullptr, 0); + realm_results_get_object(nullptr, 0); + realm_list_size(nullptr, 0); + realm_results_add_notification_callback(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); + realm_results_snapshot(nullptr); + realm_app_credentials_new_anonymous(); + gc_handle_new(nullptr); + gc_handle_deref(nullptr); #if (ANDROID) - realm_android_dummy(); + realm_android_dummy(); #endif -} \ No newline at end of file +} + +class GCHandle { +public: + GCHandle(Dart_Handle handle) : m_weakHandle(Dart_NewWeakPersistentHandle_DL(handle, this, 1, finalize_handle)) {} + + Dart_Handle value() { + return Dart_HandleFromWeakPersistent_DL(m_weakHandle); + } + +private: + // destructor is private, only called by finalize_handle, when corresponding dart object is GCed + ~GCHandle() { + if (m_weakHandle) { + Dart_DeleteWeakPersistentHandle_DL(m_weakHandle); + m_weakHandle = nullptr; + } + } + + static void finalize_handle(void* isolate_callback_data, void* peer) { + delete reinterpret_cast(peer); + } // no-op + + Dart_WeakPersistentHandle m_weakHandle; +}; + +RLM_API void* gc_handle_new(Dart_Handle handle) { + return new GCHandle(handle); +} + +RLM_API Dart_Handle gc_handle_deref(void* handle) { + return reinterpret_cast(handle)->value(); +} diff --git a/src/realm_dart.h b/src/realm_dart.h index fcf8fdc1b..c23cf307f 100644 --- a/src/realm_dart.h +++ b/src/realm_dart.h @@ -28,4 +28,9 @@ RLM_API Dart_FinalizableHandle realm_attach_finalizer(Dart_Handle handle, void* RLM_API void realm_delete_finalizable(Dart_FinalizableHandle finalizable_handle, Dart_Handle handle); +// GC Handle stuff +RLM_API void* gc_handle_new(Dart_Handle handle); +RLM_API void gc_handle_delete(void* handler); +RLM_API Dart_Handle gc_handle_deref(void* handler); + #endif // REALM_DART_H \ No newline at end of file diff --git a/src/realm_dart_collections.cpp b/src/realm_dart_collections.cpp deleted file mode 100644 index 79d4c74f6..000000000 --- a/src/realm_dart_collections.cpp +++ /dev/null @@ -1,155 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Copyright 2021 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. -// -//////////////////////////////////////////////////////////////////////////////// - -#include "realm.h" -#include "dart_api_dl.h" -#include "realm_dart_collections.h" -#include - -struct HandleScope { - HandleScope() { - Dart_EnterScope_DL(); - } - - ~HandleScope() { - Dart_ExitScope_DL(); - } -}; - -template -class CallbackData { - //This is no op and does not need to call delete_handle since ~CallbackData is always called by the RealmNotificationTokenHandle finalizer - static void finalize_handle(void* isolate_callback_data, void* peer) {} - - void delete_handle() { - if (m_handle) { - //TODO: uncomment when the HACK is removed. - //Dart_DeleteWeakPersistentHandle_DL(m_handle); - m_handle = nullptr; - } - } - -public: - CallbackData(Dart_Handle handle, Func callback) - : m_handle(Dart_NewFinalizableHandle_DL(handle, nullptr, 1, finalize_handle)), m_callback(callback) - {} - - ~CallbackData() { - delete_handle(); - } - - void callback(const Type* changes) { - if (m_handle) { - HandleScope scope; - //TODO: HACK. We can not release Dart persitent handles in delete_handle on Isolate teardown since the IsolateGroup is destroyed before it. - //This works since Dart_WeakPersistentHandle is equivalent to Dart_FinalizableHandle. They both are FinalizablePersistentHandle internally. - Dart_WeakPersistentHandle weakHnd = reinterpret_cast(m_handle); - auto handle = Dart_HandleFromWeakPersistent_DL(weakHnd); - - //clone changes object since the Dart callback is async and changes object is valid for the duration of this method only - //clone failures are handled in the Dart callback - const Type* cloned = static_cast(realm_clone(changes)); - m_callback(handle, cloned); - } - } - -private: - //TODO: We use FinalizableHandle since it is auto-deleting. Switch to Dart_WeakPersistentHandle when the HACK is removed - Dart_FinalizableHandle m_handle; - Func m_callback; -}; - -typedef CallbackData CollectionCallbackData; -typedef CallbackData ObjectCallbackData; - -void on_collection_change_callback(void* userdata, const realm_collection_changes_t* changes) { - auto& callbackData = *reinterpret_cast(userdata); - callbackData.callback(changes); -} - -template -void on_collection_change_callback_type(void* userdata, const realm_collection_changes_t* changes) { - auto& callbackData = *reinterpret_cast(userdata); - callbackData.callback(changes); -} - -void free_collection_callback_data(void* userdata) { - auto callback = reinterpret_cast(userdata); - delete callback; -} - -void on_object_change_callback(void* userdata, const realm_object_changes* changes) { - auto& callbackData = *reinterpret_cast(userdata); - callbackData.callback(changes); -} - -void free_object_callback_data(void* userdata) { - auto callback = reinterpret_cast(userdata); - delete callback; -} - -RLM_API realm_notification_token_t* realm_dart_results_add_notification_callback( - realm_results_t* results, - Dart_Handle notification_controller, - realm_dart_on_collection_change_func_t callback, - realm_scheduler_t* scheduler) -{ - auto callback_data = new CollectionCallbackData(notification_controller, callback); - - return realm_results_add_notification_callback(results, - callback_data, - free_collection_callback_data, - nullptr, - on_collection_change_callback, - nullptr, - scheduler); -} - -RLM_API realm_notification_token_t* realm_dart_list_add_notification_callback( - realm_list_t* list, - Dart_Handle notification_controller, - realm_dart_on_collection_change_func_t callback, - realm_scheduler_t* scheduler) -{ - auto callback_data = new CollectionCallbackData(notification_controller, callback); - - return realm_list_add_notification_callback(list, - callback_data, - free_collection_callback_data, - nullptr, - on_collection_change_callback, - nullptr, - scheduler); -} - -RLM_API realm_notification_token_t* realm_dart_object_add_notification_callback( - realm_object_t* realm_object, - Dart_Handle notification_controller, - realm_dart_on_object_change_func_t callback, - realm_scheduler_t* scheduler) -{ - auto callback_data = new ObjectCallbackData(notification_controller, callback); - - return realm_object_add_notification_callback(realm_object, - callback_data, - free_object_callback_data, - nullptr, - on_object_change_callback, - nullptr, - scheduler); -} diff --git a/src/realm_dart_collections.h b/src/realm_dart_collections.h deleted file mode 100644 index ee51c6078..000000000 --- a/src/realm_dart_collections.h +++ /dev/null @@ -1,75 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Copyright 2021 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. -// -//////////////////////////////////////////////////////////////////////////////// - -#ifndef REALM_DART_COLLECTIONS_H -#define REALM_DART_COLLECTIONS_H - -#include "realm.h" -#include "dart_api_dl.h" - -typedef void (*realm_dart_on_collection_change_func_t)(Dart_Handle notification_controller, const realm_collection_changes_t*); -typedef void (*realm_dart_on_object_change_func_t)(Dart_Handle notification_controller, const realm_object_changes_t*); - -/** - * Subscribe for change notifications to a realm results collection. - * - * @param results The realm results to subscribe to. - * @param notification_controller A handle to a Dart NotificationController instance that will be passed to the callback. - * @param on_change The callback to invoke, if the realm results changes. - * @return A notification token that can be released to unsubscribe. - * - * This is a dart specific wrapper for realm_results_add_notification_callback. - */ -RLM_API realm_notification_token_t* -realm_dart_results_add_notification_callback(realm_results_t* results, - Dart_Handle notification_controller, - realm_dart_on_collection_change_func_t callback, - realm_scheduler_t* scheduler); - -/** -* Subscribe for change notifications to a realm list collection. -* -* @param list The realm list to subscribe to. -* @param notification_controller A handle to a Dart NotificationController instance that will be passed to the callback. -* @param on_change The callback to invoke, if the realm list changes. -* @return A notification token that can be released to unsubscribe. -* -* This is a dart specific wrapper for realm_list_add_notification_callback. -*/ -RLM_API realm_notification_token_t* -realm_dart_list_add_notification_callback(realm_list_t* list, - Dart_Handle notification_controller, - realm_dart_on_collection_change_func_t on_change, - realm_scheduler_t* scheduler); - -/** -* Subscribe for change notifications to a realm object. -* -* @param realm_object The realm object to subscribe to. -* @param notification_controller A handle to a Dart NotificationController instance that will be passed to the callback. -* @param on_change The callback to invoke, if the realm list changes. -* @return A notification token that can be released to unsubscribe. -* -* This is a dart specific wrapper for realm_object_add_notification_callback. -*/ -RLM_API realm_notification_token_t* -realm_dart_object_add_notification_callback(realm_object_t* list, - Dart_Handle notification_controller, - realm_dart_on_object_change_func_t on_change, - realm_scheduler_t* scheduler); -#endif // REALM_DART_COLLECTIONS_H \ No newline at end of file From 6ae70e2526d1697e1241be6ae6e8b6203483a278 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kasper=20Overg=C3=A5rd=20Nielsen?= Date: Thu, 7 Apr 2022 13:24:05 +0200 Subject: [PATCH 02/11] Support hard gc handles as well, as suggested by Yavor. Ie. handles that will keep the corresponding dart object alive. --- lib/src/native/realm_bindings.dart | 46 +++++++++++++++++++----------- lib/src/native/realm_core.dart | 7 ++--- src/realm_dart.cpp | 28 ++++++++++++++---- src/realm_dart.h | 7 +++-- 4 files changed, 60 insertions(+), 28 deletions(-) diff --git a/lib/src/native/realm_bindings.dart b/lib/src/native/realm_bindings.dart index dd46aae0e..a4b5437f8 100644 --- a/lib/src/native/realm_bindings.dart +++ b/lib/src/native/realm_bindings.dart @@ -7449,39 +7449,53 @@ class RealmLibrary { late final _realm_delete_finalizable = _realm_delete_finalizablePtr .asFunction(); - ffi.Pointer gc_handle_new( + ffi.Pointer gc_handle_weak_new( Object handle, ) { - return _gc_handle_new( + return _gc_handle_weak_new( handle, ); } - late final _gc_handle_newPtr = + late final _gc_handle_weak_newPtr = _lookup Function(ffi.Handle)>>( - 'gc_handle_new'); - late final _gc_handle_new = - _gc_handle_newPtr.asFunction Function(Object)>(); + 'gc_handle_weak_new'); + late final _gc_handle_weak_new = _gc_handle_weak_newPtr + .asFunction Function(Object)>(); - void gc_handle_delete( - ffi.Pointer handler, + ffi.Pointer gc_handle_hard_new( + Object handle, + ) { + return _gc_handle_hard_new( + handle, + ); + } + + late final _gc_handle_hard_newPtr = + _lookup Function(ffi.Handle)>>( + 'gc_handle_hard_new'); + late final _gc_handle_hard_new = _gc_handle_hard_newPtr + .asFunction Function(Object)>(); + + void gc_handle_soften( + ffi.Pointer handle, ) { - return _gc_handle_delete( - handler, + return _gc_handle_soften( + handle, ); } - late final _gc_handle_deletePtr = + late final _gc_handle_softenPtr = _lookup)>>( - 'gc_handle_delete'); - late final _gc_handle_delete = - _gc_handle_deletePtr.asFunction)>(); + 'gc_handle_soften'); + late final _gc_handle_soften = + _gc_handle_softenPtr.asFunction)>(); Object gc_handle_deref( - ffi.Pointer handler, + ffi.Pointer handle, ) { return _gc_handle_deref( - handler, + handle, ); } diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index a6bfd195a..c31e9c5fe 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -20,7 +20,6 @@ import 'dart:convert'; import 'dart:ffi'; -import 'dart:ffi' as ffi show Handle; import 'dart:typed_data'; // Hide StringUtf8Pointer.toNativeUtf8 and StringUtf16Pointer since these allows silently allocating memory. Use toUtf8Ptr instead @@ -592,7 +591,7 @@ class _RealmCore { RealmNotificationTokenHandle subscribeResultsNotifications(RealmResultsHandle handle, NotificationsController controller, SchedulerHandle schedulerHandle) { final pointer = _realmLib.invokeGetPointer(() => _realmLib.realm_results_add_notification_callback( handle._pointer, - _realmLib.gc_handle_new(controller), + _realmLib.gc_handle_weak_new(controller), nullptr, nullptr, Pointer.fromFunction(collection_change_callback), @@ -606,7 +605,7 @@ class _RealmCore { RealmNotificationTokenHandle subscribeListNotifications(RealmListHandle handle, NotificationsController controller, SchedulerHandle schedulerHandle) { final pointer = _realmLib.invokeGetPointer(() => _realmLib.realm_list_add_notification_callback( handle._pointer, - _realmLib.gc_handle_new(controller), + _realmLib.gc_handle_weak_new(controller), nullptr, nullptr, Pointer.fromFunction(collection_change_callback), @@ -620,7 +619,7 @@ class _RealmCore { RealmNotificationTokenHandle subscribeObjectNotifications(RealmObjectHandle handle, NotificationsController controller, SchedulerHandle schedulerHandle) { final pointer = _realmLib.invokeGetPointer(() => _realmLib.realm_object_add_notification_callback( handle._pointer, - _realmLib.gc_handle_new(controller), + _realmLib.gc_handle_weak_new(controller), nullptr, nullptr, Pointer.fromFunction(object_change_callback), diff --git a/src/realm_dart.cpp b/src/realm_dart.cpp index 967a84b71..d67327a7e 100644 --- a/src/realm_dart.cpp +++ b/src/realm_dart.cpp @@ -70,8 +70,6 @@ void dummy(void) { realm_results_add_notification_callback(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); realm_results_snapshot(nullptr); realm_app_credentials_new_anonymous(); - gc_handle_new(nullptr); - gc_handle_deref(nullptr); #if (ANDROID) realm_android_dummy(); #endif @@ -79,12 +77,23 @@ void dummy(void) { class GCHandle { public: - GCHandle(Dart_Handle handle) : m_weakHandle(Dart_NewWeakPersistentHandle_DL(handle, this, 1, finalize_handle)) {} + GCHandle(Dart_Handle handle, bool hard) : m_weakHandle(Dart_NewWeakPersistentHandle_DL(handle, this, 1, finalize_handle)) { + if (hard) { + m_hardHandle = Dart_NewPersistentHandle_DL(handle); + } + } Dart_Handle value() { return Dart_HandleFromWeakPersistent_DL(m_weakHandle); } + void soften() { + if (m_hardHandle) { + Dart_DeletePersistentHandle_DL(m_hardHandle); + m_hardHandle = nullptr; + } + } + private: // destructor is private, only called by finalize_handle, when corresponding dart object is GCed ~GCHandle() { @@ -99,10 +108,19 @@ class GCHandle { } // no-op Dart_WeakPersistentHandle m_weakHandle; + Dart_PersistentHandle m_hardHandle; // used to pin dart object from native side, if needed }; -RLM_API void* gc_handle_new(Dart_Handle handle) { - return new GCHandle(handle); +RLM_API void* gc_handle_weak_new(Dart_Handle handle) { + return new GCHandle(handle, false); +} + +RLM_API void* gc_handle_hard_new(Dart_Handle handle) { + return new GCHandle(handle, true); +} + +RLM_API void gc_handle_soften(void* handle) { + return reinterpret_cast(handle)->soften(); } RLM_API Dart_Handle gc_handle_deref(void* handle) { diff --git a/src/realm_dart.h b/src/realm_dart.h index c23cf307f..aabcbadfb 100644 --- a/src/realm_dart.h +++ b/src/realm_dart.h @@ -29,8 +29,9 @@ RLM_API Dart_FinalizableHandle realm_attach_finalizer(Dart_Handle handle, void* RLM_API void realm_delete_finalizable(Dart_FinalizableHandle finalizable_handle, Dart_Handle handle); // GC Handle stuff -RLM_API void* gc_handle_new(Dart_Handle handle); -RLM_API void gc_handle_delete(void* handler); -RLM_API Dart_Handle gc_handle_deref(void* handler); +RLM_API void* gc_handle_weak_new(Dart_Handle handle); +RLM_API void* gc_handle_hard_new(Dart_Handle handle); +RLM_API void gc_handle_soften(void* handle); +RLM_API Dart_Handle gc_handle_deref(void* handle); #endif // REALM_DART_H \ No newline at end of file From c81e0c0fda7ad063026a9c4947d4c2b6e73248ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kasper=20Overg=C3=A5rd=20Nielsen?= Date: Fri, 1 Apr 2022 13:01:34 +0200 Subject: [PATCH 03/11] Backport to dart 2.16 where we have to work around a finalization issue on isolate shutdown: https://github.com/dart-lang/sdk/issues/48321 --- src/realm_dart.cpp | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/realm_dart.cpp b/src/realm_dart.cpp index d67327a7e..da465da22 100644 --- a/src/realm_dart.cpp +++ b/src/realm_dart.cpp @@ -77,14 +77,26 @@ void dummy(void) { class GCHandle { public: - GCHandle(Dart_Handle handle, bool hard) : m_weakHandle(Dart_NewWeakPersistentHandle_DL(handle, this, 1, finalize_handle)) { + // TODO: HACK. Should be able to use the weak handle to get the handle. + // When hack removed, replace with: + // GCHandle(Dart_Handle handle) : m_weakHandle(Dart_NewWeakPersistentHandle_DL(handle, this, 1, finalize_handle)) {} + GCHandle(Dart_Handle handle, bool hard) : m_weakHandle(Dart_NewFinalizableHandle_DL(handle, this, 1, finalize_handle)) { if (hard) { m_hardHandle = Dart_NewPersistentHandle_DL(handle); } } Dart_Handle value() { - return Dart_HandleFromWeakPersistent_DL(m_weakHandle); + // TODO: HACK. We can not release Dart weak persistent handles in on Isolate teardown until + // https://github.com/dart-lang/sdk/issues/48321 is fixed and released, since the IsolateGroup + // is destroyed before it happens. + // + // This works since Dart_WeakPersistentHandle is equivalent to Dart_FinalizableHandle. + // They both are FinalizablePersistentHandle internally. + Dart_WeakPersistentHandle weakHnd = reinterpret_cast(m_weakHandle); + return Dart_HandleFromWeakPersistent_DL(weakHnd); + // When hack removed, replace with: + // return Dart_HandleFromFinalizable_DL(m_weakHandle); } void soften() { @@ -96,18 +108,21 @@ class GCHandle { private: // destructor is private, only called by finalize_handle, when corresponding dart object is GCed + /* TODO: HACK. Uncomment when hack removed ~GCHandle() { if (m_weakHandle) { Dart_DeleteWeakPersistentHandle_DL(m_weakHandle); m_weakHandle = nullptr; } } + */ static void finalize_handle(void* isolate_callback_data, void* peer) { delete reinterpret_cast(peer); - } // no-op + } - Dart_WeakPersistentHandle m_weakHandle; + // TODO: HACK. Should be Dart_WeakPersistentHandle when hack removed + Dart_FinalizableHandle m_weakHandle; Dart_PersistentHandle m_hardHandle; // used to pin dart object from native side, if needed }; From 6899dc845a2d1c494d7c53181ed83918680dde9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kasper=20Overg=C3=A5rd=20Nielsen?= Date: Fri, 18 Mar 2022 10:43:57 +0100 Subject: [PATCH 04/11] Add support for http_transport --- lib/src/native/realm_core.dart | 183 +++++++++++++++++++++++++++++++-- src/realm_dart.cpp | 1 + 2 files changed, 178 insertions(+), 6 deletions(-) diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index c31e9c5fe..12781b405 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -20,6 +20,8 @@ import 'dart:convert'; import 'dart:ffi'; +import 'dart:ffi' as ffi show Handle; +import 'dart:io'; import 'dart:typed_data'; // Hide StringUtf8Pointer.toNativeUtf8 and StringUtf16Pointer since these allows silently allocating memory. Use toUtf8Ptr instead @@ -38,6 +40,48 @@ late RealmLibrary _realmLib; final _RealmCore realmCore = _RealmCore(); +// TODO: Once enhanced-enums land in 2.17, replace with: +/* +enum _CustomErrorCode { + noError(0), + httpClientDisposed(997), + unknownHttp(998), + unknown(999), + timeout(1000); + + final int code; + const _CustomErrorCode(this.code); +} +*/ + +enum _CustomErrorCode { + noError, + httpClientDisposed, + unknownHttp, + unknown, + timeout, +} + +extension on _CustomErrorCode { + int get code { + switch (this) { + case _CustomErrorCode.noError: return 0; + case _CustomErrorCode.httpClientDisposed: return 997; + case _CustomErrorCode.unknownHttp: return 998; + case _CustomErrorCode.unknown: return 999; + case _CustomErrorCode.timeout: return 1000; + } + } +} + +enum _HttpMethod { + get, + post, + patch, + put, + delete, +} + class _RealmCore { // From realm.h. Currently not exported from the shared library static const int RLM_INVALID_CLASS_KEY = 0x7FFFFFFF; @@ -180,6 +224,123 @@ class _RealmCore { }); } + RealmHttpTransportHandle createHttpTransport(HttpClient httpClient) { + return RealmHttpTransportHandle._(_realmLib.realm_http_transport_new( + Pointer.fromFunction(request_callback), + _realmLib.gc_handle_weak_new(httpClient), + nullptr, + )); + } + + static void request_callback(Pointer userData, realm_http_request request, Pointer request_context) { + // + // The request struct only survives until end-of-call, even though + // we explicitly call realm_http_transport_complete_request to + // mark request as completed later. + // + // Therefor we need to copy everything out of request before returning. + // We cannot clone request on the native side with realm_clone, + // since realm_http_request does not inherit from WrapC. + // + final client = _realmLib.gc_handle_deref(userData) as HttpClient; + client.connectionTimeout = Duration(milliseconds: request.timeout_ms); + + final url = Uri.parse(request.url.cast().toDartString()); + + final method = _HttpMethod.values[request.method]; + + final body = request.body.cast().toDartString(); + + final headers = {}; + for (int i = 0; i < request.num_headers; ++i) { + final header = request.headers[i]; + final name = header.name.cast().toDartString(); + final value = header.value.cast().toDartString(); + headers[name] = value; + } + + _request_callback_async(client, method, url, body, headers, request_context); + // The request struct dies here! + } + + static void _request_callback_async( + HttpClient client, + _HttpMethod method, + Uri url, + String body, + Map headers, + Pointer request_context, + ) async { + await using((arena) async { + final response_pointer = arena(); + final responseRef = response_pointer.ref; + try { + // Build request + late HttpClientRequest request; + switch (method) { + case _HttpMethod.delete: + request = await client.deleteUrl(url); + break; + case _HttpMethod.put: + request = await client.putUrl(url); + break; + case _HttpMethod.patch: + request = await client.patchUrl(url); + break; + case _HttpMethod.post: + request = await client.postUrl(url); + break; + case _HttpMethod.get: + request = await client.getUrl(url); + break; + } + + for (final header in headers.entries) { + request.headers.add(header.key, header.value); + } + + request.add(utf8.encode(body)); + + // Do the call.. + final response = await request.close(); + final responseBody = await response.fold>([], (acc, l) => acc..addAll(l)); // gather response + + // Report back to core + responseRef.status_code = response.statusCode; + responseRef.body = responseBody.toInt8Ptr(arena); + responseRef.body_size = responseBody.length; + + int headerCnt = 0; + response.headers.forEach((name, values) { + headerCnt += values.length; + }); + + responseRef.headers = arena(headerCnt); + responseRef.num_headers = headerCnt; + + response.headers.forEach((name, values) { + int idx = 0; + for (final value in values) { + final headerRef = responseRef.headers.elementAt(idx).ref; + headerRef.name = name.toUtf8Ptr(arena); + headerRef.value = value.toUtf8Ptr(arena); + } + }); + + responseRef.custom_status_code = _CustomErrorCode.noError.code; + } on SocketException catch (_) { + // TODO: A Timeout causes a socket exception, but not all socket exceptions are due to timeouts + responseRef.custom_status_code = _CustomErrorCode.timeout.code; + } on HttpException catch (_) { + responseRef.custom_status_code = _CustomErrorCode.unknownHttp.code; + } catch (_) { + responseRef.custom_status_code = _CustomErrorCode.unknown.code; + } finally { + _realmLib.realm_http_transport_complete_request(request_context, response_pointer); + } + }); + } + ConfigHandle createConfig() { final configPtr = _realmLib.realm_config_new(); return ConfigHandle._(configPtr); @@ -752,15 +913,25 @@ class RealmAppCredentialsHandle extends Handle { RealmAppCredentialsHandle._(Pointer pointer) : super(pointer, 16); } +class RealmHttpTransportHandle extends Handle { + RealmHttpTransportHandle._(Pointer pointer) : super(pointer, 256); // TODO; What should hint be? +} + +extension on List { + Pointer toInt8Ptr(Allocator allocator) { + final nativeSize = length + 1; + final result = allocator(nativeSize); + final Uint8List native = result.asTypedList(nativeSize); + native.setAll(0, this); // copy + native.last = 0; // zero terminate + return result.cast(); + } +} + extension _StringEx on String { Pointer toUtf8Ptr(Allocator allocator) { final units = utf8.encode(this); - final nativeStringSize = units.length + 1; - final result = allocator(nativeStringSize); - final Uint8List nativeString = result.asTypedList(nativeStringSize); - nativeString.setAll(0, units); // copy to native string - nativeString.last = 0; // zero terminate - return result.cast(); + return units.toInt8Ptr(allocator); } Pointer toRealmString(Allocator allocator) { diff --git a/src/realm_dart.cpp b/src/realm_dart.cpp index da465da22..9a4419652 100644 --- a/src/realm_dart.cpp +++ b/src/realm_dart.cpp @@ -70,6 +70,7 @@ void dummy(void) { realm_results_add_notification_callback(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); realm_results_snapshot(nullptr); realm_app_credentials_new_anonymous(); + realm_http_transport_new(nullptr, nullptr, nullptr); #if (ANDROID) realm_android_dummy(); #endif From 9a17d45cc915d360d570dc1581e7bb432ab6e325 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kasper=20Overg=C3=A5rd=20Nielsen?= Date: Fri, 18 Mar 2022 10:26:21 +0100 Subject: [PATCH 05/11] Added ApplicationConfiguration --- lib/src/application_configuration.dart | 79 ++++++++++++++++++++++++ lib/src/cli/metrics/metrics_command.dart | 4 +- lib/src/native/realm_core.dart | 33 ++++++++++ lib/src/realm_class.dart | 1 + src/realm_dart.cpp | 1 + test/application_configuration_test.dart | 39 ++++++++++++ 6 files changed, 155 insertions(+), 2 deletions(-) create mode 100644 lib/src/application_configuration.dart create mode 100644 test/application_configuration_test.dart diff --git a/lib/src/application_configuration.dart b/lib/src/application_configuration.dart new file mode 100644 index 000000000..c7f5f325c --- /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 1d9af251a..3cba46b0f 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 12781b405..4e11ef050 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 7052cb8bf..f7a722ea7 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 9a4419652..6b13c0fc5 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 000000000..3e5db18f8 --- /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), + ); + }); +} From 73e2e4ce4bb55570471ddc07d1429f4c72e4fe73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kasper=20Overg=C3=A5rd=20Nielsen?= Date: Thu, 7 Apr 2022 13:03:06 +0200 Subject: [PATCH 06/11] Update docs on public interface --- lib/src/application_configuration.dart | 31 ++++++++++++++++++++------ lib/src/realm_class.dart | 2 +- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/lib/src/application_configuration.dart b/lib/src/application_configuration.dart index c7f5f325c..237bfaa09 100644 --- a/lib/src/application_configuration.dart +++ b/lib/src/application_configuration.dart @@ -20,9 +20,14 @@ import 'dart:io'; import 'package:meta/meta.dart'; import 'package:pub_semver/pub_semver.dart'; +/// Specify if and how to persists user objects. enum MetadataPersistenceMode { - plainText, + /// Persist [User] objects, but do not encrypt them. + unencrypted, + /// Persist [User] objects in an encrypted store. encrypted, + /// Do not persist [User] objects. + disabled, } @immutable @@ -31,10 +36,13 @@ class ApplicationConfiguration { 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. + /// + /// This data includes metadata for users and synchronized Realms. If set, you must ensure that the [baseFilePath] + /// directory exists. 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; @@ -43,28 +51,37 @@ class ApplicationConfiguration { 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 + /// 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] + /// The [localAppVersion] can be specified, if you wish to distinguish different client versions of the + /// same application. 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] + /// The encryption key to use for user metadata on this device, if [metadataPersistenceMode] is + /// [MetadataPersistenceMode.encrypted]. + /// + /// The [metadataEncryptionKey] must be exactly 64 bytes. + /// Setting 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; + /// Instantiates a new [ApplicationConfiguration]. ApplicationConfiguration( this.appId, { this.baseUrl, @@ -72,7 +89,7 @@ class ApplicationConfiguration { this.defaultRequestTimeout, this.localAppName, this.localAppVersion, - this.metadataPersistenceMode = MetadataPersistenceMode.plainText, + this.metadataPersistenceMode = MetadataPersistenceMode.unencrypted, this.metadataEncryptionKey, HttpClient? httpClient, }) : httpClient = httpClient ?? HttpClient(); diff --git a/lib/src/realm_class.dart b/lib/src/realm_class.dart index f7a722ea7..00a00b5e9 100644 --- a/lib/src/realm_class.dart +++ b/lib/src/realm_class.dart @@ -30,7 +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 "application_configuration.dart" show ApplicationConfiguration, MetadataPersistenceMode; 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; From 0baa62f76d0178e30779dea858791a5511ac2952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kasper=20Overg=C3=A5rd=20Nielsen?= Date: Mon, 11 Apr 2022 09:21:38 +0200 Subject: [PATCH 07/11] Address PR feedback --- lib/src/application_configuration.dart | 42 +++++++++++++----------- test/application_configuration_test.dart | 17 ++++++++-- 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/lib/src/application_configuration.dart b/lib/src/application_configuration.dart index 237bfaa09..aa5f5b724 100644 --- a/lib/src/application_configuration.dart +++ b/lib/src/application_configuration.dart @@ -23,9 +23,11 @@ import 'package:pub_semver/pub_semver.dart'; /// Specify if and how to persists user objects. enum MetadataPersistenceMode { /// Persist [User] objects, but do not encrypt them. - unencrypted, + unencrypted, + /// Persist [User] objects in an encrypted store. encrypted, + /// Do not persist [User] objects. disabled, } @@ -36,45 +38,45 @@ class ApplicationConfiguration { 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. If set, you must ensure that the [baseFilePath] + /// + /// This data includes metadata for users and synchronized Realms. If set, you must ensure that the [baseFilePath] /// directory exists. - final Directory? baseFilePath; + 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; + final Uri baseUrl; /// The [defaultRequestTimeout] for HTTP requests performed as part of authentication. - final Duration? defaultRequestTimeout; + 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. - /// + /// 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] can be specified, if you wish to distinguish different client versions of the + /// The [localAppVersion] can be specified, if you wish to distinguish different client versions of the /// same application. final Version? localAppVersion; final MetadataPersistenceMode metadataPersistenceMode; - /// The encryption key to use for user metadata on this device, if [metadataPersistenceMode] is - /// [MetadataPersistenceMode.encrypted]. - /// + /// The encryption key to use for user metadata on this device, if [metadataPersistenceMode] is + /// [MetadataPersistenceMode.encrypted]. + /// /// The [metadataEncryptionKey] must be exactly 64 bytes. /// Setting 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 @@ -84,13 +86,15 @@ class ApplicationConfiguration { /// Instantiates a new [ApplicationConfiguration]. ApplicationConfiguration( this.appId, { - this.baseUrl, - this.baseFilePath, - this.defaultRequestTimeout, + Uri? baseUrl, + Directory? baseFilePath, + this.defaultRequestTimeout = const Duration(milliseconds: 60000), this.localAppName, this.localAppVersion, this.metadataPersistenceMode = MetadataPersistenceMode.unencrypted, this.metadataEncryptionKey, HttpClient? httpClient, - }) : httpClient = httpClient ?? HttpClient(); + }) : baseUrl = baseUrl ?? Uri.parse('https://realm.mongodb.com'), + baseFilePath = baseFilePath ?? Directory.current, + httpClient = httpClient ?? HttpClient(); } diff --git a/test/application_configuration_test.dart b/test/application_configuration_test.dart index 3e5db18f8..ba8314574 100644 --- a/test/application_configuration_test.dart +++ b/test/application_configuration_test.dart @@ -18,6 +18,7 @@ import 'dart:io'; import 'package:pub_semver/pub_semver.dart'; +import 'package:test/expect.dart'; import '../lib/realm.dart'; import 'test.dart'; @@ -28,12 +29,24 @@ Future main([List? args]) async { setupTests(args); test('ApplicationConfiguration can be created', () { - ApplicationConfiguration( - 'foo', + final a = ApplicationConfiguration('a'); + expect(a.appId, 'a'); + expect(a.baseFilePath.path, Directory.current.path); + expect(a.baseUrl, Uri.parse('https://realm.mongodb.com')); + expect(a.defaultRequestTimeout, const Duration(minutes: 1)); + + final b = ApplicationConfiguration( + 'b', + baseFilePath: Directory.systemTemp, baseUrl: Uri.parse('https://not_re.al'), defaultRequestTimeout: const Duration(seconds: 2), localAppName: 'bar', localAppVersion: Version(1, 0, 0), + httpClient: HttpClient(context: SecurityContext(withTrustedRoots: false)), ); + expect(b.appId, 'b'); + expect(b.baseFilePath.path, Directory.systemTemp.path); + expect(b.baseUrl,Uri.parse('https://not_re.al')); + expect(b.defaultRequestTimeout, const Duration(milliseconds: 2000)); }); } From b9cc16376ffff472715b513cd8dea2883d985897 Mon Sep 17 00:00:00 2001 From: Desislava Stefanova Date: Tue, 12 Apr 2022 01:51:16 +0300 Subject: [PATCH 08/11] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b22431997..679228964 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ x.x.x Release notes (yyyy-MM-dd) * Support result value from write transaction callbacks ([#294](https://github.com/realm/realm-dart/pull/294/)) * Added a property `Realm.isInTransaction` that indicates whether the Realm instance has an open write transaction associated with it. * Support anonymous application credentials ([#443](https://github.com/realm/realm-dart/pull/443/)) +* Support application configuration ([#306](https://github.com/realm/realm-dart/pull/306/)) ### 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)) From c8c9646ee73abb518530d25859f4d5b6539b67cc Mon Sep 17 00:00:00 2001 From: blagoev Date: Tue, 12 Apr 2022 08:57:07 +0300 Subject: [PATCH 09/11] fixup app config --- ...on_configuration.dart => application.dart} | 10 +++--- lib/src/native/realm_core.dart | 32 +++++++++---------- lib/src/realm_class.dart | 2 +- ...ration_test.dart => application_test.dart} | 17 +++++----- 4 files changed, 31 insertions(+), 30 deletions(-) rename lib/src/{application_configuration.dart => application.dart} (91%) rename test/{application_configuration_test.dart => application_test.dart} (80%) diff --git a/lib/src/application_configuration.dart b/lib/src/application.dart similarity index 91% rename from lib/src/application_configuration.dart rename to lib/src/application.dart index aa5f5b724..d7dcb609a 100644 --- a/lib/src/application_configuration.dart +++ b/lib/src/application.dart @@ -18,7 +18,6 @@ import 'dart:io'; import 'package:meta/meta.dart'; -import 'package:pub_semver/pub_semver.dart'; /// Specify if and how to persists user objects. enum MetadataPersistenceMode { @@ -49,7 +48,7 @@ class ApplicationConfiguration { /// 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. + /// The [defaultRequestTimeout] for HTTP requests. Defaults to 60 seconds. final Duration defaultRequestTimeout; /// The [localAppName] is the friendly name identifying the current client application. @@ -64,8 +63,9 @@ class ApplicationConfiguration { /// The [localAppVersion] can be specified, if you wish to distinguish different client versions of the /// same application. - final Version? localAppVersion; + final String? localAppVersion; + /// Enumeration that specifies how and if logged-in User objects are persisted across application launches. final MetadataPersistenceMode metadataPersistenceMode; /// The encryption key to use for user metadata on this device, if [metadataPersistenceMode] is @@ -83,12 +83,12 @@ class ApplicationConfiguration { /// a more complex networking setup. final HttpClient httpClient; - /// Instantiates a new [ApplicationConfiguration]. + /// Instantiates a new [ApplicationConfiguration] with the specified appId. ApplicationConfiguration( this.appId, { Uri? baseUrl, Directory? baseFilePath, - this.defaultRequestTimeout = const Duration(milliseconds: 60000), + this.defaultRequestTimeout = const Duration(seconds: 60), this.localAppName, this.localAppVersion, this.metadataPersistenceMode = MetadataPersistenceMode.unencrypted, diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index 2a9e01654..6155f43af 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -27,7 +27,7 @@ import 'dart:typed_data'; import 'package:ffi/ffi.dart' hide StringUtf8Pointer, StringUtf16Pointer; import 'package:pub_semver/pub_semver.dart'; -import '../application_configuration.dart'; +import '../application.dart'; import '../collections.dart'; import '../init.dart'; import '../list.dart'; @@ -626,26 +626,26 @@ class _RealmCore { AppConfigHandle createAppConfig(ApplicationConfiguration configuration, RealmHttpTransportHandle httpTransport) { return using((arena) { - final c = configuration; - final app_id = c.appId.toUtf8Ptr(arena); + final app_id = configuration.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)); + + _realmLib.realm_app_config_set_base_url(handle._pointer, configuration.baseUrl.toString().toUtf8Ptr(arena)); + _realmLib.realm_app_config_set_default_request_timeout(handle._pointer, configuration.defaultRequestTimeout!.inMilliseconds); + + if (configuration.localAppName != null) { + _realmLib.realm_app_config_set_local_app_name(handle._pointer, configuration.localAppName!.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)); + + if (configuration.localAppVersion != null) { + _realmLib.realm_app_config_set_local_app_version(handle._pointer, configuration.localAppVersion!.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)); + + //This sets the realm lib version instead of the SDK version. + //TODO: Read the SDK version from code generated version field + _realmLib.realm_app_config_set_sdk_version(handle._pointer, libraryVersion.toUtf8Ptr(arena)); return handle; }); diff --git a/lib/src/realm_class.dart b/lib/src/realm_class.dart index 8ba7a641e..dac5e1c00 100644 --- a/lib/src/realm_class.dart +++ b/lib/src/realm_class.dart @@ -30,7 +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, MetadataPersistenceMode; +export 'application.dart' show ApplicationConfiguration, MetadataPersistenceMode; 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/test/application_configuration_test.dart b/test/application_test.dart similarity index 80% rename from test/application_configuration_test.dart rename to test/application_test.dart index ba8314574..64dc030e1 100644 --- a/test/application_configuration_test.dart +++ b/test/application_test.dart @@ -17,7 +17,6 @@ //////////////////////////////////////////////////////////////////////////////// import 'dart:io'; -import 'package:pub_semver/pub_semver.dart'; import 'package:test/expect.dart'; import '../lib/realm.dart'; @@ -29,24 +28,26 @@ Future main([List? args]) async { setupTests(args); test('ApplicationConfiguration can be created', () { - final a = ApplicationConfiguration('a'); - expect(a.appId, 'a'); + final a = ApplicationConfiguration('myapp'); + expect(a.appId, 'myapp'); expect(a.baseFilePath.path, Directory.current.path); expect(a.baseUrl, Uri.parse('https://realm.mongodb.com')); expect(a.defaultRequestTimeout, const Duration(minutes: 1)); + final httpClient = HttpClient(context: SecurityContext(withTrustedRoots: false)); final b = ApplicationConfiguration( - 'b', + 'myapp1', baseFilePath: Directory.systemTemp, baseUrl: Uri.parse('https://not_re.al'), defaultRequestTimeout: const Duration(seconds: 2), localAppName: 'bar', - localAppVersion: Version(1, 0, 0), - httpClient: HttpClient(context: SecurityContext(withTrustedRoots: false)), + localAppVersion: "1.0.0", + httpClient: httpClient, ); - expect(b.appId, 'b'); + 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(milliseconds: 2000)); + expect(b.defaultRequestTimeout, const Duration(seconds: 2)); + expect(b.httpClient, httpClient); }); } From 69d821c765fba0633b2b47c7cbd14ad46ba641b7 Mon Sep 17 00:00:00 2001 From: blagoev Date: Tue, 12 Apr 2022 08:59:21 +0300 Subject: [PATCH 10/11] fix imports spacing --- lib/src/application.dart | 2 +- test/application_test.dart | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/src/application.dart b/lib/src/application.dart index d7dcb609a..999d76d51 100644 --- a/lib/src/application.dart +++ b/lib/src/application.dart @@ -15,8 +15,8 @@ // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// -import 'dart:io'; +import 'dart:io'; import 'package:meta/meta.dart'; /// Specify if and how to persists user objects. diff --git a/test/application_test.dart b/test/application_test.dart index 64dc030e1..6a12a3987 100644 --- a/test/application_test.dart +++ b/test/application_test.dart @@ -15,6 +15,7 @@ // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// + import 'dart:io'; import 'package:test/expect.dart'; From 77bbbdb1ee3ea09481f742a50e86be8a3a81edee Mon Sep 17 00:00:00 2001 From: blagoev Date: Tue, 12 Apr 2022 09:00:53 +0300 Subject: [PATCH 11/11] add method to dummy --- flutter/realm_flutter/ios/Classes/RealmPlugin.m | 1 + 1 file changed, 1 insertion(+) diff --git a/flutter/realm_flutter/ios/Classes/RealmPlugin.m b/flutter/realm_flutter/ios/Classes/RealmPlugin.m index 644e7c2f7..98917c015 100644 --- a/flutter/realm_flutter/ios/Classes/RealmPlugin.m +++ b/flutter/realm_flutter/ios/Classes/RealmPlugin.m @@ -43,6 +43,7 @@ void dummy(void) { realm_list_size(NULL, 0); realm_results_snapshot(NULL); realm_app_credentials_new_anonymous(); + realm_app_config_new(NULL, NULL); } @end