From 453e018215ff2c4ad03fb7fbb8fc19921198fd2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kasper=20Overg=C3=A5rd=20Nielsen?= Date: Mon, 14 Mar 2022 16:24:42 +0100 Subject: [PATCH 01/91] Rollback Dart_WeakHandle finalizer hack --- src/realm_dart_collections.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/realm_dart_collections.cpp b/src/realm_dart_collections.cpp index 79d4c74f6..f24ba4844 100644 --- a/src/realm_dart_collections.cpp +++ b/src/realm_dart_collections.cpp @@ -38,15 +38,14 @@ class CallbackData { void delete_handle() { if (m_handle) { - //TODO: uncomment when the HACK is removed. - //Dart_DeleteWeakPersistentHandle_DL(m_handle); + 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) + : m_handle(Dart_NewWeakPersistentHandle_DL(handle, nullptr, 1, finalize_handle)), m_callback(callback) {} ~CallbackData() { @@ -56,21 +55,16 @@ class CallbackData { 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)); + auto handle = Dart_HandleFromWeakPersistent_DL(m_handle); 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; + Dart_WeakPersistentHandle m_handle; Func m_callback; }; From 8430d42a0b06bd0da7afe0adc3f02d1f5a2efcfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kasper=20Overg=C3=A5rd=20Nielsen?= Date: Tue, 22 Mar 2022 16:42:24 +0100 Subject: [PATCH 02/91] Use dart main/flutter beta in github actions. Drop once stable is updated with finalizer fix. Don't merge before that! --- .github/workflows/pr.yml | 6 +++--- .github/workflows/realm-dart-macos.yml | 2 +- .github/workflows/realm-dart-windows.yml | 2 +- .github/workflows/realm-flutter-android.yml | 2 +- .github/workflows/realm-flutter-ios.yml | 2 +- .github/workflows/realm-flutter-macos.yml | 2 +- .github/workflows/realm-flutter-windows.yml | 2 +- .github/workflows/realm-generator.yml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index d17cf058d..a153ad408 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -32,7 +32,7 @@ jobs: - name : Setup Dart SDK uses: dart-lang/setup-dart@main with: - sdk: stable + sdk: beta - name: Deploy Apps run: | dart run realm_dart deploy-apps \ @@ -85,7 +85,7 @@ jobs: - name : Setup Dart SDK uses: dart-lang/setup-dart@main with: - sdk: stable + sdk: beta - name: Install dependencies run: dart pub get - name: Run tests @@ -136,7 +136,7 @@ jobs: - name: Setup Flutter uses: subosito/flutter-action@v2 with: - channel: 'stable' + channel: beta - name: Enable Flutter Desktop support run: flutter config --enable-linux-desktop - name: Install dependencies diff --git a/.github/workflows/realm-dart-macos.yml b/.github/workflows/realm-dart-macos.yml index 3f3837f8e..3259798af 100644 --- a/.github/workflows/realm-dart-macos.yml +++ b/.github/workflows/realm-dart-macos.yml @@ -30,7 +30,7 @@ jobs: - name : Setup Dart SDK uses: dart-lang/setup-dart@main with: - sdk: stable + sdk: beta - name: Install dependencies run: dart pub get diff --git a/.github/workflows/realm-dart-windows.yml b/.github/workflows/realm-dart-windows.yml index 9aa017692..710e76e2e 100644 --- a/.github/workflows/realm-dart-windows.yml +++ b/.github/workflows/realm-dart-windows.yml @@ -30,7 +30,7 @@ jobs: - name : Setup Dart SDK uses: dart-lang/setup-dart@main with: - sdk: stable + sdk: beta - name: Install dependencies run: dart pub get diff --git a/.github/workflows/realm-flutter-android.yml b/.github/workflows/realm-flutter-android.yml index 4adcfd10d..ab61ac17d 100644 --- a/.github/workflows/realm-flutter-android.yml +++ b/.github/workflows/realm-flutter-android.yml @@ -35,7 +35,7 @@ jobs: - name: Setup Flutter uses: subosito/flutter-action@v2 with: - channel: 'stable' + channel: beta - name: Install dependencies run: flutter pub get diff --git a/.github/workflows/realm-flutter-ios.yml b/.github/workflows/realm-flutter-ios.yml index 845dea4c7..8282e08d7 100644 --- a/.github/workflows/realm-flutter-ios.yml +++ b/.github/workflows/realm-flutter-ios.yml @@ -47,7 +47,7 @@ jobs: - name: Setup Flutter uses: subosito/flutter-action@v2 with: - channel: 'stable' + channel: beta - name: Install dependencies run: flutter pub get diff --git a/.github/workflows/realm-flutter-macos.yml b/.github/workflows/realm-flutter-macos.yml index b5ded4885..e8c8bd2fd 100644 --- a/.github/workflows/realm-flutter-macos.yml +++ b/.github/workflows/realm-flutter-macos.yml @@ -26,7 +26,7 @@ jobs: - name: Setup Flutter uses: subosito/flutter-action@v2 with: - channel: 'stable' + channel: beta - name: Enable Flutter Desktop support run: flutter config --enable-macos-desktop diff --git a/.github/workflows/realm-flutter-windows.yml b/.github/workflows/realm-flutter-windows.yml index 552328081..c95841b81 100644 --- a/.github/workflows/realm-flutter-windows.yml +++ b/.github/workflows/realm-flutter-windows.yml @@ -26,7 +26,7 @@ jobs: - name: Setup Flutter uses: subosito/flutter-action@v2 with: - channel: 'stable' + channel: beta - name: Enable Flutter Desktop support run: flutter config --enable-windows-desktop diff --git a/.github/workflows/realm-generator.yml b/.github/workflows/realm-generator.yml index 3f61acd79..424a10a4e 100644 --- a/.github/workflows/realm-generator.yml +++ b/.github/workflows/realm-generator.yml @@ -21,7 +21,7 @@ jobs: - name : Setup Dart SDK uses: dart-lang/setup-dart@main with: - sdk: stable + sdk: beta - name: Install generator dependencies run: dart pub get From e847de271ca75a3aa5e8e03eaa21750e3ff6b437 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kasper=20Overg=C3=A5rd=20Nielsen?= Date: Wed, 16 Mar 2022 12:35:36 +0100 Subject: [PATCH 03/91] Add dart specific support for realm_http_transport_new Update build --- ffigen/config.yaml | 2 + ffigen/realm_dart_http_transport.h | 1 + .../realm_flutter/ios/Classes/RealmPlugin.m | 2 + flutter/realm_flutter/ios/realm.podspec | 1 + lib/src/native/realm_bindings.dart | 39 ++++++++- scripts/build-ios.sh | 1 + src/CMakeLists.txt | 4 +- src/realm_dart.cpp | 1 + src/realm_dart_http_transport.cpp | 85 +++++++++++++++++++ src/realm_dart_http_transport.h | 46 ++++++++++ 10 files changed, 180 insertions(+), 2 deletions(-) create mode 120000 ffigen/realm_dart_http_transport.h create mode 100644 src/realm_dart_http_transport.cpp create mode 100644 src/realm_dart_http_transport.h diff --git a/ffigen/config.yaml b/ffigen/config.yaml index 7462ad42e..bd87bab7f 100644 --- a/ffigen/config.yaml +++ b/ffigen/config.yaml @@ -7,12 +7,14 @@ headers: - 'realm_dart.h' - 'realm_dart_scheduler.h' - 'realm_dart_collections.h' + - 'realm_dart_http_transport.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_dart_http_transport.h' - 'realm_android_platform.h' compiler-opts: - '-DRLM_NO_ANON_UNIONS' diff --git a/ffigen/realm_dart_http_transport.h b/ffigen/realm_dart_http_transport.h new file mode 120000 index 000000000..0e55ca055 --- /dev/null +++ b/ffigen/realm_dart_http_transport.h @@ -0,0 +1 @@ +../src/realm_dart_http_transport.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 302b4c225..ef38cfe2c 100644 --- a/flutter/realm_flutter/ios/Classes/RealmPlugin.m +++ b/flutter/realm_flutter/ios/Classes/RealmPlugin.m @@ -28,6 +28,7 @@ #import "realm_dart.h" #import "realm_dart_scheduler.h" #import "realm_dart_collections.h" +#import "realm_dart_http_transport.h" #import "platform.h" @implementation RealmPlugin + (void)registerWithRegistrar:(NSObject*)registrar { @@ -43,6 +44,7 @@ void dummy(void) { realm_results_get_object(NULL, 0); realm_list_size(NULL, 0); realm_dart_results_add_notification_callback(NULL, NULL, NULL, NULL); + realm_dart_http_transport_new(NULL, NULL); realm_results_snapshot(NULL); } diff --git a/flutter/realm_flutter/ios/realm.podspec b/flutter/realm_flutter/ios/realm.podspec index 9e77e11e2..c723ad26b 100644 --- a/flutter/realm_flutter/ios/realm.podspec +++ b/flutter/realm_flutter/ios/realm.podspec @@ -25,6 +25,7 @@ Pod::Spec.new do |s| 'src/realm_dart.cpp' 'src/realm_dart_scheduler.cpp' 'src/realm_dart_collections.cpp' + 'src/realm_dart_http_transport.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..e1cf9c64b 100644 --- a/lib/src/native/realm_bindings.dart +++ b/lib/src/native/realm_bindings.dart @@ -3,7 +3,7 @@ // Generated by `package:ffigen`. import 'dart:ffi' as ffi; -/// A Windows config for ffigen Usage: dart run ffigen --config windows.yaml +/// A MacOs config for ffigen Usage: dart run ffigen --config macos.yaml class RealmLibrary { /// Holds the symbol lookup function. final ffi.Pointer Function(String symbolName) @@ -7609,6 +7609,29 @@ class RealmLibrary { realm_dart_on_object_change_func_t, ffi.Pointer)>(); + /// Create a new HTTP transport with these callbacks implementing its functionality. + /// + /// This is a dart specific wrapper for realm_http_transport_new. + ffi.Pointer realm_dart_http_transport_new( + realm_dart_http_request_func_t request_callback, + Object userdata, + ) { + return _realm_dart_http_transport_new( + request_callback, + userdata, + ); + } + + late final _realm_dart_http_transport_newPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + realm_dart_http_request_func_t, + ffi.Handle)>>('realm_dart_http_transport_new'); + late final _realm_dart_http_transport_new = + _realm_dart_http_transport_newPtr.asFunction< + ffi.Pointer Function( + realm_dart_http_request_func_t, Object)>(); + ffi.Pointer realm_dart_get_files_path() { return _realm_dart_get_files_path(); } @@ -8560,3 +8583,17 @@ typedef realm_dart_on_collection_change_func_t = ffi.Pointer< typedef realm_dart_on_object_change_func_t = ffi.Pointer< ffi.NativeFunction< ffi.Void Function(ffi.Handle, ffi.Pointer)>>; + +/// Callback function used by Core to make a HTTP request. +/// +/// Complete the request by calling realm_dart_http_transport_complete_request(), +/// passing in the request_context pointer here and the received response. +/// Network request are expected to be asynchronous and can be completed on any thread. +/// +/// @param userdata The userdata pointer passed to realm_dart_http_transport_new(). +/// @param request The request to send. +/// @param request_context Internal state pointer of Core, needed by realm_http_transport_complete_request(). +typedef realm_dart_http_request_func_t = ffi.Pointer< + ffi.NativeFunction< + ffi.Void Function( + ffi.Handle, realm_http_request_t, ffi.Pointer)>>; diff --git a/scripts/build-ios.sh b/scripts/build-ios.sh index ffdd6f7b4..13fa9e551 100755 --- a/scripts/build-ios.sh +++ b/scripts/build-ios.sh @@ -114,6 +114,7 @@ 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 "$PROJECT_ROOT"/src/realm_dart_http_transport.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 3b6711664..792350c9a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,6 +2,7 @@ set(SOURCES realm_dart.cpp realm_dart_scheduler.cpp realm_dart_collections.cpp + realm_dart_http_transport.cpp dart-include/dart_api_dl.c ) @@ -9,6 +10,7 @@ set(HEADERS realm_dart.h realm_dart_scheduler.h realm_dart_collections.h + realm_dart_http_transport.h realm-core/src/realm.h ) @@ -42,7 +44,7 @@ endif() option(REALM_BUILD_CORE_FROM_SOURCE "Build Realm Core from source" ON) if(REALM_BUILD_CORE_FROM_SOURCE) set(REALM_BUILD_LIB_ONLY ON) - set(REALM_ENABLE_SYNC OFF) + set(REALM_ENABLE_SYNC ON) if(ANDROID) message ("Realm Flutter Android build enabled") diff --git a/src/realm_dart.cpp b/src/realm_dart.cpp index 5197af06f..e6f12263f 100644 --- a/src/realm_dart.cpp +++ b/src/realm_dart.cpp @@ -69,6 +69,7 @@ void dummy(void) { realm_list_size(nullptr, 0); realm_results_add_notification_callback(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); realm_results_snapshot(nullptr); + realm_http_transport_new(nullptr, nullptr, nullptr); #if (ANDROID) realm_android_dummy(); #endif diff --git a/src/realm_dart_http_transport.cpp b/src/realm_dart_http_transport.cpp new file mode 100644 index 000000000..9d2c4b9fc --- /dev/null +++ b/src/realm_dart_http_transport.cpp @@ -0,0 +1,85 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "realm_dart_http_transport.h" +#include "dart_api_dl.h" + +struct HandleScope { + HandleScope() { + Dart_EnterScope_DL(); + } + + ~HandleScope() { + Dart_ExitScope_DL(); + } +}; + +class RequestCallbackData { + //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) { + Dart_DeleteWeakPersistentHandle_DL(m_handle); + m_handle = nullptr; + } + } + +public: + RequestCallbackData(Dart_Handle handle, realm_dart_http_request_func_t callback) + : m_handle(Dart_NewWeakPersistentHandle_DL(handle, nullptr, 1, finalize_handle)), m_callback(callback) + {} + + ~RequestCallbackData() { + delete_handle(); + } + + void callback(const realm_http_request_t request, void* request_context) { + if (m_handle) { + HandleScope scope; + auto handle = Dart_HandleFromWeakPersistent_DL(m_handle); + m_callback(handle, request, request_context); + } + } + +private: + Dart_WeakPersistentHandle m_handle; + realm_dart_http_request_func_t m_callback; +}; + +void free_request_callback_data(void* userdata) { + auto request_callback_data = static_cast(userdata); + delete request_callback_data; +} + +void on_request_callback( + void* userdata, + const realm_http_request_t request, + void* request_context) +{ + auto request_callback_data = static_cast(userdata); + request_callback_data->callback(request, request_context); +} + +RLM_API realm_http_transport_t* realm_dart_http_transport_new( + realm_dart_http_request_func_t request_callback, + Dart_Handle userdata) +{ + auto request_callback_data = new RequestCallbackData(userdata, request_callback); + return realm_http_transport_new(on_request_callback, request_callback_data, free_request_callback_data); +} diff --git a/src/realm_dart_http_transport.h b/src/realm_dart_http_transport.h new file mode 100644 index 000000000..cf247c709 --- /dev/null +++ b/src/realm_dart_http_transport.h @@ -0,0 +1,46 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef REALM_DART_HTTP_TRANSPORT_H +#define REALM_DART_HTTP_TRANSPORT_H + +#include "realm.h" +#include "dart_api_dl.h" + +/** + * Callback function used by Core to make a HTTP request. + * + * Complete the request by calling realm_dart_http_transport_complete_request(), + * passing in the request_context pointer here and the received response. + * Network request are expected to be asynchronous and can be completed on any thread. + * + * @param userdata The userdata pointer passed to realm_dart_http_transport_new(). + * @param request The request to send. + * @param request_context Internal state pointer of Core, needed by realm_http_transport_complete_request(). + */ +typedef void (*realm_dart_http_request_func_t)(Dart_Handle userdata, const realm_http_request_t request, void* request_context); + +/** + * Create a new HTTP transport with these callbacks implementing its functionality. + * + * This is a dart specific wrapper for realm_http_transport_new. + */ +RLM_API realm_http_transport_t* realm_dart_http_transport_new(realm_dart_http_request_func_t request_callback, + Dart_Handle userdata); + +#endif \ No newline at end of file From 6a5c9c489cf4f7df25c52c0aa305715e814ab408 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/91] Wire up realm_core --- lib/src/native/realm_core.dart | 102 +++++++++++++++++++++++++++++++-- 1 file changed, 96 insertions(+), 6 deletions(-) diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index 977eebefe..ab8ac780d 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -21,6 +21,7 @@ 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 @@ -181,6 +182,85 @@ class _RealmCore { }); } + RealmHttpTransportHandle createHttpTransport(HttpClient httpClient) { + return RealmHttpTransportHandle._(_realmLib.realm_dart_http_transport_new(Pointer.fromFunction(request_callback), httpClient)); + } + + static void request_callback(Object userData, realm_http_request request, Pointer request_context) { + () async { // TODO: Use another isolate, perhaps? + final client = userData as HttpClient; + client.connectionTimeout = Duration(milliseconds: request.timeout_ms); + try { + final url = Uri.parse(request.url.cast().toDartString()); + late HttpClientRequest mappedRequest; + switch (request.method) { + case 4: // DELETE + mappedRequest = await client.deleteUrl(url); + break; + case 3: // PUT + mappedRequest = await client.putUrl(url); + break; + case 2: // PATCH + mappedRequest = await client.patchUrl(url); + break; + case 1: // POST + mappedRequest = await client.postUrl(url); + break; + case 0: // GET + default: + mappedRequest = await client.getUrl(url); + break; + } + + final body = request.body.asTypedList(request.body_size); // we avoid a potentially expensive copy here + mappedRequest.add(body); + + // Unfortunately we need to copy the headers, due to the interface of HttpClientRequest + 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(); + mappedRequest.headers.add(name, value); + } + + // Do the call.. + final response = await mappedRequest.close(); + final responseBody = await response.fold>([], (acc, l) => acc..addAll(l)); // gather response + + // Report back to core + using((arena) { + final response_pointer = arena(); + final responseRef = response_pointer.ref; + + responseRef.body = responseBody.toInt8Ptr(arena); // need to copy here :-( + responseRef.body_size = responseBody.length; + + int headerCnt = 0; + response.headers.forEach((name, values) { + // Freaking odd interface :-/ + 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); + } + }); + + _realmLib.realm_http_transport_complete_request(request_context, response_pointer); + }); + } catch (ex) { + print(ex); // TODO! + } + }; + } + ConfigHandle createConfig() { final configPtr = _realmLib.realm_config_new(); return ConfigHandle._(configPtr); @@ -725,15 +805,25 @@ class RealmObjectChangesHandle extends Handle { RealmObjectChangesHandle._(Pointer pointer) : super(pointer, 256); } +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); } } From fca9182da64689e7be09b0c314b8cbb91081e743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kasper=20Overg=C3=A5rd=20Nielsen?= Date: Mon, 28 Mar 2022 11:00:50 +0200 Subject: [PATCH 05/91] Apply suggestions from code review Co-authored-by: blagoev --- lib/src/native/realm_core.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index ab8ac780d..e0e730b7b 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -187,7 +187,7 @@ class _RealmCore { } static void request_callback(Object userData, realm_http_request request, Pointer request_context) { - () async { // TODO: Use another isolate, perhaps? + () async { final client = userData as HttpClient; client.connectionTimeout = Duration(milliseconds: request.timeout_ms); try { @@ -232,12 +232,11 @@ class _RealmCore { final response_pointer = arena(); final responseRef = response_pointer.ref; - responseRef.body = responseBody.toInt8Ptr(arena); // need to copy here :-( + responseRef.body = responseBody.toInt8Ptr(arena); responseRef.body_size = responseBody.length; int headerCnt = 0; response.headers.forEach((name, values) { - // Freaking odd interface :-/ headerCnt += values.length; }); From 435e484904fea2813433158ff3406656ae249f06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kasper=20Overg=C3=A5rd=20Nielsen?= Date: Mon, 28 Mar 2022 14:10:23 +0200 Subject: [PATCH 06/91] Copy all info from realm_http_request before starting async request --- lib/src/native/realm_core.dart | 149 +++++++++++++++++++++------------ 1 file changed, 94 insertions(+), 55 deletions(-) diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index e0e730b7b..418fa06d0 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -40,6 +40,14 @@ late RealmLibrary _realmLib; final _RealmCore realmCore = _RealmCore(); +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; @@ -187,77 +195,108 @@ class _RealmCore { } static void request_callback(Object userData, realm_http_request request, Pointer request_context) { - () async { - final client = userData as HttpClient; - client.connectionTimeout = Duration(milliseconds: request.timeout_ms); + // + // 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 = 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; + // pre-fill in case something fails + responseRef.custom_status_code = 100; // TODO! + try { - final url = Uri.parse(request.url.cast().toDartString()); - late HttpClientRequest mappedRequest; - switch (request.method) { - case 4: // DELETE - mappedRequest = await client.deleteUrl(url); + // Build request + late HttpClientRequest request; + switch (method) { + case _HttpMethod.delete: + request = await client.deleteUrl(url); break; - case 3: // PUT - mappedRequest = await client.putUrl(url); + case _HttpMethod.put: + request = await client.putUrl(url); break; - case 2: // PATCH - mappedRequest = await client.patchUrl(url); + case _HttpMethod.patch: + request = await client.patchUrl(url); break; - case 1: // POST - mappedRequest = await client.postUrl(url); + case _HttpMethod.post: + request = await client.postUrl(url); break; - case 0: // GET - default: - mappedRequest = await client.getUrl(url); + case _HttpMethod.get: + request = await client.getUrl(url); break; } - final body = request.body.asTypedList(request.body_size); // we avoid a potentially expensive copy here - mappedRequest.add(body); - - // Unfortunately we need to copy the headers, due to the interface of HttpClientRequest - 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(); - mappedRequest.headers.add(name, value); + for (final header in headers.entries) { + request.headers.add(header.key, header.value); } + request.add(utf8.encode(body)); + // Do the call.. - final response = await mappedRequest.close(); + final response = await request.close(); final responseBody = await response.fold>([], (acc, l) => acc..addAll(l)); // gather response // Report back to core - using((arena) { - final response_pointer = arena(); - final responseRef = response_pointer.ref; - - 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); - } - }); - - _realmLib.realm_http_transport_complete_request(request_context, response_pointer); + 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); + } }); - } catch (ex) { - print(ex); // TODO! + + responseRef.custom_status_code = 0; // all is well, reset custom_status_code + } finally { + _realmLib.realm_http_transport_complete_request(request_context, response_pointer); } - }; + }); } ConfigHandle createConfig() { From d7207d94b7ab9ce069025ab97f6852943668b535 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 07/91] Added ApplicationConfiguration --- lib/src/application_configuration.dart | 51 ++++++++++++++++++++++ lib/src/cli/metrics/metrics_command.dart | 4 +- lib/src/native/realm_core.dart | 54 ++++++++++++++++++++++++ src/realm_dart.cpp | 1 + test/application_configuration_test.dart | 38 +++++++++++++++++ 5 files changed, 146 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..d8cda526c --- /dev/null +++ b/lib/src/application_configuration.dart @@ -0,0 +1,51 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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'; + +import 'cli/common/utils.dart'; +import 'native/realm_core.dart'; + +@immutable +class ApplicationConfiguration { + final RealmAppConfigHandle _handle; + + final String appId; + final Uri? baseUrl; + final Duration? defaultRequestTimeout; + final String? localAppName; + final Version? localAppVersion; + + ApplicationConfiguration( + this.appId, { + this.baseUrl, + this.defaultRequestTimeout, + this.localAppName, + this.localAppVersion, + }) : _handle = realmCore.createAppConfig(appId, realmCore.createHttpTransport(HttpClient())) { + if (baseUrl != null) realmCore.setAppConfigBaseUrl(_handle, baseUrl!); + if (defaultRequestTimeout != null) realmCore.setAppConfigDefaultRequestTimeout(_handle, defaultRequestTimeout!); + if (localAppName != null) realmCore.setAppConfigLocalAppName(_handle, localAppName!); + if (localAppVersion != null) realmCore.setAppConfigLocalAppVersion(_handle, localAppVersion!); + realmCore.setAppConfigPlatform(_handle, Platform.operatingSystem); + realmCore.setAppConfigPlatformVersion(_handle, Platform.operatingSystemVersion); + realmCore.setAppConfigSdkVersion(_handle, Version.parse(Platform.version.takeUntil(' '))); + } +} 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 418fa06d0..86c445c23 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -26,6 +26,7 @@ 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 '../collections.dart'; import '../configuration.dart'; @@ -751,6 +752,55 @@ class _RealmCore { return out_modified.asTypedList(count).toList(); }); } + + RealmAppConfigHandle createAppConfig(String appId, RealmHttpTransportHandle httpTransport) { + return using((arena) { + final app_id = appId.toUtf8Ptr(arena); + return RealmAppConfigHandle._(_realmLib.realm_app_config_new(app_id, httpTransport._pointer)); + }); + } + + void setAppConfigBaseUrl(RealmAppConfigHandle handle, Uri baseUrl) { + using((arena) { + _realmLib.realm_app_config_set_base_url(handle._pointer, baseUrl.toString().toUtf8Ptr(arena)); + }); + } + + void setAppConfigDefaultRequestTimeout(RealmAppConfigHandle handle, Duration defaultRequestTimeout) { + _realmLib.realm_app_config_set_default_request_timeout(handle._pointer, defaultRequestTimeout.inMilliseconds); + } + + void setAppConfigLocalAppName(RealmAppConfigHandle handle, String localAppName) { + using((arena) { + _realmLib.realm_app_config_set_local_app_name(handle._pointer, localAppName.toUtf8Ptr(arena)); + }); + } + + void setAppConfigLocalAppVersion(RealmAppConfigHandle handle, Version localAppVersion) { + using((arena) { + final versionString = localAppVersion.toString(); + _realmLib.realm_app_config_set_local_app_version(handle._pointer, versionString.toUtf8Ptr(arena)); + }); + } + + void setAppConfigPlatform(RealmAppConfigHandle handle, String platform) { + using((arena) { + _realmLib.realm_app_config_set_platform(handle._pointer, platform.toUtf8Ptr(arena)); + }); + } + + void setAppConfigPlatformVersion(RealmAppConfigHandle handle, String platformVersion) { + using((arena) { + _realmLib.realm_app_config_set_platform_version(handle._pointer, platformVersion.toUtf8Ptr(arena)); + }); + } + + void setAppConfigSdkVersion(RealmAppConfigHandle handle, Version sdkVersion) { + using((arena) { + final versionString = sdkVersion.toString(); + _realmLib.realm_app_config_set_sdk_version(handle._pointer, versionString.toUtf8Ptr(arena)); + }); + } } class LastError { @@ -847,6 +897,10 @@ class RealmHttpTransportHandle extends Handle { RealmHttpTransportHandle._(Pointer pointer) : super(pointer, 256); // TODO; What should hint be? } +class RealmAppConfigHandle extends Handle { + RealmAppConfigHandle._(Pointer pointer) : super(pointer, 256); // TODO: What should hint be? +} + extension on List { Pointer toInt8Ptr(Allocator allocator) { final nativeSize = length + 1; diff --git a/src/realm_dart.cpp b/src/realm_dart.cpp index e6f12263f..089a22d9b 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_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..086e1b05d --- /dev/null +++ b/test/application_configuration_test.dart @@ -0,0 +1,38 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 'package:realm_dart/src/application_configuration.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 ea8d7ef7405f1c0593852f65fc2367c6d14c01fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kasper=20Overg=C3=A5rd=20Nielsen?= Date: Fri, 25 Mar 2022 10:22:15 +0100 Subject: [PATCH 08/91] Added Application class with async logIn method (includes stubs for User and Credentials) --- ffigen/config.yaml | 2 ++ ffigen/realm_dart_app.h | 1 + lib/src/application.dart | 32 +++++++++++++++++ lib/src/application_configuration.dart | 4 +++ lib/src/credentials.dart | 32 +++++++++++++++++ lib/src/native/realm_bindings.dart | 50 +++++++++++++++++++++++++- lib/src/native/realm_core.dart | 39 ++++++++++++++++++++ lib/src/realm_class.dart | 4 +++ lib/src/user.dart | 29 +++++++++++++++ src/realm_dart_app.h | 48 +++++++++++++++++++++++++ 10 files changed, 240 insertions(+), 1 deletion(-) create mode 120000 ffigen/realm_dart_app.h create mode 100644 lib/src/application.dart create mode 100644 lib/src/credentials.dart create mode 100644 lib/src/user.dart create mode 100644 src/realm_dart_app.h diff --git a/ffigen/config.yaml b/ffigen/config.yaml index bd87bab7f..2fcf317f6 100644 --- a/ffigen/config.yaml +++ b/ffigen/config.yaml @@ -5,6 +5,7 @@ headers: entry-points: - 'realm.h' - 'realm_dart.h' + - 'realm_dart_app.h' - 'realm_dart_scheduler.h' - 'realm_dart_collections.h' - 'realm_dart_http_transport.h' @@ -12,6 +13,7 @@ headers: include-directives: #generate only for these headers - 'realm.h' - 'realm_dart.h' + - 'realm_dart_app.h' - 'realm_dart_scheduler.h' - 'realm_dart_collections.h' - 'realm_dart_http_transport.h' diff --git a/ffigen/realm_dart_app.h b/ffigen/realm_dart_app.h new file mode 120000 index 000000000..6f5c0c684 --- /dev/null +++ b/ffigen/realm_dart_app.h @@ -0,0 +1 @@ +../src/realm_dart_app.h \ No newline at end of file diff --git a/lib/src/application.dart b/lib/src/application.dart new file mode 100644 index 000000000..8e49918e8 --- /dev/null +++ b/lib/src/application.dart @@ -0,0 +1,32 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 'application_configuration.dart'; + +import 'native/realm_core.dart'; +import 'credentials.dart'; +import 'user.dart'; + +class Application { + final RealmAppHandle _handle; + + Application(ApplicationConfiguration configuration) : _handle = realmCore.getApp(configuration.handle); + + Future logIn(Credentials credentials) async { + return UserInternal.create(await realmCore.logIn(_handle, credentials.handle)); + } +} diff --git a/lib/src/application_configuration.dart b/lib/src/application_configuration.dart index d8cda526c..aa83657e6 100644 --- a/lib/src/application_configuration.dart +++ b/lib/src/application_configuration.dart @@ -49,3 +49,7 @@ class ApplicationConfiguration { realmCore.setAppConfigSdkVersion(_handle, Version.parse(Platform.version.takeUntil(' '))); } } + +extension ApplicationConfigurationInternal on ApplicationConfiguration { + RealmAppConfigHandle get handle => _handle; +} \ No newline at end of file diff --git a/lib/src/credentials.dart b/lib/src/credentials.dart new file mode 100644 index 000000000..c4685cc2c --- /dev/null +++ b/lib/src/credentials.dart @@ -0,0 +1,32 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +// Stubs for Credentials + +import 'native/realm_core.dart'; + +class Credentials { + late final RealmAppCredentialsHandle _handle; + + Credentials.anonymous(); +} + +extension CredentialsInternal on Credentials{ + RealmAppCredentialsHandle get handle => _handle; +} + diff --git a/lib/src/native/realm_bindings.dart b/lib/src/native/realm_bindings.dart index e1cf9c64b..8ea22423a 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 MacOs config for ffigen Usage: dart run ffigen --config macos.yaml class RealmLibrary { /// Holds the symbol lookup function. final ffi.Pointer Function(String symbolName) @@ -7450,6 +7449,41 @@ class RealmLibrary { late final _realm_delete_finalizable = _realm_delete_finalizablePtr .asFunction(); + /// @brief + /// + /// @param completion + /// @param userdata + /// @return true if operation started successfully, false if an error occurred. + bool realm_dart_app_log_in_with_credentials( + ffi.Pointer arg0, + ffi.Pointer arg1, + realm_dart_app_user_completion_func_t completion, + Object userdata, + ) { + return _realm_dart_app_log_in_with_credentials( + arg0, + arg1, + completion, + userdata, + ) != + 0; + } + + late final _realm_dart_app_log_in_with_credentialsPtr = _lookup< + ffi.NativeFunction< + ffi.Uint8 Function( + ffi.Pointer, + ffi.Pointer, + realm_dart_app_user_completion_func_t, + ffi.Handle)>>('realm_dart_app_log_in_with_credentials'); + late final _realm_dart_app_log_in_with_credentials = + _realm_dart_app_log_in_with_credentialsPtr.asFunction< + int Function( + ffi.Pointer, + ffi.Pointer, + realm_dart_app_user_completion_func_t, + Object)>(); + ffi.Pointer realm_dart_create_scheduler( int isolateId, int port, @@ -8574,6 +8608,20 @@ typedef Dart_FinalizableHandle = ffi.Pointer<_Dart_FinalizableHandle>; class _Dart_FinalizableHandle extends ffi.Opaque {} +/// Completion callback for asynchronous Realm App operations that yield a user object. +/// +/// @param userdata The userdata the asynchronous operation was started with. +/// @param user User object produced by the operation, or null if it failed. +/// The pointer is alive only for the duration of the callback, +/// if you wish to use it further make a copy with realm_clone(). +/// @param error Pointer to an error object if the operation failed, otherwise null if it completed successfully. +/// +/// This is a dart specific version of the completion callback for asynchronous Realm operations. +typedef realm_dart_app_user_completion_func_t = ffi.Pointer< + ffi.NativeFunction< + ffi.Void Function(ffi.Handle, ffi.Pointer, + ffi.Pointer)>>; + /// 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< diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index 86c445c23..45666c87d 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -18,6 +18,7 @@ // ignore_for_file: constant_identifier_names, non_constant_identifier_names +import 'dart:async'; import 'dart:convert'; import 'dart:ffi'; import 'dart:ffi' as ffi show Handle; @@ -801,6 +802,32 @@ class _RealmCore { _realmLib.realm_app_config_set_sdk_version(handle._pointer, versionString.toUtf8Ptr(arena)); }); } + + RealmAppHandle getApp(RealmAppConfigHandle appConfig) { + return RealmAppHandle._(_realmLib.realm_app_get(appConfig._pointer, nullptr)); // TODO: realm_sync_client_config + } + + static void _loginCallback(Object userdata, Pointer user, Pointer error) { + if (userdata is Completer) { + if (error != nullptr) { + final message = error.ref.message.cast().toDartString(); + userdata.completeError(RealmException(message)); + } else { + userdata.complete(RealmUserHandle._(_realmLib.realm_clone(user.cast()).cast())); + } + } + } + + Future logIn(RealmAppHandle app, RealmAppCredentialsHandle credentials) { + final completer = Completer(); + _realmLib.invokeGetBool(() => _realmLib.realm_dart_app_log_in_with_credentials( + app._pointer, + credentials._pointer, + Pointer.fromFunction(_loginCallback), + completer, + )); + return completer.future; + } } class LastError { @@ -901,6 +928,18 @@ class RealmAppConfigHandle extends Handle { RealmAppConfigHandle._(Pointer pointer) : super(pointer, 256); // TODO: What should hint be? } +class RealmAppHandle extends Handle { + RealmAppHandle._(Pointer pointer) : super(pointer, 256); // TODO: What should hint be? +} + +class RealmAppCredentialsHandle extends Handle { + RealmAppCredentialsHandle._(Pointer pointer) : super(pointer, 256); // TODO: What should hint be? +} + +class RealmUserHandle extends Handle { + RealmUserHandle._(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 c287ca4a1..1e4dfe96f 100644 --- a/lib/src/realm_class.dart +++ b/lib/src/realm_class.dart @@ -32,11 +32,15 @@ import 'results.dart'; // always expose with `show` to explicitly control the public API surface export 'package:realm_common/realm_common.dart' show Ignored, Indexed, MapTo, PrimaryKey, RealmError, RealmModel, RealmUnsupportedSetError, RealmStateError, RealmCollectionType, RealmPropertyType; + +export 'application.dart' show Application; +export 'application_configuration.dart' show ApplicationConfiguration; export "configuration.dart" show Configuration, RealmSchema, SchemaObject; export 'list.dart' show RealmList, RealmListOfObject, RealmListChanges; export 'realm_object.dart' show RealmEntity, RealmException, RealmObject, RealmObjectChanges; export 'realm_property.dart'; export 'results.dart' show RealmResults, RealmResultsChanges; +export 'user.dart' show User; /// A [Realm] instance represents a `Realm` database. /// diff --git a/lib/src/user.dart b/lib/src/user.dart new file mode 100644 index 000000000..e92a605b8 --- /dev/null +++ b/lib/src/user.dart @@ -0,0 +1,29 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 'native/realm_core.dart'; + +class User { + final RealmUserHandle _handle; + + User._(this._handle); +} + +extension UserInternal on User { + static User create(RealmUserHandle handle) => User._(handle); +} \ No newline at end of file diff --git a/src/realm_dart_app.h b/src/realm_dart_app.h new file mode 100644 index 000000000..bf90f828b --- /dev/null +++ b/src/realm_dart_app.h @@ -0,0 +1,48 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef REALM_DART_APP_H +#define REALM_DART_APP_H + +#include "realm.h" +#include "dart_api_dl.h" + +/** + * Completion callback for asynchronous Realm App operations that yield a user object. + * + * @param userdata The userdata the asynchronous operation was started with. + * @param user User object produced by the operation, or null if it failed. + * The pointer is alive only for the duration of the callback, + * if you wish to use it further make a copy with realm_clone(). + * @param error Pointer to an error object if the operation failed, otherwise null if it completed successfully. + * + * This is a dart specific version of the completion callback for asynchronous Realm operations. + */ +typedef void (*realm_dart_app_user_completion_func_t)(Dart_Handle userdata, realm_user_t* user, const realm_app_error_t* error); + +/** + * @brief + * + * @param completion + * @param userdata + * @return true if operation started successfully, false if an error occurred. + */ +RLM_API bool realm_dart_app_log_in_with_credentials(realm_app_t*, realm_app_credentials_t*, + realm_dart_app_user_completion_func_t completion, Dart_Handle userdata); + +#endif \ No newline at end of file From b4eaf29d97a86a7972a3b461aa00e5412046310c Mon Sep 17 00:00:00 2001 From: Desislava Stefanova Date: Wed, 30 Mar 2022 23:58:24 +0300 Subject: [PATCH 09/91] Implement email/password provider --- lib/src/application.dart | 13 ++++++++-- lib/src/email_password_provider.dart | 38 ++++++++++++++++++++++++++++ lib/src/native/realm_core.dart | 17 +++++++++++++ lib/src/realm_class.dart | 3 ++- 4 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 lib/src/email_password_provider.dart diff --git a/lib/src/application.dart b/lib/src/application.dart index 8e49918e8..e92e4dfee 100644 --- a/lib/src/application.dart +++ b/lib/src/application.dart @@ -16,17 +16,26 @@ // //////////////////////////////////////////////////////////////////////////////// import 'application_configuration.dart'; - +import 'email_password_provider.dart'; import 'native/realm_core.dart'; import 'credentials.dart'; import 'user.dart'; class Application { final RealmAppHandle _handle; + late final EmailPasswordProvider _emailPasswordProvider; + + Application(ApplicationConfiguration configuration) : _handle = realmCore.getApp(configuration.handle) { + _emailPasswordProvider = EmailPasswordProvider(this); + } - Application(ApplicationConfiguration configuration) : _handle = realmCore.getApp(configuration.handle); + EmailPasswordProvider get emailPasswordProvider => _emailPasswordProvider; Future logIn(Credentials credentials) async { return UserInternal.create(await realmCore.logIn(_handle, credentials.handle)); } } + +extension ApplicationInternal on Application { + RealmAppHandle get handle => _handle; +} diff --git a/lib/src/email_password_provider.dart b/lib/src/email_password_provider.dart new file mode 100644 index 000000000..91e79a593 --- /dev/null +++ b/lib/src/email_password_provider.dart @@ -0,0 +1,38 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 'application.dart'; +import 'native/realm_core.dart'; + +/// A class, encapsulating functionality for users, logged in with [Credentials.emailPassword()]. +/// It is always scoped to a particular app and can only be accessed via [emailPasswordProvider]. +/// {@category Application} +class EmailPasswordProvider { + final RealmAppHandle _handle; + + EmailPasswordProvider(Application app) : _handle = app.handle; + + /// Registers a new user with the given email and password. + /// The [email] to register with. This will be the user's username and, if user confirmation is enabled, this will be the address for + /// the confirmation email. + /// The [password] to associate with the email. The password must be between 6 and 128 characters long. + /// Returns an awaitable [Future] representing the asynchronous RegisterUser operation. Successful completion indicates that the user has been + /// created on the server and can now be logged in calling [logIn] with [Credentials.emailPassword()]" + Future registerUser(String email, String password) async { + return await realmCore.appEmailPasswordRegisterUser(_handle, email, password); + } +} diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index 45666c87d..7410f809d 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -828,6 +828,15 @@ class _RealmCore { )); return completer.future; } + + Future appEmailPasswordRegisterUser(RealmAppHandle app, String email, String password) { + final completer = Completer(); + using((arena) { + _realmLib.invokeGetBool(() => _realmLib.realm_app_email_password_provider_client_register_email( + app._pointer, email.toUtf8Ptr(arena), password.toRealmString(arena).ref, nullptr, nullptr, nullptr)); + }); + return completer.future; + } } class LastError { @@ -956,6 +965,14 @@ extension _StringEx on String { final units = utf8.encode(this); return units.toInt8Ptr(allocator); } + + Pointer toRealmString(Allocator allocator) { + final realm_string = allocator(); + realm_string.ref.data = toUtf8Ptr(allocator); + final units = utf8.encode(this); + realm_string.ref.size = units.length + 1; + return realm_string; + } } extension _RealmLibraryEx on RealmLibrary { diff --git a/lib/src/realm_class.dart b/lib/src/realm_class.dart index 1e4dfe96f..38364cd2f 100644 --- a/lib/src/realm_class.dart +++ b/lib/src/realm_class.dart @@ -33,6 +33,7 @@ import 'results.dart'; export 'package:realm_common/realm_common.dart' show Ignored, Indexed, MapTo, PrimaryKey, RealmError, RealmModel, RealmUnsupportedSetError, RealmStateError, RealmCollectionType, RealmPropertyType; +export 'email_password_provider.dart' show EmailPasswordProvider; export 'application.dart' show Application; export 'application_configuration.dart' show ApplicationConfiguration; export "configuration.dart" show Configuration, RealmSchema, SchemaObject; @@ -74,7 +75,7 @@ class Realm { _scheduler.stop(); rethrow; } - _config.isInUse = true; + _config.isInUse = true; } /// Deletes all files associated with a `Realm` located at given [path] From 5e887a0f47a68d057aff5bfc65abbe3fed66d60d Mon Sep 17 00:00:00 2001 From: Desislava Stefanova Date: Thu, 31 Mar 2022 15:49:52 +0300 Subject: [PATCH 10/91] Implement RegisterUser in Application emailPasswordProvider --- .../tests/test_driver/realm_test.dart | 3 ++ lib/src/email_password_provider.dart | 2 +- lib/src/native/realm_bindings.dart | 28 +++++++++++++ lib/src/native/realm_core.dart | 20 ++++++++-- src/realm_dart.cpp | 35 ++++++++++++++++ src/realm_dart.h | 4 ++ test/email_password_provider_test.dart | 40 +++++++++++++++++++ 7 files changed, 127 insertions(+), 5 deletions(-) create mode 100644 test/email_password_provider_test.dart diff --git a/flutter/realm_flutter/tests/test_driver/realm_test.dart b/flutter/realm_flutter/tests/test_driver/realm_test.dart index 869771812..2207be580 100644 --- a/flutter/realm_flutter/tests/test_driver/realm_test.dart +++ b/flutter/realm_flutter/tests/test_driver/realm_test.dart @@ -11,6 +11,8 @@ import '../test/realm_test.dart' as realm_tests; import '../test/realm_object_test.dart' as realm_object_tests; import '../test/list_test.dart' as list_tests; import '../test/results_test.dart' as results_tests; +import '../test/email_password_provider_test.dart' as email_password_provider_test; + Future main(List args) async { final Completer completer = Completer(); @@ -21,6 +23,7 @@ Future main(List args) async { await realm_object_tests.main(args); await list_tests.main(args); await results_tests.main(args); + await email_password_provider_test.main(args); tearDown(() { if (Invoker.current?.liveTest.state.result == test_api.Result.error || Invoker.current?.liveTest.state.result == test_api.Result.failure) { diff --git a/lib/src/email_password_provider.dart b/lib/src/email_password_provider.dart index 91e79a593..f0ca279c4 100644 --- a/lib/src/email_password_provider.dart +++ b/lib/src/email_password_provider.dart @@ -32,7 +32,7 @@ class EmailPasswordProvider { /// The [password] to associate with the email. The password must be between 6 and 128 characters long. /// Returns an awaitable [Future] representing the asynchronous RegisterUser operation. Successful completion indicates that the user has been /// created on the server and can now be logged in calling [logIn] with [Credentials.emailPassword()]" - Future registerUser(String email, String password) async { + Future registerUser(String email, String password) async { return await realmCore.appEmailPasswordRegisterUser(_handle, email, password); } } diff --git a/lib/src/native/realm_bindings.dart b/lib/src/native/realm_bindings.dart index 8ea22423a..07e1b8468 100644 --- a/lib/src/native/realm_bindings.dart +++ b/lib/src/native/realm_bindings.dart @@ -7449,6 +7449,34 @@ class RealmLibrary { late final _realm_delete_finalizable = _realm_delete_finalizablePtr .asFunction(); + ffi.Pointer gc_handle_toPtr( + Object handle, + ) { + return _gc_handle_toPtr( + handle, + ); + } + + late final _gc_handle_toPtrPtr = + _lookup Function(ffi.Handle)>>( + 'gc_handle_toPtr'); + late final _gc_handle_toPtr = + _gc_handle_toPtrPtr.asFunction Function(Object)>(); + + Object gc_handle_fromPtr( + ffi.Pointer handler, + ) { + return _gc_handle_fromPtr( + handler, + ); + } + + late final _gc_handle_fromPtrPtr = + _lookup)>>( + 'gc_handle_fromPtr'); + late final _gc_handle_fromPtr = _gc_handle_fromPtrPtr + .asFunction)>(); + /// @brief /// /// @param completion diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index 7410f809d..3f549afc4 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -829,11 +829,23 @@ class _RealmCore { return completer.future; } - Future appEmailPasswordRegisterUser(RealmAppHandle app, String email, String password) { - final completer = Completer(); + static void _appEmailPasswordProviderCallback(Pointer completerPtr, Pointer error) { + final completer = _realmLib.gc_handle_fromPtr(completerPtr); + if (completer is Completer) { + if (error != nullptr) { + final message = error.ref.message.cast().toDartString(); + completer.completeError(RealmException(message)); + } else { + completer.complete(); + } + } + } + + Future appEmailPasswordRegisterUser(RealmAppHandle app, String email, String password) { + final completer = Completer(); using((arena) { - _realmLib.invokeGetBool(() => _realmLib.realm_app_email_password_provider_client_register_email( - app._pointer, email.toUtf8Ptr(arena), password.toRealmString(arena).ref, nullptr, nullptr, nullptr)); + _realmLib.invokeGetBool(() => _realmLib.realm_app_email_password_provider_client_register_email(app._pointer, email.toUtf8Ptr(arena), + password.toRealmString(arena).ref, Pointer.fromFunction(_appEmailPasswordProviderCallback), _realmLib.gc_handle_toPtr(completer), nullptr)); }); return completer.future; } diff --git a/src/realm_dart.cpp b/src/realm_dart.cpp index 089a22d9b..60e00b1d3 100644 --- a/src/realm_dart.cpp +++ b/src/realm_dart.cpp @@ -71,7 +71,42 @@ void dummy(void) { realm_results_snapshot(nullptr); realm_http_transport_new(nullptr, nullptr, nullptr); realm_app_config_new(nullptr, nullptr); + gc_handle_toPtr(nullptr); + gc_handle_fromPtr(nullptr); #if (ANDROID) realm_android_dummy(); #endif +} + + +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_toPtr(Dart_Handle handle) { + return new GCHandle(handle); +} + +RLM_API Dart_Handle gc_handle_fromPtr(void* handle) { + return reinterpret_cast(handle)->value(); } \ No newline at end of file diff --git a/src/realm_dart.h b/src/realm_dart.h index fcf8fdc1b..f2a5de548 100644 --- a/src/realm_dart.h +++ b/src/realm_dart.h @@ -28,4 +28,8 @@ 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_toPtr(Dart_Handle handle); +RLM_API Dart_Handle gc_handle_fromPtr(void* handler); + #endif // REALM_DART_H \ No newline at end of file diff --git a/test/email_password_provider_test.dart b/test/email_password_provider_test.dart new file mode 100644 index 000000000..20699cbda --- /dev/null +++ b/test/email_password_provider_test.dart @@ -0,0 +1,40 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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('Email/Password - register user', () async { + final appConfig = ApplicationConfiguration( + 'foo', + baseUrl: Uri.parse('https://not_re.al'), + defaultRequestTimeout: const Duration(seconds: 2), + localAppName: 'bar', + localAppVersion: Version(1, 0, 0), + ); + final app = Application(appConfig); + await app.emailPasswordProvider.registerUser("foo@bar.com", "pwd"); + }); +} From b034700b93513847f28f10b1bca12cf418881222 Mon Sep 17 00:00:00 2001 From: Desislava Stefanova Date: Thu, 31 Mar 2022 15:56:29 +0300 Subject: [PATCH 11/91] test commented --- test/email_password_provider_test.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/email_password_provider_test.dart b/test/email_password_provider_test.dart index 20699cbda..4572488df 100644 --- a/test/email_password_provider_test.dart +++ b/test/email_password_provider_test.dart @@ -34,7 +34,7 @@ Future main([List? args]) async { localAppName: 'bar', localAppVersion: Version(1, 0, 0), ); - final app = Application(appConfig); - await app.emailPasswordProvider.registerUser("foo@bar.com", "pwd"); + //final app = Application(appConfig); + //await app.emailPasswordProvider.registerUser("foo@bar.com", "pwd"); }); } From 44625d462c6e865259b3db566d6ff7d7b960dae8 Mon Sep 17 00:00:00 2001 From: Desislava Stefanova Date: Thu, 31 Mar 2022 16:41:48 +0300 Subject: [PATCH 12/91] CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9b176ff8..5d9c30770 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ x.x.x Release notes (yyyy-MM-dd) ============================================================== **This project is in the Alpha stage. All API's might change without warning and no guarantees are given about stability. Do not use it in production.** +### Features +* Support user registration with email-password authentication provider ([#452](https://github.com/realm/realm-dart/pull/452/)) ### Enhancements * Support result value from write transaction callbacks ([#294](https://github.com/realm/realm-dart/pull/294/)) 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 13/91] 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 14/91] 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 15/91] 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 16/91] 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 17/91] 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 18/91] 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 19/91] 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 20e95af62b7d0760cf4e7d250f8a17e3ca3461f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kasper=20Overg=C3=A5rd=20Nielsen?= Date: Fri, 8 Apr 2022 19:43:27 +0200 Subject: [PATCH 20/91] Added Application class with async logIn method (includes stubs for User) --- lib/src/application.dart | 37 +++++++++++++ lib/src/native/realm_core.dart | 96 +++++++++++++++++++++++++++++----- lib/src/realm_class.dart | 6 ++- lib/src/user.dart | 29 ++++++++++ src/realm_dart.cpp | 1 + 5 files changed, 155 insertions(+), 14 deletions(-) create mode 100644 lib/src/application.dart create mode 100644 lib/src/user.dart diff --git a/lib/src/application.dart b/lib/src/application.dart new file mode 100644 index 000000000..4cbe24858 --- /dev/null +++ b/lib/src/application.dart @@ -0,0 +1,37 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 'application_configuration.dart'; + +import 'native/realm_core.dart'; +import 'credentials.dart'; +import 'user.dart'; + +class Application { + final AppHandle _handle; + final ApplicationConfiguration configuration; + + Application(this.configuration) : _handle = realmCore.getApp(configuration); + + Future logIn(Credentials credentials) async { + return UserInternal.create(await realmCore.logIn(this, credentials)); + } +} + +extension ApplicationInternal on Application { + AppHandle get handle => _handle; +} diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index 4e11ef050..b9f61b510 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -18,9 +18,9 @@ // ignore_for_file: constant_identifier_names, non_constant_identifier_names +import 'dart:async'; import 'dart:convert'; import 'dart:ffi'; -import 'dart:ffi' as ffi show Handle; import 'dart:io'; import 'dart:typed_data'; @@ -28,9 +28,11 @@ import 'dart:typed_data'; import 'package:ffi/ffi.dart' hide StringUtf8Pointer, StringUtf16Pointer; import 'package:pub_semver/pub_semver.dart'; +import '../application.dart'; import '../application_configuration.dart'; import '../collections.dart'; import '../configuration.dart'; +import '../credentials.dart'; import '../init.dart'; import '../list.dart'; import '../realm_class.dart'; @@ -67,11 +69,16 @@ enum _CustomErrorCode { 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; + 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; } } } @@ -309,7 +316,7 @@ class _RealmCore { // Report back to core responseRef.status_code = response.statusCode; - responseRef.body = responseBody.toInt8Ptr(arena); + responseRef.body = responseBody.toUint8Ptr(arena).cast(); responseRef.body_size = responseBody.length; int headerCnt = 0; @@ -320,12 +327,13 @@ class _RealmCore { responseRef.headers = arena(headerCnt); responseRef.num_headers = headerCnt; + int idx = 0; 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); + idx++; } }); @@ -840,12 +848,62 @@ class _RealmCore { } _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); + final version = Version.parse(Platform.version.split(' ')[0]); _realmLib.realm_app_config_set_sdk_version(handle._pointer, version.toString().toUtf8Ptr(arena)); return handle; }); } + + SyncClientConfigHandle createSyncClientConfig(ApplicationConfiguration configuration) { + return using((arena) { + final c = configuration; + final handle = SyncClientConfigHandle._(_realmLib.realm_sync_client_config_new()); + if (c.baseFilePath != null) { + _realmLib.realm_sync_client_config_set_base_file_path(handle._pointer, c.baseFilePath!.path.toUtf8Ptr(arena)); + } + _realmLib.realm_sync_client_config_set_metadata_mode(handle._pointer, c.metadataPersistenceMode.index); + if (c.metadataEncryptionKey != null && c.metadataPersistenceMode == MetadataPersistenceMode.encrypted) { + _realmLib.realm_sync_client_config_set_metadata_encryption_key(handle._pointer, c.metadataEncryptionKey!.toUint8Ptr(arena)); + } + return handle; + }); + } + + AppHandle getApp(ApplicationConfiguration configuration) { + final httpTransport = createHttpTransport(configuration.httpClient); + final appConfig = createAppConfig(configuration, httpTransport); + final syncClientConfig = createSyncClientConfig(configuration); + return AppHandle._(_realmLib.invokeGetPointer(() => _realmLib.realm_app_get(appConfig._pointer, syncClientConfig._pointer))); + } + + static void _logInCallback(Pointer userdata, Pointer user, Pointer error) { + final userHandleCompleter = _realmLib.gc_handle_deref(userdata); + if (userHandleCompleter is Completer) { + if (error == nullptr) { + userHandleCompleter.complete(UserHandle._(_realmLib.realm_clone(user.cast()).cast())); + } else { + final message = error.ref.message.cast().toDartString(); + userHandleCompleter.completeError(RealmException(message)); + } + } + } + + static void _freeCallback(Pointer userdata) { + _realmLib.gc_handle_soften(userdata); + } + + Future logIn(Application application, Credentials credentials) async { + final completer = Completer(); + _realmLib.realm_app_log_in_with_credentials( + application.handle._pointer, + credentials.handle._pointer, + Pointer.fromFunction(_logInCallback), + _realmLib.gc_handle_hard_new(completer), + Pointer.fromFunction(_freeCallback), + ); + return completer.future; + } } class LastError { @@ -950,21 +1008,35 @@ class AppConfigHandle extends Handle { AppConfigHandle._(Pointer pointer) : super(pointer, 256); // TODO: What should hint be? } +class SyncClientConfigHandle extends Handle { + SyncClientConfigHandle._(Pointer pointer) : super(pointer, 256); // TODO: What should hint be? +} + +class AppHandle extends Handle { + AppHandle._( + Pointer pointer, + ) : super(pointer, 256); // TODO: What should hint be? +} + +class UserHandle extends Handle { + UserHandle._(Pointer pointer) : super(pointer, 256); // TODO: What should hint be? +} + extension on List { - Pointer toInt8Ptr(Allocator allocator) { + Pointer toUint8Ptr(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(); + return result; } } extension _StringEx on String { Pointer toUtf8Ptr(Allocator allocator) { final units = utf8.encode(this); - return units.toInt8Ptr(allocator); + return units.toUint8Ptr(allocator).cast(); } Pointer toRealmString(Allocator allocator) { diff --git a/lib/src/realm_class.dart b/lib/src/realm_class.dart index 00a00b5e9..5f5173ee1 100644 --- a/lib/src/realm_class.dart +++ b/lib/src/realm_class.dart @@ -30,15 +30,17 @@ 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 'package:realm_common/realm_common.dart' show Ignored, Indexed, MapTo, PrimaryKey, RealmError, RealmModel, RealmUnsupportedSetError, RealmStateError, RealmCollectionType, RealmPropertyType; + +export 'application.dart' show Application; +export 'application_configuration.dart' show ApplicationConfiguration; export "configuration.dart" show Configuration, RealmSchema, SchemaObject; +export 'credentials.dart' show Credentials, AuthProvider; export 'list.dart' show RealmList, RealmListOfObject, RealmListChanges; export 'realm_object.dart' show RealmEntity, RealmException, RealmObject, RealmObjectChanges; export 'realm_property.dart'; export 'results.dart' show RealmResults, RealmResultsChanges; -export 'credentials.dart' show Credentials, AuthProvider; /// A [Realm] instance represents a `Realm` database. /// diff --git a/lib/src/user.dart b/lib/src/user.dart new file mode 100644 index 000000000..a5ec0647b --- /dev/null +++ b/lib/src/user.dart @@ -0,0 +1,29 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 'native/realm_core.dart'; + +class User { + final UserHandle _handle; + + User._(this._handle); +} + +extension UserInternal on User { + static User create(UserHandle handle) => User._(handle); +} \ No newline at end of file diff --git a/src/realm_dart.cpp b/src/realm_dart.cpp index 6b13c0fc5..653c4cfba 100644 --- a/src/realm_dart.cpp +++ b/src/realm_dart.cpp @@ -72,6 +72,7 @@ void dummy(void) { realm_app_credentials_new_anonymous(); realm_http_transport_new(nullptr, nullptr, nullptr); realm_app_config_new(nullptr, nullptr); + realm_sync_client_config_new(); #if (ANDROID) realm_android_dummy(); #endif From d63f205c9501fc1f55b02731097071ce807ef5c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kasper=20Overg=C3=A5rd=20Nielsen?= Date: Fri, 8 Apr 2022 19:44:11 +0200 Subject: [PATCH 21/91] Add testWithBass function --- test/application_test.dart | 46 ++++++++++++++++++++++++++++++++++++++ test/test.dart | 26 +++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 test/application_test.dart diff --git a/test/application_test.dart b/test/application_test.dart new file mode 100644 index 000000000..4cb879421 --- /dev/null +++ b/test/application_test.dart @@ -0,0 +1,46 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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:async'; +import 'dart:io'; + +import 'package:test/expect.dart'; + +import '../lib/realm.dart'; +import 'test.dart'; + +Future main([List? args]) async { + print("Current PID $pid"); + + await setupTests(args); + + test('Application can be created', () async { + final tmp = await Directory.systemTemp.createTemp(); + final configuration = ApplicationConfiguration( + generateRandomString(10), + baseFilePath: tmp, + ); + final application = Application(configuration); + expect(application.configuration, configuration); + }); + + testWithBaaS('Application log in', (configuration) async { + final application = Application(configuration); + final credentials = Credentials.anonymous(); + final user = await application.logIn(credentials); + }); +} diff --git a/test/test.dart b/test/test.dart index 50ec2e950..4ec16591b 100644 --- a/test/test.dart +++ b/test/test.dart @@ -16,6 +16,7 @@ // //////////////////////////////////////////////////////////////////////////////// +import 'dart:async'; import 'dart:collection'; import 'dart:io'; import 'dart:math'; @@ -207,3 +208,28 @@ Future setupBaas() async { baasApps.addAll(await client.getOrCreateApps()); } + +Future testWithBaaS( + String? name, + FutureOr Function(ApplicationConfiguration configuration) testFunction, { + String appName = 'flexible', + bool skip = false, +}) async { + final url = Uri.tryParse(Platform.environment['BAAS_URL'] ?? 'https://realm-dev.mongodb.com'); + final apiKey = Platform.environment['BAAS_API_KEY']; + final projectId = Platform.environment['BAAS_PROJECT_ID']; + + final missingOrSkip = skip || url == null || apiKey == null || projectId == null; + test(name, () async { + if (!missingOrSkip) { + final app = baasApps[appName] ?? baasApps.values.first; + final temporary = await Directory.systemTemp.createTemp('realm_dart_test_'); + final configuration = ApplicationConfiguration( + app.clientAppId, + baseUrl: url, + baseFilePath: temporary, + ); + return await testFunction(configuration); + } + }, skip: missingOrSkip); +} From b9cc16376ffff472715b513cd8dea2883d985897 Mon Sep 17 00:00:00 2001 From: Desislava Stefanova Date: Tue, 12 Apr 2022 01:51:16 +0300 Subject: [PATCH 22/91] 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 7ff02efe015c44f5d210f6ef13ec98201e0c3d2e Mon Sep 17 00:00:00 2001 From: Desislava Stefanova Date: Tue, 12 Apr 2022 02:17:32 +0300 Subject: [PATCH 23/91] Commit after merge --- ffigen/realm_dart_app.h | 1 - ffigen/realm_dart_http_transport.h | 1 - 2 files changed, 2 deletions(-) delete mode 120000 ffigen/realm_dart_app.h delete mode 120000 ffigen/realm_dart_http_transport.h diff --git a/ffigen/realm_dart_app.h b/ffigen/realm_dart_app.h deleted file mode 120000 index 6f5c0c684..000000000 --- a/ffigen/realm_dart_app.h +++ /dev/null @@ -1 +0,0 @@ -../src/realm_dart_app.h \ No newline at end of file diff --git a/ffigen/realm_dart_http_transport.h b/ffigen/realm_dart_http_transport.h deleted file mode 120000 index 0e55ca055..000000000 --- a/ffigen/realm_dart_http_transport.h +++ /dev/null @@ -1 +0,0 @@ -../src/realm_dart_http_transport.h \ No newline at end of file From fc5e4812ea0ed25da5dc65159057289fd4a37561 Mon Sep 17 00:00:00 2001 From: Desislava Stefanova Date: Tue, 12 Apr 2022 02:59:02 +0300 Subject: [PATCH 24/91] Fixes after merge --- CHANGELOG.md | 3 +- .../tests/test_driver/realm_test.dart | 8 ++- lib/src/application.dart | 2 +- lib/src/native/realm_core.dart | 57 +++++++++---------- test/application_test.dart | 32 +++++++++++ 5 files changed, 67 insertions(+), 35 deletions(-) create mode 100644 test/application_test.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 679228964..0f11fcf3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,8 @@ 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/)) +* Support creating application configuration ([#306](https://github.com/realm/realm-dart/pull/306/)) +* Support creating application ([#446](https://github.com/realm/realm-dart/pull/446/)) ### 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)) diff --git a/flutter/realm_flutter/tests/test_driver/realm_test.dart b/flutter/realm_flutter/tests/test_driver/realm_test.dart index a4e057ee9..9f63c2889 100644 --- a/flutter/realm_flutter/tests/test_driver/realm_test.dart +++ b/flutter/realm_flutter/tests/test_driver/realm_test.dart @@ -11,7 +11,9 @@ import '../test/realm_test.dart' as realm_tests; import '../test/realm_object_test.dart' as realm_object_tests; import '../test/list_test.dart' as list_tests; import '../test/results_test.dart' as results_tests; -import '../test/credentials_test.dart' as credentials; +import '../test/credentials_test.dart' as credentials_tests; +import '../test/application_configuration_test.dart' as application_configuration_tests; +import '../test/application_test.dart' as application_tests; Future main(List args) async { final Completer completer = Completer(); @@ -22,7 +24,9 @@ Future main(List args) async { await realm_object_tests.main(args); await list_tests.main(args); await results_tests.main(args); - await credentials.main(args); + await credentials_tests.main(args); + await application_configuration_tests.main(args); + await application_tests.main(args); tearDown(() { if (Invoker.current?.liveTest.state.result == test_api.Result.error || Invoker.current?.liveTest.state.result == test_api.Result.failure) { diff --git a/lib/src/application.dart b/lib/src/application.dart index 8e49918e8..a5c832cff 100644 --- a/lib/src/application.dart +++ b/lib/src/application.dart @@ -24,7 +24,7 @@ import 'user.dart'; class Application { final RealmAppHandle _handle; - Application(ApplicationConfiguration configuration) : _handle = realmCore.getApp(configuration.handle); + Application(ApplicationConfiguration configuration) : _handle = realmCore.getApp(configuration); Future logIn(Credentials credentials) async { return UserInternal.create(await realmCore.logIn(_handle, credentials.handle)); diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index 87d79052e..f25af714e 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -18,6 +18,7 @@ // ignore_for_file: constant_identifier_names, non_constant_identifier_names +import 'dart:async'; import 'dart:convert'; import 'dart:ffi'; import 'dart:io'; @@ -623,8 +624,8 @@ class _RealmCore { return out_modified.asTypedList(count).toList(); }); } - - AppConfigHandle createAppConfig(ApplicationConfiguration configuration, RealmHttpTransportHandle httpTransport) { + + AppConfigHandle _createAppConfig(ApplicationConfiguration configuration, RealmHttpTransportHandle httpTransport) { return using((arena) { final c = configuration; final app_id = c.appId.toUtf8Ptr(arena); @@ -633,7 +634,7 @@ class _RealmCore { _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); + _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)); @@ -644,8 +645,7 @@ class _RealmCore { } _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)); + _realmLib.realm_app_config_set_sdk_version(handle._pointer, Platform.version.toUtf8Ptr(arena)); return handle; }); @@ -723,7 +723,7 @@ class _RealmCore { // this throws if requestMethod is unknown _HttpMethod final method = _HttpMethod.values[requestMethod]; - + switch (method) { case _HttpMethod.delete: request = await client.deleteUrl(url); @@ -787,30 +787,31 @@ class _RealmCore { } }); } - - RealmAppHandle getApp(RealmAppConfigHandle appConfig) { - return RealmAppHandle._(_realmLib.realm_app_get(appConfig._pointer, nullptr)); // TODO: realm_sync_client_config + + RealmAppHandle getApp(ApplicationConfiguration appConfig) { + final httpHandle = createHttpTransport(appConfig.httpClient); + final appConfigHandle = _createAppConfig(appConfig, httpHandle); + return RealmAppHandle._(_realmLib.realm_app_get(appConfigHandle._pointer, nullptr)); // TODO: realm_sync_client_config } - static void _loginCallback(Object userdata, Pointer user, Pointer error) { - if (userdata is Completer) { - if (error != nullptr) { - final message = error.ref.message.cast().toDartString(); - userdata.completeError(RealmException(message)); - } else { - userdata.complete(RealmUserHandle._(_realmLib.realm_clone(user.cast()).cast())); - } + static void app_log_in_with_credentials_callback(Pointer userdata, Pointer user, Pointer error) { + final Completer? completer = userdata.toObject(); + if (completer == null) { + return; + } + if (error != nullptr) { + final message = error.ref.message.cast().toDartString(); + completer.completeError(RealmException(message)); + } else { + completer.complete(RealmUserHandle._(_realmLib.realm_clone(user.cast()).cast())); } } Future logIn(RealmAppHandle app, RealmAppCredentialsHandle credentials) { final completer = Completer(); - _realmLib.invokeGetBool(() => _realmLib.realm_dart_app_log_in_with_credentials( - app._pointer, - credentials._pointer, - Pointer.fromFunction(_loginCallback), - completer, - )); + final callback = + Pointer.fromFunction, Pointer, Pointer)>(app_log_in_with_credentials_callback); + _realmLib.invokeGetBool(() => _realmLib.realm_app_log_in_with_credentials(app._pointer, credentials._pointer, callback, completer.toGCHandle(), nullptr)); return completer.future; } } @@ -914,7 +915,7 @@ class RealmHttpTransportHandle extends Handle { } class AppConfigHandle extends Handle { - AppConfigHandle._(Pointer pointer) : super(pointer, 256); // TODO: What should hint be? + AppConfigHandle._(Pointer pointer) : super(pointer, 8); } class RealmAppHandle extends Handle { @@ -1123,10 +1124,4 @@ extension on _CustomErrorCode { } } -enum _HttpMethod { - get, - post, - patch, - put, - delete -} \ No newline at end of file +enum _HttpMethod { get, post, patch, put, delete } diff --git a/test/application_test.dart b/test/application_test.dart new file mode 100644 index 000000000..762665518 --- /dev/null +++ b/test/application_test.dart @@ -0,0 +1,32 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 '../lib/realm.dart'; +import 'test.dart'; + +Future main([List? args]) async { + print("Current PID $pid"); + + setupTests(args); + + test('Application can be created', () { + final appConfig = ApplicationConfiguration('a'); + final app = Application(appConfig); + }); +} From e07be9797e394776f95634b865758e84e4ea605b Mon Sep 17 00:00:00 2001 From: Desislava Stefanova Date: Tue, 12 Apr 2022 03:38:50 +0300 Subject: [PATCH 25/91] Fixes after merge --- .../realm_flutter/ios/Classes/RealmPlugin.m | 3 ++ lib/src/native/realm_core.dart | 45 ++++++++++++------- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/flutter/realm_flutter/ios/Classes/RealmPlugin.m b/flutter/realm_flutter/ios/Classes/RealmPlugin.m index 644e7c2f7..0c3f0a419 100644 --- a/flutter/realm_flutter/ios/Classes/RealmPlugin.m +++ b/flutter/realm_flutter/ios/Classes/RealmPlugin.m @@ -42,7 +42,10 @@ void dummy(void) { realm_results_get_object(NULL, 0); realm_list_size(NULL, 0); realm_results_snapshot(NULL); + realm_app_config_new(NULL, NULL); + realm_sync_client_config_new(); realm_app_credentials_new_anonymous(); + realm_http_transport_new(NULL, NULL, NULL); } @end diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index f0288a4f5..f0cdafeca 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -28,7 +28,8 @@ 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 '../credentials.dart'; import '../collections.dart'; import '../init.dart'; import '../list.dart'; @@ -625,7 +626,7 @@ class _RealmCore { }); } - AppConfigHandle _createAppConfig(ApplicationConfiguration configuration, RealmHttpTransportHandle httpTransport) { + AppConfigHandle createAppConfig(ApplicationConfiguration configuration, RealmHttpTransportHandle httpTransport) { return using((arena) { final c = configuration; final app_id = c.appId.toUtf8Ptr(arena); @@ -755,7 +756,7 @@ class _RealmCore { // Report back to core responseRef.status_code = response.statusCode; - responseRef.body = responseBody.toUint8Ptr(arena); + responseRef.body = responseBody.toInt8Ptr(arena); responseRef.body_size = responseBody.length; int headerCnt = 0; @@ -787,14 +788,14 @@ class _RealmCore { _realmLib.realm_http_transport_complete_request(request_context, response_pointer); } }); - } + } SyncClientConfigHandle createSyncClientConfig(ApplicationConfiguration configuration) { return using((arena) { final c = configuration; final handle = SyncClientConfigHandle._(_realmLib.realm_sync_client_config_new()); if (c.baseFilePath != null) { - _realmLib.realm_sync_client_config_set_base_file_path(handle._pointer, c.baseFilePath!.path.toUtf8Ptr(arena)); + _realmLib.realm_sync_client_config_set_base_file_path(handle._pointer, c.baseFilePath.path.toUtf8Ptr(arena)); } _realmLib.realm_sync_client_config_set_metadata_mode(handle._pointer, c.metadataPersistenceMode.index); if (c.metadataEncryptionKey != null && c.metadataPersistenceMode == MetadataPersistenceMode.encrypted) { @@ -812,20 +813,21 @@ class _RealmCore { } static void _logInCallback(Pointer userdata, Pointer user, Pointer error) { - final userHandleCompleter = _realmLib.gc_handle_deref(userdata); - if (userHandleCompleter is Completer) { - if (error == nullptr) { - userHandleCompleter.complete(UserHandle._(_realmLib.realm_clone(user.cast()).cast())); - } else { - final message = error.ref.message.cast().toDartString(); - userHandleCompleter.completeError(RealmException(message)); - } + final Completer? userHandleCompleter = userdata.toObject(); + if (userHandleCompleter == null) { + return; + } + if (error == nullptr) { + userHandleCompleter.complete(UserHandle._(_realmLib.realm_clone(user.cast()).cast())); + } else { + final message = error.ref.message.cast().toDartString(); + userHandleCompleter.completeError(RealmException(message)); } } static void _freeCallback(Pointer userdata) { - _realmLib.gc_handle_soften(userdata); - } + userdata.toObject(); // TODO: release + } Future logIn(Application application, Credentials credentials) async { final completer = Completer(); @@ -833,7 +835,7 @@ class _RealmCore { application.handle._pointer, credentials.handle._pointer, Pointer.fromFunction(_logInCallback), - _realmLib.gc_handle_hard_new(completer), + completer.toGCHandle(), Pointer.fromFunction(_freeCallback), ); return completer.future; @@ -965,12 +967,21 @@ extension on List { native.last = 0; // zero terminate return result; } + + Pointer toInt8Ptr(Allocator allocator) { + final nativeSize = length + 1; + final result = allocator(nativeSize); + final Int8List native = result.asTypedList(nativeSize); + native.setAll(0, this); // copy + native.last = 0; // zero terminate + return result; + } } extension _StringEx on String { Pointer toUtf8Ptr(Allocator allocator) { final units = utf8.encode(this); - return units.toUint8Ptr(allocator).cast(); + return units.toInt8Ptr(allocator).cast(); } Pointer toRealmString(Allocator allocator) { From b7ddc962471dc953dd35fde69ba608678e9b6083 Mon Sep 17 00:00:00 2001 From: Desislava Stefanova Date: Tue, 12 Apr 2022 04:06:06 +0300 Subject: [PATCH 26/91] Remove user and login --- lib/src/application.dart | 5 ----- lib/src/native/realm_core.dart | 35 +--------------------------------- lib/src/realm_class.dart | 1 - lib/src/user.dart | 29 ---------------------------- test/application_test.dart | 5 ----- test/test.dart | 24 ----------------------- 6 files changed, 1 insertion(+), 98 deletions(-) delete mode 100644 lib/src/user.dart diff --git a/lib/src/application.dart b/lib/src/application.dart index 4cbe24858..cfc83549b 100644 --- a/lib/src/application.dart +++ b/lib/src/application.dart @@ -18,8 +18,6 @@ import 'application_configuration.dart'; import 'native/realm_core.dart'; -import 'credentials.dart'; -import 'user.dart'; class Application { final AppHandle _handle; @@ -27,9 +25,6 @@ class Application { Application(this.configuration) : _handle = realmCore.getApp(configuration); - Future logIn(Credentials credentials) async { - return UserInternal.create(await realmCore.logIn(this, credentials)); - } } extension ApplicationInternal on Application { diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index f0cdafeca..b4eed7802 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -810,36 +810,7 @@ class _RealmCore { final appConfig = createAppConfig(configuration, httpTransport); final syncClientConfig = createSyncClientConfig(configuration); return AppHandle._(_realmLib.invokeGetPointer(() => _realmLib.realm_app_get(appConfig._pointer, syncClientConfig._pointer))); - } - - static void _logInCallback(Pointer userdata, Pointer user, Pointer error) { - final Completer? userHandleCompleter = userdata.toObject(); - if (userHandleCompleter == null) { - return; - } - if (error == nullptr) { - userHandleCompleter.complete(UserHandle._(_realmLib.realm_clone(user.cast()).cast())); - } else { - final message = error.ref.message.cast().toDartString(); - userHandleCompleter.completeError(RealmException(message)); - } - } - - static void _freeCallback(Pointer userdata) { - userdata.toObject(); // TODO: release - } - - Future logIn(Application application, Credentials credentials) async { - final completer = Completer(); - _realmLib.realm_app_log_in_with_credentials( - application.handle._pointer, - credentials.handle._pointer, - Pointer.fromFunction(_logInCallback), - completer.toGCHandle(), - Pointer.fromFunction(_freeCallback), - ); - return completer.future; - } + } } class LastError { @@ -954,10 +925,6 @@ class AppHandle extends Handle { ) : super(pointer, 256); // TODO: What should hint be? } -class UserHandle extends Handle { - UserHandle._(Pointer pointer) : super(pointer, 256); // TODO: What should hint be? -} - extension on List { Pointer toUint8Ptr(Allocator allocator) { final nativeSize = length + 1; diff --git a/lib/src/realm_class.dart b/lib/src/realm_class.dart index 5e6dd484b..71b990c5b 100644 --- a/lib/src/realm_class.dart +++ b/lib/src/realm_class.dart @@ -41,7 +41,6 @@ export 'realm_object.dart' show RealmEntity, RealmException, RealmObject, RealmO export 'realm_property.dart'; export 'results.dart' show RealmResults, RealmResultsChanges; export 'credentials.dart' show Credentials, AuthProvider; -export 'user.dart' show User; /// A [Realm] instance represents a `Realm` database. /// diff --git a/lib/src/user.dart b/lib/src/user.dart deleted file mode 100644 index a5ec0647b..000000000 --- a/lib/src/user.dart +++ /dev/null @@ -1,29 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// 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 'native/realm_core.dart'; - -class User { - final UserHandle _handle; - - User._(this._handle); -} - -extension UserInternal on User { - static User create(UserHandle handle) => User._(handle); -} \ No newline at end of file diff --git a/test/application_test.dart b/test/application_test.dart index 4cb879421..946afdfeb 100644 --- a/test/application_test.dart +++ b/test/application_test.dart @@ -38,9 +38,4 @@ Future main([List? args]) async { expect(application.configuration, configuration); }); - testWithBaaS('Application log in', (configuration) async { - final application = Application(configuration); - final credentials = Credentials.anonymous(); - final user = await application.logIn(credentials); - }); } diff --git a/test/test.dart b/test/test.dart index 099a620f9..039109f78 100644 --- a/test/test.dart +++ b/test/test.dart @@ -197,27 +197,3 @@ Future setupBaas() async { baasApps.addAll(await client.getOrCreateApps()); } -Future testWithBaaS( - String? name, - FutureOr Function(ApplicationConfiguration configuration) testFunction, { - String appName = 'flexible', - bool skip = false, -}) async { - final url = Uri.tryParse(Platform.environment['BAAS_URL'] ?? 'https://realm-dev.mongodb.com'); - final apiKey = Platform.environment['BAAS_API_KEY']; - final projectId = Platform.environment['BAAS_PROJECT_ID']; - - final missingOrSkip = skip || url == null || apiKey == null || projectId == null; - test(name, () async { - if (!missingOrSkip) { - final app = baasApps[appName] ?? baasApps.values.first; - final temporary = await Directory.systemTemp.createTemp('realm_dart_test_'); - final configuration = ApplicationConfiguration( - app.clientAppId, - baseUrl: url, - baseFilePath: temporary, - ); - return await testFunction(configuration); - } - }, skip: missingOrSkip); -} From 938a16883c2d93c1bf91d8e38b45fec2921930b9 Mon Sep 17 00:00:00 2001 From: Desislava Stefanova Date: Tue, 12 Apr 2022 04:15:31 +0300 Subject: [PATCH 27/91] size of handles --- lib/src/native/realm_core.dart | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index b4eed7802..fe97ee3ce 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -810,7 +810,7 @@ class _RealmCore { final appConfig = createAppConfig(configuration, httpTransport); final syncClientConfig = createSyncClientConfig(configuration); return AppHandle._(_realmLib.invokeGetPointer(() => _realmLib.realm_app_get(appConfig._pointer, syncClientConfig._pointer))); - } + } } class LastError { @@ -916,13 +916,11 @@ class AppConfigHandle extends Handle { } class SyncClientConfigHandle extends Handle { - SyncClientConfigHandle._(Pointer pointer) : super(pointer, 256); // TODO: What should hint be? + SyncClientConfigHandle._(Pointer pointer) : super(pointer, 8); } class AppHandle extends Handle { - AppHandle._( - Pointer pointer, - ) : super(pointer, 256); // TODO: What should hint be? + AppHandle._(Pointer pointer) : super(pointer, 16); } extension on List { From d447fe0d96aaf0f9edd6eeed0c629d68ecbbf132 Mon Sep 17 00:00:00 2001 From: Desislava Stefanova Date: Tue, 12 Apr 2022 04:20:04 +0300 Subject: [PATCH 28/91] Set size of handles --- lib/src/native/realm_core.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index f0cdafeca..7a8070f0e 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -945,13 +945,12 @@ class AppConfigHandle extends Handle { } class SyncClientConfigHandle extends Handle { - SyncClientConfigHandle._(Pointer pointer) : super(pointer, 256); // TODO: What should hint be? + SyncClientConfigHandle._(Pointer pointer) : super(pointer, 8); } class AppHandle extends Handle { AppHandle._( - Pointer pointer, - ) : super(pointer, 256); // TODO: What should hint be? + Pointer pointer) : super(pointer, 16); } class UserHandle extends Handle { From ef644c5c480976462b0e7584c57b1e41961e7bea Mon Sep 17 00:00:00 2001 From: Desislava Stefanova Date: Tue, 12 Apr 2022 04:23:36 +0300 Subject: [PATCH 29/91] workflow chanel = stable --- .github/workflows/pr.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 3c4981ea5..0d371afbd 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -36,7 +36,7 @@ jobs: - name : Setup Dart SDK uses: dart-lang/setup-dart@main with: - sdk: beta + sdk: stable - name: Deploy Apps run: | dart run realm_dart deploy-apps \ @@ -88,7 +88,7 @@ jobs: - name : Setup Dart SDK uses: dart-lang/setup-dart@main with: - sdk: beta + sdk: stable - name: Install dependencies run: dart pub get - name: Run tests @@ -138,7 +138,7 @@ jobs: - name: Setup Flutter uses: subosito/flutter-action@v2 with: - channel: beta + channel: 'stable' - name: Enable Flutter Desktop support run: flutter config --enable-linux-desktop - name: Install dependencies From a132bf454f5ecd04fb7e580807a4abc1a6cd5bf5 Mon Sep 17 00:00:00 2001 From: Desislava Stefanova Date: Tue, 12 Apr 2022 04:27:06 +0300 Subject: [PATCH 30/91] Delete realm_dart_http_transport --- src/realm_dart_http_transport.cpp | 85 ------------------------------- src/realm_dart_http_transport.h | 46 ----------------- 2 files changed, 131 deletions(-) delete mode 100644 src/realm_dart_http_transport.cpp delete mode 100644 src/realm_dart_http_transport.h diff --git a/src/realm_dart_http_transport.cpp b/src/realm_dart_http_transport.cpp deleted file mode 100644 index 9d2c4b9fc..000000000 --- a/src/realm_dart_http_transport.cpp +++ /dev/null @@ -1,85 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// 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. -// -//////////////////////////////////////////////////////////////////////////////// - -#include "realm_dart_http_transport.h" -#include "dart_api_dl.h" - -struct HandleScope { - HandleScope() { - Dart_EnterScope_DL(); - } - - ~HandleScope() { - Dart_ExitScope_DL(); - } -}; - -class RequestCallbackData { - //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) { - Dart_DeleteWeakPersistentHandle_DL(m_handle); - m_handle = nullptr; - } - } - -public: - RequestCallbackData(Dart_Handle handle, realm_dart_http_request_func_t callback) - : m_handle(Dart_NewWeakPersistentHandle_DL(handle, nullptr, 1, finalize_handle)), m_callback(callback) - {} - - ~RequestCallbackData() { - delete_handle(); - } - - void callback(const realm_http_request_t request, void* request_context) { - if (m_handle) { - HandleScope scope; - auto handle = Dart_HandleFromWeakPersistent_DL(m_handle); - m_callback(handle, request, request_context); - } - } - -private: - Dart_WeakPersistentHandle m_handle; - realm_dart_http_request_func_t m_callback; -}; - -void free_request_callback_data(void* userdata) { - auto request_callback_data = static_cast(userdata); - delete request_callback_data; -} - -void on_request_callback( - void* userdata, - const realm_http_request_t request, - void* request_context) -{ - auto request_callback_data = static_cast(userdata); - request_callback_data->callback(request, request_context); -} - -RLM_API realm_http_transport_t* realm_dart_http_transport_new( - realm_dart_http_request_func_t request_callback, - Dart_Handle userdata) -{ - auto request_callback_data = new RequestCallbackData(userdata, request_callback); - return realm_http_transport_new(on_request_callback, request_callback_data, free_request_callback_data); -} diff --git a/src/realm_dart_http_transport.h b/src/realm_dart_http_transport.h deleted file mode 100644 index cf247c709..000000000 --- a/src/realm_dart_http_transport.h +++ /dev/null @@ -1,46 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// 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. -// -//////////////////////////////////////////////////////////////////////////////// - -#ifndef REALM_DART_HTTP_TRANSPORT_H -#define REALM_DART_HTTP_TRANSPORT_H - -#include "realm.h" -#include "dart_api_dl.h" - -/** - * Callback function used by Core to make a HTTP request. - * - * Complete the request by calling realm_dart_http_transport_complete_request(), - * passing in the request_context pointer here and the received response. - * Network request are expected to be asynchronous and can be completed on any thread. - * - * @param userdata The userdata pointer passed to realm_dart_http_transport_new(). - * @param request The request to send. - * @param request_context Internal state pointer of Core, needed by realm_http_transport_complete_request(). - */ -typedef void (*realm_dart_http_request_func_t)(Dart_Handle userdata, const realm_http_request_t request, void* request_context); - -/** - * Create a new HTTP transport with these callbacks implementing its functionality. - * - * This is a dart specific wrapper for realm_http_transport_new. - */ -RLM_API realm_http_transport_t* realm_dart_http_transport_new(realm_dart_http_request_func_t request_callback, - Dart_Handle userdata); - -#endif \ No newline at end of file From 4e15ea7ae4c7ca085877deef0604ddecf043f99e Mon Sep 17 00:00:00 2001 From: Desislava Stefanova Date: Tue, 12 Apr 2022 04:29:51 +0300 Subject: [PATCH 31/91] remove import dart:async from tests --- test/application_test.dart | 1 - test/test.dart | 1 - 2 files changed, 2 deletions(-) diff --git a/test/application_test.dart b/test/application_test.dart index 946afdfeb..c25227293 100644 --- a/test/application_test.dart +++ b/test/application_test.dart @@ -15,7 +15,6 @@ // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// -import 'dart:async'; import 'dart:io'; import 'package:test/expect.dart'; diff --git a/test/test.dart b/test/test.dart index 039109f78..c52532a29 100644 --- a/test/test.dart +++ b/test/test.dart @@ -16,7 +16,6 @@ // //////////////////////////////////////////////////////////////////////////////// -import 'dart:async'; import 'dart:collection'; import 'dart:io'; import 'dart:math'; From 348ed3ef573fdeaef8df23ec75e33df401bfd294 Mon Sep 17 00:00:00 2001 From: Desislava Stefanova Date: Tue, 12 Apr 2022 10:32:34 +0300 Subject: [PATCH 32/91] Fixes after merge --- lib/src/application.dart | 5 ++- lib/src/native/realm_core.dart | 5 +-- test/application_configuration_test.dart | 52 ------------------------ test/application_test.dart | 9 ++-- 4 files changed, 8 insertions(+), 63 deletions(-) delete mode 100644 test/application_configuration_test.dart diff --git a/lib/src/application.dart b/lib/src/application.dart index 4733abf0c..1c3973ed4 100644 --- a/lib/src/application.dart +++ b/lib/src/application.dart @@ -105,8 +105,9 @@ class Application { final ApplicationConfiguration configuration; Application(this.configuration) : _handle = realmCore.getApp(configuration); - } extension ApplicationInternal on Application { - AppHandle get handle => _handle; \ No newline at end of file + AppHandle get handle => _handle; +} + diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index 601e606c5..285d23843 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -655,7 +655,7 @@ class _RealmCore { final handle = AppConfigHandle._(_realmLib.realm_app_config_new(app_id, httpTransport._pointer)); _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); + _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)); @@ -817,7 +817,7 @@ class _RealmCore { return using((arena) { final c = configuration; final handle = SyncClientConfigHandle._(_realmLib.realm_sync_client_config_new()); - if (c.baseFilePath != null) { + if (c.baseFilePath.path.isNotEmpty) { _realmLib.realm_sync_client_config_set_base_file_path(handle._pointer, c.baseFilePath.path.toUtf8Ptr(arena)); } _realmLib.realm_sync_client_config_set_metadata_mode(handle._pointer, c.metadataPersistenceMode.index); @@ -835,7 +835,6 @@ class _RealmCore { return AppHandle._(_realmLib.invokeGetPointer(() => _realmLib.realm_app_get(appConfig._pointer, syncClientConfig._pointer))); } } -} class LastError { final int code; diff --git a/test/application_configuration_test.dart b/test/application_configuration_test.dart deleted file mode 100644 index ba8314574..000000000 --- a/test/application_configuration_test.dart +++ /dev/null @@ -1,52 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// 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 'package:test/expect.dart'; - -import '../lib/realm.dart'; -import 'test.dart'; - -Future main([List? args]) async { - print("Current PID $pid"); - - setupTests(args); - - test('ApplicationConfiguration can be created', () { - 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)); - }); -} diff --git a/test/application_test.dart b/test/application_test.dart index d4c23b635..aa36b839a 100644 --- a/test/application_test.dart +++ b/test/application_test.dart @@ -47,17 +47,14 @@ Future main([List? args]) async { ); expect(b.appId, 'myapp1'); expect(b.baseFilePath.path, Directory.systemTemp.path); - expect(b.baseUrl,Uri.parse('https://not_re.al')); + expect(b.baseUrl, Uri.parse('https://not_re.al')); expect(b.defaultRequestTimeout, const Duration(seconds: 2)); expect(b.httpClient, httpClient); }); - + test('Application can be created', () async { final tmp = await Directory.systemTemp.createTemp(); - final configuration = ApplicationConfiguration( - generateRandomString(10), - baseFilePath: tmp, - ); + final configuration = ApplicationConfiguration(generateRandomString(10), baseFilePath: tmp); final application = Application(configuration); expect(application.configuration, configuration); }); From 2778ff255b970a274d5cae1d20d42f92921c276d Mon Sep 17 00:00:00 2001 From: Desislava Stefanova Date: Tue, 12 Apr 2022 10:43:14 +0300 Subject: [PATCH 33/91] Fix after merge --- flutter/realm_flutter/tests/test_driver/realm_test.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/flutter/realm_flutter/tests/test_driver/realm_test.dart b/flutter/realm_flutter/tests/test_driver/realm_test.dart index 9f63c2889..80ac1f1e4 100644 --- a/flutter/realm_flutter/tests/test_driver/realm_test.dart +++ b/flutter/realm_flutter/tests/test_driver/realm_test.dart @@ -12,7 +12,6 @@ import '../test/realm_object_test.dart' as realm_object_tests; import '../test/list_test.dart' as list_tests; import '../test/results_test.dart' as results_tests; import '../test/credentials_test.dart' as credentials_tests; -import '../test/application_configuration_test.dart' as application_configuration_tests; import '../test/application_test.dart' as application_tests; Future main(List args) async { @@ -25,7 +24,6 @@ Future main(List args) async { await list_tests.main(args); await results_tests.main(args); await credentials_tests.main(args); - await application_configuration_tests.main(args); await application_tests.main(args); tearDown(() { From de08be05437a4b5b56866fce3a4332a8e1d8a696 Mon Sep 17 00:00:00 2001 From: Desislava Stefanova Date: Tue, 12 Apr 2022 11:34:08 +0300 Subject: [PATCH 34/91] Fix after merge --- lib/src/email_password_provider.dart | 2 +- lib/src/native/realm_core.dart | 47 +++++++++++--------------- test/email_password_provider_test.dart | 16 +++------ 3 files changed, 26 insertions(+), 39 deletions(-) diff --git a/lib/src/email_password_provider.dart b/lib/src/email_password_provider.dart index f0ca279c4..20e037351 100644 --- a/lib/src/email_password_provider.dart +++ b/lib/src/email_password_provider.dart @@ -22,7 +22,7 @@ import 'native/realm_core.dart'; /// It is always scoped to a particular app and can only be accessed via [emailPasswordProvider]. /// {@category Application} class EmailPasswordProvider { - final RealmAppHandle _handle; + final AppHandle _handle; EmailPasswordProvider(Application app) : _handle = app.handle; diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index 19e032e6f..1c1206974 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -648,15 +648,15 @@ class _RealmCore { return out_modified.asTypedList(count).toList(); }); } - + AppConfigHandle createAppConfig(ApplicationConfiguration configuration, RealmHttpTransportHandle httpTransport) { return using((arena) { final app_id = configuration.appId.toUtf8Ptr(arena); final handle = AppConfigHandle._(_realmLib.realm_app_config_new(app_id, httpTransport._pointer)); - + _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)); } @@ -667,7 +667,7 @@ class _RealmCore { _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)); - + //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)); @@ -812,7 +812,7 @@ class _RealmCore { } }); } - + SyncClientConfigHandle createSyncClientConfig(ApplicationConfiguration configuration) { return using((arena) { final c = configuration; @@ -835,23 +835,24 @@ class _RealmCore { return AppHandle._(_realmLib.invokeGetPointer(() => _realmLib.realm_app_get(appConfig._pointer, syncClientConfig._pointer))); } - static void _appEmailPasswordProviderCallback(Pointer completerPtr, Pointer error) { - final completer = _realmLib.gc_handle_fromPtr(completerPtr); - if (completer is Completer) { - if (error != nullptr) { - final message = error.ref.message.cast().toDartString(); - completer.completeError(RealmException(message)); - } else { - completer.complete(); - } + static void app_email_password_provider_callback(Pointer userdata, Pointer error) { + final Completer? completer = userdata.toObject(); + if (completer == null) { + return; + } + if (error != nullptr) { + final message = error.ref.message.cast().toDartString(); + completer.completeError(RealmException(message)); + } else { + completer.complete(); } } - Future appEmailPasswordRegisterUser(RealmAppHandle app, String email, String password) { + Future appEmailPasswordRegisterUser(AppHandle application, String email, String password) { final completer = Completer(); using((arena) { - _realmLib.invokeGetBool(() => _realmLib.realm_app_email_password_provider_client_register_email(app._pointer, email.toUtf8Ptr(arena), - password.toRealmString(arena).ref, Pointer.fromFunction(_appEmailPasswordProviderCallback), _realmLib.gc_handle_toPtr(completer), nullptr)); + _realmLib.invokeGetBool(() => _realmLib.realm_app_email_password_provider_client_register_email(application._pointer, email.toUtf8Ptr(arena), + password.toRealmString(arena).ref, Pointer.fromFunction(app_email_password_provider_callback), completer.toGCHandle(), nullptr)); }); return completer.future; } @@ -972,7 +973,7 @@ class AppHandle extends Handle { } extension on List { - Pointer toUint8Ptr(Allocator allocator) { + Pointer toUint8Ptr(Allocator allocator) { final nativeSize = length + 1; final result = allocator(nativeSize); final Uint8List native = result.asTypedList(nativeSize); @@ -980,7 +981,7 @@ extension on List { native.last = 0; // zero terminate return result; } - + Pointer toInt8Ptr(Allocator allocator) { final nativeSize = length + 1; final result = allocator(nativeSize); @@ -1004,14 +1005,6 @@ extension _StringEx on String { realm_string.ref.size = units.length + 1; return realm_string; } - - Pointer toRealmString(Allocator allocator) { - final realm_string = allocator(); - realm_string.ref.data = toUtf8Ptr(allocator); - final units = utf8.encode(this); - realm_string.ref.size = units.length + 1; - return realm_string; - } } extension _RealmLibraryEx on RealmLibrary { diff --git a/test/email_password_provider_test.dart b/test/email_password_provider_test.dart index 4572488df..000fdb411 100644 --- a/test/email_password_provider_test.dart +++ b/test/email_password_provider_test.dart @@ -17,24 +17,18 @@ //////////////////////////////////////////////////////////////////////////////// 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); + await setupTests(args); test('Email/Password - register user', () async { - final appConfig = ApplicationConfiguration( - 'foo', - baseUrl: Uri.parse('https://not_re.al'), - defaultRequestTimeout: const Duration(seconds: 2), - localAppName: 'bar', - localAppVersion: Version(1, 0, 0), - ); - //final app = Application(appConfig); - //await app.emailPasswordProvider.registerUser("foo@bar.com", "pwd"); + final tmp = await Directory.systemTemp.createTemp(); + final configuration = ApplicationConfiguration(generateRandomString(10), baseFilePath: tmp); + final application = Application(configuration); + await application.emailPasswordProvider.registerUser("foo@bar.com", "pwd"); }); } From 6f28a2247dce644e665965e6561afc1b3ec35dea Mon Sep 17 00:00:00 2001 From: Desislava Stefanova Date: Tue, 12 Apr 2022 15:36:10 +0300 Subject: [PATCH 35/91] Fix after merge --- lib/src/native/realm_core.dart.orig | 1183 --------------------------- 1 file changed, 1183 deletions(-) delete mode 100644 lib/src/native/realm_core.dart.orig diff --git a/lib/src/native/realm_core.dart.orig b/lib/src/native/realm_core.dart.orig deleted file mode 100644 index 1243320ce..000000000 --- a/lib/src/native/realm_core.dart.orig +++ /dev/null @@ -1,1183 +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. -// -//////////////////////////////////////////////////////////////////////////////// - -// ignore_for_file: constant_identifier_names, non_constant_identifier_names - -import 'dart:async'; -import 'dart:convert'; -import 'dart:ffi'; -import 'dart:io'; -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 '../application.dart'; -import '../credentials.dart'; -import '../collections.dart'; -import '../init.dart'; -import '../list.dart'; -import '../realm_class.dart'; -import '../realm_object.dart'; -import '../results.dart'; -import 'realm_bindings.dart'; - -late RealmLibrary _realmLib; - -final _RealmCore realmCore = _RealmCore(); - -class _RealmCore { - // From realm.h. Currently not exported from the shared library - static const int RLM_INVALID_CLASS_KEY = 0x7FFFFFFF; - // ignore: unused_field - static const int RLM_INVALID_PROPERTY_KEY = -1; - // ignore: unused_field - static const int RLM_INVALID_OBJECT_KEY = -1; - - static const int TRUE = 1; - static const int FALSE = 0; - - // Hide the RealmCore class and make it a singleton - static _RealmCore? _instance; - late final int isolateKey; - - _RealmCore._() { - final lib = initRealm(); - _realmLib = RealmLibrary(lib); - } - - factory _RealmCore() { - return _instance ??= _RealmCore._(); - } - - String get libraryVersion => _realmLib.realm_get_library_version().cast().toDartString(); - - LastError? getLastError(Allocator allocator) { - final error = allocator(); - final success = _realmLib.realm_get_last_error(error); - if (!success) { - return null; - } - - String? message; - if (error.ref.message != nullptr) { - message = error.ref.message.cast().toDartString(); - } - - return LastError(error.ref.error, message); - } - - void throwLastError([String? errorMessage]) { - using((Arena arena) { - final lastError = getLastError(arena); - throw RealmException('${errorMessage != null ? errorMessage + ". " : ""}${lastError ?? ""}'); - }); - } - - SchemaHandle _createSchema(Iterable schema) { - return using((Arena arena) { - final classCount = schema.length; - - final schemaClasses = arena(classCount); - final schemaProperties = arena>(classCount); - - for (var i = 0; i < classCount; i++) { - final schemaObject = schema.elementAt(i); - final classInfo = schemaClasses.elementAt(i).ref; - - classInfo.name = schemaObject.name.toUtf8Ptr(arena); - classInfo.primary_key = "".toUtf8Ptr(arena); - classInfo.num_properties = schemaObject.properties.length; - classInfo.num_computed_properties = 0; - classInfo.key = RLM_INVALID_CLASS_KEY; - classInfo.flags = realm_class_flags.RLM_CLASS_NORMAL; - - final propertiesCount = schemaObject.properties.length; - final properties = arena(propertiesCount); - - for (var j = 0; j < propertiesCount; j++) { - final schemaProperty = schemaObject.properties[j]; - final propInfo = properties.elementAt(j).ref; - propInfo.name = schemaProperty.name.toUtf8Ptr(arena); - //TODO: assign the correct public name value. - propInfo.public_name = "".toUtf8Ptr(arena); - propInfo.link_target = (schemaProperty.linkTarget ?? "").toUtf8Ptr(arena); - propInfo.link_origin_property_name = "".toUtf8Ptr(arena); - propInfo.type = schemaProperty.propertyType.index; - propInfo.collection_type = schemaProperty.collectionType.index; - propInfo.flags = realm_property_flags.RLM_PROPERTY_NORMAL; - - if (schemaProperty.optional) { - propInfo.flags |= realm_property_flags.RLM_PROPERTY_NULLABLE; - } - - if (schemaProperty.primaryKey) { - classInfo.primary_key = schemaProperty.name.toUtf8Ptr(arena); - propInfo.flags = realm_property_flags.RLM_PROPERTY_PRIMARY_KEY; - } - } - - schemaProperties[i] = properties; - schemaProperties.elementAt(i).value = properties; - } - - final schemaPtr = _realmLib.invokeGetPointer(() => _realmLib.realm_schema_new(schemaClasses, classCount, schemaProperties)); - return SchemaHandle._(schemaPtr); - }); - } - - ConfigHandle _createConfig(Configuration config, SchedulerHandle schedulerHandle) { - return using((Arena arena) { - final schemaHandle = _createSchema(config.schema); - final configPtr = _realmLib.realm_config_new(); - final configHandle = ConfigHandle._(configPtr); - _realmLib.realm_config_set_schema(configHandle._pointer, schemaHandle._pointer); - _realmLib.realm_config_set_schema_version(configHandle._pointer, config.schemaVersion); - _realmLib.realm_config_set_path(configHandle._pointer, config.path.toUtf8Ptr(arena)); - _realmLib.realm_config_set_scheduler(configHandle._pointer, schedulerHandle._pointer); - if (config.isReadOnly) { - _realmLib.realm_config_set_schema_mode(configHandle._pointer, realm_schema_mode.RLM_SCHEMA_MODE_IMMUTABLE); - } - if (config.isInMemory) { - _realmLib.realm_config_set_in_memory(configHandle._pointer, config.isInMemory); - } - if (config.fifoFilesFallbackPath != null) { - _realmLib.realm_config_set_fifo_path(configHandle._pointer, config.fifoFilesFallbackPath!.toUtf8Ptr(arena)); - } - if (config.disableFormatUpgrade) { - _realmLib.realm_config_set_disable_format_upgrade(configHandle._pointer, config.disableFormatUpgrade); - } - - if (config.initialDataCallback != null) { - _realmLib.realm_config_set_data_initialization_function(configHandle._pointer, Pointer.fromFunction(initial_data_callback, FALSE), config.toGCHandle()); - } - - return configHandle; - }); - } - - static int initial_data_callback(Pointer userdata, Pointer realmHandle) { - try { - final Configuration? config = userdata.toObject(); - if (config == null) { - return FALSE; - } - final realm = RealmInternal.getUnowned(config, RealmHandle._unowned(realmHandle)); - config.initialDataCallback!(realm); - return TRUE; - } catch (ex) { - // TODO: this should propagate the error to Core: https://github.com/realm/realm-core/issues/5366 - } - - return FALSE; - } - - SchedulerHandle createScheduler(int isolateId, int sendPort) { - final schedulerPtr = _realmLib.realm_dart_create_scheduler(isolateId, sendPort); - return SchedulerHandle._(schedulerPtr); - } - - void invokeScheduler(SchedulerHandle schedulerHandle) { - _realmLib.realm_scheduler_perform_work(schedulerHandle._pointer); - } - - RealmHandle openRealm(Configuration config, Scheduler scheduler) { - final configHandle = _createConfig(config, scheduler.handle); - final realmPtr = _realmLib.invokeGetPointer(() => _realmLib.realm_open(configHandle._pointer), "Error opening realm at path ${config.path}"); - return RealmHandle._(realmPtr); - } - - void deleteRealmFiles(String path) { - using((Arena arena) { - final realm_deleted = arena(); - _realmLib.invokeGetBool(() => _realmLib.realm_delete_files(path.toUtf8Ptr(arena), realm_deleted), "Error deleting realm at path $path"); - }); - } - - String getFilesPath() { - return _realmLib.realm_dart_get_files_path().cast().toDartString(); - } - - void closeRealm(Realm realm) { - _realmLib.invokeGetBool(() => _realmLib.realm_close(realm.handle._pointer), "Realm close failed"); - } - - bool isRealmClosed(Realm realm) { - return _realmLib.realm_is_closed(realm.handle._pointer); - } - - void beginWrite(Realm realm) { - _realmLib.invokeGetBool(() => _realmLib.realm_begin_write(realm.handle._pointer), "Could not begin write"); - } - - void commitWrite(Realm realm) { - _realmLib.invokeGetBool(() => _realmLib.realm_commit(realm.handle._pointer), "Could not commit write"); - } - - bool getIsWritable(Realm realm) { - return _realmLib.realm_is_writable(realm.handle._pointer); - } - - void rollbackWrite(Realm realm) { - _realmLib.invokeGetBool(() => _realmLib.realm_rollback(realm.handle._pointer), "Could not rollback write"); - } - - void realmRefresh(Realm realm) { - _realmLib.invokeGetBool(() => _realmLib.realm_refresh(realm.handle._pointer), "Could not refresh"); - } - - RealmClassMetadata getClassMetadata(Realm realm, String className, Type classType) { - return using((Arena arena) { - final found = arena(); - final classInfo = arena(); - _realmLib.invokeGetBool(() => _realmLib.realm_find_class(realm.handle._pointer, className.toUtf8Ptr(arena), found, classInfo), - "Error getting class $className from realm at ${realm.config.path}"); - - if (found.value == 0) { - throwLastError("Class $className not found in ${realm.config.path}"); - } - - String? primaryKey; - if (classInfo.ref.primary_key != nullptr) { - primaryKey = classInfo.ref.primary_key.cast().toDartString(); - if (primaryKey.isEmpty) { - primaryKey = null; - } - } - return RealmClassMetadata(classType, classInfo.ref.key, primaryKey); - }); - } - - Map getPropertyMetadata(Realm realm, int classKey) { - return using((Arena arena) { - final propertyCountPtr = arena(); - _realmLib.invokeGetBool( - () => _realmLib.realm_get_property_keys(realm.handle._pointer, classKey, nullptr, 0, propertyCountPtr), "Error getting property count"); - - var propertyCount = propertyCountPtr.value; - final propertiesPtr = arena(propertyCount); - _realmLib.invokeGetBool(() => _realmLib.realm_get_class_properties(realm.handle._pointer, classKey, propertiesPtr, propertyCount, propertyCountPtr), - "Error getting class properties."); - - propertyCount = propertyCountPtr.value; - Map result = {}; - for (var i = 0; i < propertyCount; i++) { - final property = propertiesPtr.elementAt(i); - final propertyName = property.ref.name.cast().toDartString(); - final propertyMeta = RealmPropertyMetadata(property.ref.key, RealmCollectionType.values.elementAt(property.ref.collection_type)); - result[propertyName] = propertyMeta; - } - return result; - }); - } - - RealmObjectHandle createRealmObject(Realm realm, int classKey) { - final realmPtr = _realmLib.invokeGetPointer(() => _realmLib.realm_object_create(realm.handle._pointer, classKey)); - return RealmObjectHandle._(realmPtr); - } - - RealmObjectHandle createRealmObjectWithPrimaryKey(Realm realm, int classKey, Object primaryKey) { - return using((Arena arena) { - final realm_value = _toRealmValue(primaryKey, arena); - final realmPtr = _realmLib.invokeGetPointer(() => _realmLib.realm_object_create_with_primary_key(realm.handle._pointer, classKey, realm_value.ref)); - return RealmObjectHandle._(realmPtr); - }); - } - - Object? getProperty(RealmObject object, int propertyKey) { - return using((Arena arena) { - final realm_value = arena(); - _realmLib.invokeGetBool(() => _realmLib.realm_get_value(object.handle._pointer, propertyKey, realm_value)); - return realm_value.toDartValue(object.realm); - }); - } - - void setProperty(RealmObject object, int propertyKey, Object? value, bool isDefault) { - return using((Arena arena) { - final realm_value = _toRealmValue(value, arena); - _realmLib.invokeGetBool(() => _realmLib.realm_set_value(object.handle._pointer, propertyKey, realm_value.ref, isDefault)); - }); - } - - // For debugging - // ignore: unused_element - int get _threadId => _realmLib.get_thread_id(); - - RealmObjectHandle? find(Realm realm, int classKey, Object primaryKey) { - return using((Arena arena) { - final realm_value = _toRealmValue(primaryKey, arena); - final pointer = _realmLib.realm_object_find_with_primary_key(realm.handle._pointer, classKey, realm_value.ref, nullptr); - if (pointer == nullptr) { - return null; - } - - return RealmObjectHandle._(pointer); - }); - } - - void deleteRealmObject(RealmObject object) { - _realmLib.invokeGetBool(() => _realmLib.realm_object_delete(object.handle._pointer)); - } - - RealmResultsHandle findAll(Realm realm, int classKey) { - final pointer = _realmLib.invokeGetPointer(() => _realmLib.realm_object_find_all(realm.handle._pointer, classKey)); - return RealmResultsHandle._(pointer); - } - - RealmResultsHandle queryClass(Realm realm, int classKey, String query, List args) { - return using((arena) { - final length = args.length; - final argsPointer = arena(length); - for (var i = 0; i < length; ++i) { - _intoRealmValue(args[i], argsPointer.elementAt(i), arena); - } - final queryHandle = RealmQueryHandle._(_realmLib.invokeGetPointer( - () => _realmLib.realm_query_parse( - realm.handle._pointer, - classKey, - query.toUtf8Ptr(arena), - length, - argsPointer, - ), - )); - final resultsPointer = _realmLib.invokeGetPointer(() => _realmLib.realm_query_find_all(queryHandle._pointer)); - return RealmResultsHandle._(resultsPointer); - }); - } - - RealmResultsHandle queryResults(RealmResults target, String query, List args) { - return using((arena) { - final length = args.length; - final argsPointer = arena(length); - for (var i = 0; i < length; ++i) { - _intoRealmValue(args[i], argsPointer.elementAt(i), arena); - } - final queryHandle = RealmQueryHandle._(_realmLib.invokeGetPointer( - () => _realmLib.realm_query_parse_for_results( - target.handle._pointer, - query.toUtf8Ptr(arena), - length, - argsPointer, - ), - )); - final resultsPointer = _realmLib.invokeGetPointer(() => _realmLib.realm_query_find_all(queryHandle._pointer)); - return RealmResultsHandle._(resultsPointer); - }); - } - - RealmResultsHandle queryList(RealmList target, String query, List args) { - return using((arena) { - final length = args.length; - final argsPointer = arena(length); - for (var i = 0; i < length; ++i) { - _intoRealmValue(args[i], argsPointer.elementAt(i), arena); - } - final queryHandle = RealmQueryHandle._(_realmLib.invokeGetPointer( - () => _realmLib.realm_query_parse_for_list( - target.handle._pointer, - query.toUtf8Ptr(arena), - length, - argsPointer, - ), - )); - final resultsPointer = _realmLib.invokeGetPointer(() => _realmLib.realm_query_find_all(queryHandle._pointer)); - return RealmResultsHandle._(resultsPointer); - }); - } - - RealmObjectHandle getObjectAt(RealmResults results, int index) { - final pointer = _realmLib.invokeGetPointer(() => _realmLib.realm_results_get_object(results.handle._pointer, index)); - return RealmObjectHandle._(pointer); - } - - int getResultsCount(RealmResults results) { - return using((Arena arena) { - final countPtr = arena(); - _realmLib.invokeGetBool(() => _realmLib.realm_results_count(results.handle._pointer, countPtr)); - return countPtr.value; - }); - } - - CollectionChanges getCollectionChanges(RealmCollectionChangesHandle changes) { - return using((arena) { - final out_num_deletions = arena(); - final out_num_insertions = arena(); - final out_num_modifications = arena(); - final out_num_moves = arena(); - _realmLib.realm_collection_changes_get_num_changes( - changes._pointer, - out_num_deletions, - out_num_insertions, - out_num_modifications, - out_num_moves, - ); - - final deletionsCount = out_num_deletions != nullptr ? out_num_deletions.value : 0; - final insertionCount = out_num_insertions != nullptr ? out_num_insertions.value : 0; - final modificationCount = out_num_modifications != nullptr ? out_num_modifications.value : 0; - var moveCount = out_num_moves != nullptr ? out_num_moves.value : 0; - - final out_deletion_indexes = arena(deletionsCount); - final out_insertion_indexes = arena(insertionCount); - final out_modification_indexes = arena(modificationCount); - final out_modification_indexes_after = arena(modificationCount); - final out_moves = arena(moveCount); - - _realmLib.realm_collection_changes_get_changes( - changes._pointer, - out_deletion_indexes, - deletionsCount, - out_insertion_indexes, - insertionCount, - out_modification_indexes, - modificationCount, - out_modification_indexes_after, - modificationCount, - out_moves, - moveCount, - ); - - var elementZero = out_moves.elementAt(0); - List moves = List.filled(moveCount, Move(elementZero.ref.from, elementZero.ref.to)); - for (var i = 1; i < moveCount; i++) { - final movePtr = out_moves.elementAt(i); - moves[i] = Move(movePtr.ref.from, movePtr.ref.to); - } - - return CollectionChanges(out_deletion_indexes.toIntList(deletionsCount), out_insertion_indexes.toIntList(insertionCount), - out_modification_indexes.toIntList(modificationCount), out_modification_indexes_after.toIntList(modificationCount), moves); - }); - } - - RealmLinkHandle _getObjectAsLink(RealmObject object) { - final realmLink = _realmLib.realm_object_as_link(object.handle._pointer); - return RealmLinkHandle._(realmLink); - } - - RealmObjectHandle _getObject(Realm realm, int classKey, int objectKey) { - final pointer = _realmLib.invokeGetPointer(() => _realmLib.realm_get_object(realm.handle._pointer, classKey, objectKey)); - return RealmObjectHandle._(pointer); - } - - RealmListHandle getListProperty(RealmObject object, int propertyKey) { - final pointer = _realmLib.invokeGetPointer(() => _realmLib.realm_get_list(object.handle._pointer, propertyKey)); - return RealmListHandle._(pointer); - } - - int getListSize(RealmListHandle handle) { - return using((Arena arena) { - final size = arena(); - _realmLib.invokeGetBool(() => _realmLib.realm_list_size(handle._pointer, size)); - return size.value; - }); - } - - Object? listGetElementAt(RealmList list, int index) { - return using((Arena arena) { - final realm_value = arena(); - _realmLib.invokeGetBool(() => _realmLib.realm_list_get(list.handle._pointer, index, realm_value)); - return realm_value.toDartValue(list.realm); - }); - } - - void listSetElementAt(RealmListHandle handle, int index, Object? value) { - return using((Arena arena) { - final realm_value = _toRealmValue(value, arena); - _realmLib.invokeGetBool(() => _realmLib.realm_list_set(handle._pointer, index, realm_value.ref)); - }); - } - - void listInsertElementAt(RealmListHandle handle, int index, Object? value) { - return using((Arena arena) { - final realm_value = _toRealmValue(value, arena); - _realmLib.invokeGetBool(() => _realmLib.realm_list_insert(handle._pointer, index, realm_value.ref)); - }); - } - - void listDeleteAll(RealmList list) { - _realmLib.invokeGetBool(() => _realmLib.realm_list_remove_all(list.handle._pointer)); - } - - void resultsDeleteAll(RealmResults results) { - _realmLib.invokeGetBool(() => _realmLib.realm_results_delete_all(results.handle._pointer)); - } - - void listClear(RealmList list) { - _realmLib.invokeGetBool(() => _realmLib.realm_list_clear(list.handle._pointer)); - } - - bool _equals(Handle first, Handle second) { - return _realmLib.realm_equals(first._pointer.cast(), second._pointer.cast()); - } - - bool objectEquals(RealmObject first, RealmObject second) => _equals(first.handle, second.handle); - bool realmEquals(Realm first, Realm second) => _equals(first.handle, second.handle); - - RealmResultsHandle resultsSnapshot(RealmResults results) { - final resultsPointer = _realmLib.invokeGetPointer(() => _realmLib.realm_results_snapshot(results.handle._pointer)); - return RealmResultsHandle._(resultsPointer); - } - - bool objectIsValid(RealmObject object) { - return _realmLib.realm_object_is_valid(object.handle._pointer); - } - - bool listIsValid(RealmList list) { - return _realmLib.realm_list_is_valid(list.handle._pointer); - } - - static void collection_change_callback(Pointer userdata, Pointer data) { - NotificationsController? controller = userdata.toObject(); - if (controller == null) { - return; - } - - if (data == nullptr) { - controller.onError(RealmError("Invalid notifications data received")); - return; - } - - try { - final clonedData = _realmLib.realm_clone(data.cast()); - if (clonedData == nullptr) { - controller.onError(RealmError("Error while cloning notifications data")); - return; - } - - final changesHandle = RealmCollectionChangesHandle._(clonedData.cast()); - controller.onChanges(changesHandle); - } catch (e) { - controller.onError(RealmError("Error handling change notifications. Error: $e")); - } - } - - static void object_change_callback(Pointer userdata, Pointer data) { - NotificationsController? controller = userdata.toObject(); - if (controller == null) { - return; - } - - if (data == nullptr) { - controller.onError(RealmError("Invalid notifications data received")); - return; - } - - try { - final clonedData = _realmLib.realm_clone(data.cast()); - if (clonedData == nullptr) { - controller.onError(RealmError("Error while cloning notifications data")); - return; - } - - final changesHandle = RealmObjectChangesHandle._(clonedData.cast()); - controller.onChanges(changesHandle); - } catch (e) { - controller.onError(RealmError("Error handling change notifications. Error: $e")); - } - } - - RealmNotificationTokenHandle subscribeResultsNotifications(RealmResultsHandle handle, NotificationsController controller, SchedulerHandle schedulerHandle) { - final pointer = _realmLib.invokeGetPointer(() => _realmLib.realm_results_add_notification_callback( - handle._pointer, - controller.toGCHandle(), - nullptr, - nullptr, - Pointer.fromFunction(collection_change_callback), - nullptr, - schedulerHandle._pointer, - )); - - return RealmNotificationTokenHandle._(pointer); - } - - RealmNotificationTokenHandle subscribeListNotifications(RealmListHandle handle, NotificationsController controller, SchedulerHandle schedulerHandle) { - final pointer = _realmLib.invokeGetPointer(() => _realmLib.realm_list_add_notification_callback( - handle._pointer, - controller.toGCHandle(), - nullptr, - nullptr, - Pointer.fromFunction(collection_change_callback), - nullptr, - schedulerHandle._pointer, - )); - - return RealmNotificationTokenHandle._(pointer); - } - - RealmNotificationTokenHandle subscribeObjectNotifications(RealmObjectHandle handle, NotificationsController controller, SchedulerHandle schedulerHandle) { - final pointer = _realmLib.invokeGetPointer(() => _realmLib.realm_object_add_notification_callback( - handle._pointer, - controller.toGCHandle(), - nullptr, - nullptr, - Pointer.fromFunction(object_change_callback), - nullptr, - schedulerHandle._pointer, - )); - - return RealmNotificationTokenHandle._(pointer); - } - - bool getObjectChangesIsDeleted(RealmObjectChangesHandle handle) { - return _realmLib.realm_object_changes_is_deleted(handle._pointer); - } - - List getObjectChangesProperties(RealmObjectChangesHandle handle) { - return using((arena) { - final count = _realmLib.realm_object_changes_get_num_modified_properties(handle._pointer); - - final out_modified = arena(count); - _realmLib.realm_object_changes_get_modified_properties(handle._pointer, out_modified, count); - - return out_modified.asTypedList(count).toList(); - }); - } - - AppConfigHandle createAppConfig(ApplicationConfiguration configuration, RealmHttpTransportHandle httpTransport) { - return using((arena) { - final app_id = configuration.appId.toUtf8Ptr(arena); - final handle = AppConfigHandle._(_realmLib.realm_app_config_new(app_id, httpTransport._pointer)); - - _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 (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)); - - //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; - }); - } - - RealmAppCredentialsHandle createAppCredentialsAnonymous() { - return RealmAppCredentialsHandle._(_realmLib.realm_app_credentials_new_anonymous()); - } - - RealmAppCredentialsHandle createAppCredentialsEmailPassword(String email, String password) { - return using((arena) { - final emailPtr = email.toUtf8Ptr(arena); - final passwordPtr = password.toRealmString(arena); - return RealmAppCredentialsHandle._(_realmLib.realm_app_credentials_new_email_password(emailPtr, passwordPtr.ref)); - }); - } - - RealmHttpTransportHandle createHttpTransport(HttpClient httpClient) { - return RealmHttpTransportHandle._(_realmLib.realm_http_transport_new( - Pointer.fromFunction(request_callback), - httpClient.toGCHandle(), - 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. - - HttpClient? userObject = userData.toObject(); - if (userObject == null) { - return; - } - - HttpClient client = userObject; - - client.connectionTimeout = Duration(milliseconds: request.timeout_ms); - - final url = Uri.parse(request.url.cast().toDartString()); - - 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, request.method, url, body, headers, request_context); - // The request struct dies here! - } - - static void _request_callback_async( - HttpClient client, - int requestMethod, - 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; - - // this throws if requestMethod is unknown _HttpMethod - final method = _HttpMethod.values[requestMethod]; - - 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); - } - }); - } - - SyncClientConfigHandle createSyncClientConfig(ApplicationConfiguration configuration) { - return using((arena) { - final c = configuration; - final handle = SyncClientConfigHandle._(_realmLib.realm_sync_client_config_new()); - if (c.baseFilePath.path.isNotEmpty) { - _realmLib.realm_sync_client_config_set_base_file_path(handle._pointer, c.baseFilePath.path.toUtf8Ptr(arena)); - } - _realmLib.realm_sync_client_config_set_metadata_mode(handle._pointer, c.metadataPersistenceMode.index); - if (c.metadataEncryptionKey != null && c.metadataPersistenceMode == MetadataPersistenceMode.encrypted) { - _realmLib.realm_sync_client_config_set_metadata_encryption_key(handle._pointer, c.metadataEncryptionKey!.toUint8Ptr(arena)); - } - return handle; - }); - } - - AppHandle getApp(ApplicationConfiguration configuration) { - final httpTransport = createHttpTransport(configuration.httpClient); - final appConfig = createAppConfig(configuration, httpTransport); - final syncClientConfig = createSyncClientConfig(configuration); - return AppHandle._(_realmLib.invokeGetPointer(() => _realmLib.realm_app_get(appConfig._pointer, syncClientConfig._pointer))); - } -} - -class LastError { - final int code; - final String? message; - - LastError(this.code, [this.message]); - - @override - String toString() { - return "Error code: $code ${(message != null ? ". Message: $message" : "")}"; - } -} - -abstract class Handle { - final Pointer _pointer; - late final Dart_FinalizableHandle _finalizableHandle; - - Handle(this._pointer, int size) { - _finalizableHandle = _realmLib.realm_attach_finalizer(this, _pointer.cast(), size); - if (_finalizableHandle == nullptr) { - throw Exception("Error creating $runtimeType"); - } - } - - Handle.unowned(this._pointer); - - @override - String toString() => "${_pointer.toString()} value=${_pointer.cast().value}"; -} - -class SchemaHandle extends Handle { - SchemaHandle._(Pointer pointer) : super(pointer, 24); -} - -class ConfigHandle extends Handle { - ConfigHandle._(Pointer pointer) : super(pointer, 512); -} - -class RealmHandle extends Handle { - RealmHandle._(Pointer pointer) : super(pointer, 24); - - RealmHandle._unowned(Pointer pointer) : super.unowned(pointer); -} - -class SchedulerHandle extends Handle { - SchedulerHandle._(Pointer pointer) : super(pointer, 24); -} - -class RealmObjectHandle extends Handle { - RealmObjectHandle._(Pointer pointer) : super(pointer, 112); -} - -class RealmLinkHandle { - final int targetKey; - final int classKey; - RealmLinkHandle._(realm_link_t link) - : targetKey = link.target, - classKey = link.target_table; -} - -class RealmResultsHandle extends Handle { - RealmResultsHandle._(Pointer pointer) : super(pointer, 872); -} - -class RealmListHandle extends Handle { - RealmListHandle._(Pointer pointer) : super(pointer, 88); -} - -class RealmQueryHandle extends Handle { - RealmQueryHandle._(Pointer pointer) : super(pointer, 256); -} - -class RealmNotificationTokenHandle extends Handle { - bool released = false; - RealmNotificationTokenHandle._(Pointer pointer) : super(pointer, 1 << 32); - - void release() { - if (released) { - return; - } - - _realmLib.realm_delete_finalizable(_finalizableHandle, this); - _realmLib.realm_release(_pointer.cast()); - released = true; - } -} - -class RealmCollectionChangesHandle extends Handle { - RealmCollectionChangesHandle._(Pointer pointer) : super(pointer, 256); -} - -class RealmObjectChangesHandle extends Handle { - RealmObjectChangesHandle._(Pointer pointer) : super(pointer, 256); -} - -class RealmAppCredentialsHandle extends Handle { - RealmAppCredentialsHandle._(Pointer pointer) : super(pointer, 16); -} - -class RealmHttpTransportHandle extends Handle { - RealmHttpTransportHandle._(Pointer pointer) : super(pointer, 24); -} - -class AppConfigHandle extends Handle { - AppConfigHandle._(Pointer pointer) : super(pointer, 8); -} - -class SyncClientConfigHandle extends Handle { - SyncClientConfigHandle._(Pointer pointer) : super(pointer, 8); -} - -class AppHandle extends Handle { -<<<<<<< HEAD - AppHandle._( - Pointer pointer) : super(pointer, 16); -} - -class UserHandle extends Handle { - UserHandle._(Pointer pointer) : super(pointer, 256); // TODO: What should hint be? -======= - AppHandle._(Pointer pointer) : super(pointer, 16); ->>>>>>> realm_app -} - -extension on List { - Pointer toUint8Ptr(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; - } - - 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); - return units.toInt8Ptr(allocator).cast(); - } - - Pointer toRealmString(Allocator allocator) { - final realm_string = allocator(); - realm_string.ref.data = toUtf8Ptr(allocator); - final units = utf8.encode(this); - realm_string.ref.size = units.length + 1; - return realm_string; - } -} - -extension _RealmLibraryEx on RealmLibrary { - void invokeGetBool(bool Function() callback, [String? errorMessage]) { - bool success = callback(); - if (!success) { - realmCore.throwLastError(errorMessage); - } - } - - Pointer invokeGetPointer(Pointer Function() callback, [String? errorMessage]) { - final result = callback(); - if (result == nullptr) { - realmCore.throwLastError(errorMessage); - } - return result; - } -} - -Pointer _toRealmValue(Object? value, Allocator allocator) { - final realm_value = allocator(); - _intoRealmValue(value, realm_value, allocator); - return realm_value; -} - -void _intoRealmValue(Object? value, Pointer realm_value, Allocator allocator) { - if (value == null) { - realm_value.ref.type = realm_value_type.RLM_TYPE_NULL; - } else if (value is RealmObject) { - //when converting a RealmObject to realm_value.link we assume the object is managed - final link = realmCore._getObjectAsLink(value); - realm_value.ref.values.link.target = link.targetKey; - realm_value.ref.values.link.target_table = link.classKey; - realm_value.ref.type = realm_value_type.RLM_TYPE_LINK; - } else { - switch (value.runtimeType) { - case int: - realm_value.ref.values.integer = value as int; - realm_value.ref.type = realm_value_type.RLM_TYPE_INT; - break; - case bool: - realm_value.ref.values.boolean = value as bool ? 0 : 1; - realm_value.ref.type = realm_value_type.RLM_TYPE_BOOL; - break; - case String: - String string = value as String; - final units = utf8.encode(string); - final result = allocator(units.length); - final Uint8List nativeString = result.asTypedList(units.length); - nativeString.setAll(0, units); - realm_value.ref.values.string.data = result.cast(); - realm_value.ref.values.string.size = units.length; - realm_value.ref.type = realm_value_type.RLM_TYPE_STRING; - break; - case double: - realm_value.ref.values.dnum = value as double; - realm_value.ref.type = realm_value_type.RLM_TYPE_DOUBLE; - break; - case ObjectId: - final bytes = (value as ObjectId).bytes; - for (var i = 0; i < 12; i++) { - realm_value.ref.values.object_id.bytes[i] = bytes[i]; - } - - realm_value.ref.type = realm_value_type.RLM_TYPE_OBJECT_ID; - break; - default: - throw RealmException("Property type ${value.runtimeType} not supported"); - } - } -} - -extension on Pointer { - Object? toDartValue(Realm realm) { - if (this == nullptr) { - throw RealmException("Can not convert nullptr realm_value to Dart value"); - } - - switch (ref.type) { - case realm_value_type.RLM_TYPE_NULL: - return null; - case realm_value_type.RLM_TYPE_INT: - return ref.values.integer; - case realm_value_type.RLM_TYPE_BOOL: - return ref.values.boolean == 0; - case realm_value_type.RLM_TYPE_STRING: - return ref.values.string.data.cast().toDartString(length: ref.values.string.size); - case realm_value_type.RLM_TYPE_FLOAT: - return ref.values.fnum; - case realm_value_type.RLM_TYPE_DOUBLE: - return ref.values.dnum; - case realm_value_type.RLM_TYPE_LINK: - final objectKey = ref.values.link.target; - final classKey = ref.values.link.target_table; - RealmObjectHandle handle = realmCore._getObject(realm, classKey, objectKey); - return handle; - case realm_value_type.RLM_TYPE_BINARY: - throw Exception("Not implemented"); - case realm_value_type.RLM_TYPE_TIMESTAMP: - throw Exception("Not implemented"); - case realm_value_type.RLM_TYPE_DECIMAL128: - throw Exception("Not implemented"); - case realm_value_type.RLM_TYPE_OBJECT_ID: - return ObjectId.fromBytes(cast().asTypedList(12)); - case realm_value_type.RLM_TYPE_UUID: - throw Exception("Not implemented"); - default: - throw RealmException("realm_value_type ${ref.type} not supported"); - } - } -} - -extension on Pointer { - List toIntList(int count) { - List result = List.filled(count, elementAt(0).value); - for (var i = 1; i < count; i++) { - result[i] = elementAt(i).value; - } - return result; - } -} - -extension on Pointer { - T? toObject() { - assert(this != nullptr, "Pointer is null"); - - final object = _realmLib.gc_handle_to_object(this); - - assert(object is T, "$T expected"); - if (object is! T) { - return null; - } - - return object; - } -} - -extension on Object { - Pointer toGCHandle() { - return _realmLib.object_to_gc_handle(this); - } -} - -// 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, -} From 9fd8fece4ca22daad4e6346fd2cc3faadb536196 Mon Sep 17 00:00:00 2001 From: Desislava Stefanova Date: Tue, 12 Apr 2022 15:42:12 +0300 Subject: [PATCH 36/91] Fix after merge --- lib/src/application_configuration.dart | 100 ------------------------- 1 file changed, 100 deletions(-) delete mode 100644 lib/src/application_configuration.dart diff --git a/lib/src/application_configuration.dart b/lib/src/application_configuration.dart deleted file mode 100644 index aa5f5b724..000000000 --- a/lib/src/application_configuration.dart +++ /dev/null @@ -1,100 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// 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'; - -/// Specify if and how to persists user objects. -enum MetadataPersistenceMode { - /// Persist [User] objects, but do not encrypt them. - unencrypted, - - /// Persist [User] objects in an encrypted store. - encrypted, - - /// Do not persist [User] objects. - disabled, -} - -@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. 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; - - /// 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] 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 [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, { - Uri? baseUrl, - Directory? baseFilePath, - this.defaultRequestTimeout = const Duration(milliseconds: 60000), - this.localAppName, - this.localAppVersion, - this.metadataPersistenceMode = MetadataPersistenceMode.unencrypted, - this.metadataEncryptionKey, - HttpClient? httpClient, - }) : baseUrl = baseUrl ?? Uri.parse('https://realm.mongodb.com'), - baseFilePath = baseFilePath ?? Directory.current, - httpClient = httpClient ?? HttpClient(); -} From 07be0aea38276991191e57f8dc7aecf633a7a82e Mon Sep 17 00:00:00 2001 From: Desislava Stefanova Date: Tue, 12 Apr 2022 15:52:15 +0300 Subject: [PATCH 37/91] Fix after merge --- src/realm_dart_app.h | 48 -------------------------------------------- 1 file changed, 48 deletions(-) delete mode 100644 src/realm_dart_app.h diff --git a/src/realm_dart_app.h b/src/realm_dart_app.h deleted file mode 100644 index bf90f828b..000000000 --- a/src/realm_dart_app.h +++ /dev/null @@ -1,48 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// 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. -// -//////////////////////////////////////////////////////////////////////////////// - -#ifndef REALM_DART_APP_H -#define REALM_DART_APP_H - -#include "realm.h" -#include "dart_api_dl.h" - -/** - * Completion callback for asynchronous Realm App operations that yield a user object. - * - * @param userdata The userdata the asynchronous operation was started with. - * @param user User object produced by the operation, or null if it failed. - * The pointer is alive only for the duration of the callback, - * if you wish to use it further make a copy with realm_clone(). - * @param error Pointer to an error object if the operation failed, otherwise null if it completed successfully. - * - * This is a dart specific version of the completion callback for asynchronous Realm operations. - */ -typedef void (*realm_dart_app_user_completion_func_t)(Dart_Handle userdata, realm_user_t* user, const realm_app_error_t* error); - -/** - * @brief - * - * @param completion - * @param userdata - * @return true if operation started successfully, false if an error occurred. - */ -RLM_API bool realm_dart_app_log_in_with_credentials(realm_app_t*, realm_app_credentials_t*, - realm_dart_app_user_completion_func_t completion, Dart_Handle userdata); - -#endif \ No newline at end of file From 18ec3ca6ffd9534139fd7dd320c421f12739459c Mon Sep 17 00:00:00 2001 From: Desislava Stefanova Date: Tue, 12 Apr 2022 16:00:50 +0300 Subject: [PATCH 38/91] Updarte CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b41053ac..d19677967 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ x.x.x Release notes (yyyy-MM-dd) * Support application ([#446](https://github.com/realm/realm-dart/pull/446/)) * Support should realm compact on open callback `Configuration.shouldCompactCallback` as option when configuring a Realm to determine if it should be compacted before being returned. ([#466](https://github.com/realm/realm-dart/pull/466/)) * Support ObjectId ([#468](https://github.com/realm/realm-dart/pull/468)) +* Support application user login method ([#469](https://github.com/realm/realm-dart/pull/469/)) ### 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 60b665c78c080a7402e8f2ab53bdd7cd9696870b Mon Sep 17 00:00:00 2001 From: blagoev Date: Wed, 13 Apr 2022 09:46:42 +0300 Subject: [PATCH 39/91] fix build --- lib/src/application.dart | 1 + lib/src/native/realm_core.dart | 6 +++++- test/test.dart | 24 ++++++++++++++---------- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/lib/src/application.dart b/lib/src/application.dart index b351153c6..cf797e262 100644 --- a/lib/src/application.dart +++ b/lib/src/application.dart @@ -21,6 +21,7 @@ import 'package:meta/meta.dart'; import 'native/realm_core.dart'; import 'credentials.dart'; import 'user.dart'; +import 'configuration.dart'; /// Specify if and how to persists user objects. enum MetadataPersistenceMode { diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index 59d67599f..2bc62805b 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -849,6 +849,8 @@ class _RealmCore { final syncClientConfig = createSyncClientConfig(configuration); final realm_app = _realmLib.invokeGetPointer(() => _realmLib.realm_app_get(appConfig._pointer, syncClientConfig._pointer)); return AppHandle._(realm_app); + } + static void _logInCallback(Pointer userdata, Pointer user, Pointer error) { final Completer? userHandleCompleter = userdata.toObject(); if (userHandleCompleter == null) { @@ -861,10 +863,11 @@ class _RealmCore { userHandleCompleter.completeError(RealmException(message)); } } + static void _freeCallback(Pointer userdata) { userdata.toObject(); // TODO: release } - + Future logIn(Application application, Credentials credentials) async { final completer = Completer(); _realmLib.realm_app_log_in_with_credentials( @@ -995,6 +998,7 @@ class AppHandle extends Handle { class UserHandle extends Handle { UserHandle._(Pointer pointer) : super(pointer, 256); // TODO: What should hint be? } + extension on List { Pointer toInt8Ptr(Allocator allocator) { return toUint8Ptr(allocator).cast(); diff --git a/test/test.dart b/test/test.dart index 53356855b..9fea6338b 100644 --- a/test/test.dart +++ b/test/test.dart @@ -82,8 +82,8 @@ Map baasApps = {}; final _openRealms = Queue(); //Overrides test method so we can filter tests -void test(String? name, dynamic Function() testFunction, {dynamic skip}) { - if (testName != null && !name!.contains(testName!)) { +void test(String name, dynamic Function() testFunction, {dynamic skip}) { + if (testName != null && !name.contains(testName!)) { return; } @@ -92,7 +92,7 @@ void test(String? name, dynamic Function() testFunction, {dynamic skip}) { timeout = Duration.secondsPerDay; return true; }()); - + testing.test(name, testFunction, skip: skip, timeout: Timeout(Duration(seconds: timeout))); } @@ -200,26 +200,30 @@ Future setupBaas() async { @isTest Future testWithBaaS( - String? name, + String name, FutureOr Function(ApplicationConfiguration configuration) testFunction, { String appName = 'flexible', - bool skip = false, + dynamic skip, }) async { final url = Uri.tryParse(Platform.environment['BAAS_URL'] ?? 'https://realm-dev.mongodb.com'); final apiKey = Platform.environment['BAAS_API_KEY']; final projectId = Platform.environment['BAAS_PROJECT_ID']; - final missingOrSkip = skip || url == null || apiKey == null || projectId == null; + if (skip == null) { + skip = url == null || apiKey == null || projectId == null; + } + else if (skip is bool) { + skip = skip || url == null || apiKey == null || projectId == null; + } + test(name, () async { - if (!missingOrSkip) { final app = baasApps[appName] ?? baasApps.values.first; - final temporary = await Directory.systemTemp.createTemp('realm_dart_test_'); + final temporary = await Directory.systemTemp.createTemp('realm_test_'); final configuration = ApplicationConfiguration( app.clientAppId, baseUrl: url, baseFilePath: temporary, ); return await testFunction(configuration); - } - }, skip: missingOrSkip); + }, skip: skip); } From 4e26d963f5af3f6f8ee924e1dc07bb560d9ba86e Mon Sep 17 00:00:00 2001 From: blagoev Date: Wed, 13 Apr 2022 09:49:09 +0300 Subject: [PATCH 40/91] rename method --- test/application_test.dart | 2 +- test/test.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/application_test.dart b/test/application_test.dart index 2fab3d226..2d91bd9c9 100644 --- a/test/application_test.dart +++ b/test/application_test.dart @@ -59,7 +59,7 @@ Future main([List? args]) async { expect(application.configuration, configuration); }); - testWithBaaS('Application log in', (configuration) async { + syncTest('Application log in', (configuration) async { final application = Application(configuration); final credentials = Credentials.anonymous(); final user = await application.logIn(credentials); diff --git a/test/test.dart b/test/test.dart index 9fea6338b..e3064f9bd 100644 --- a/test/test.dart +++ b/test/test.dart @@ -199,7 +199,7 @@ Future setupBaas() async { } @isTest -Future testWithBaaS( +Future syncTest( String name, FutureOr Function(ApplicationConfiguration configuration) testFunction, { String appName = 'flexible', From b94d62cac447962633d64336706af876412c47ee Mon Sep 17 00:00:00 2001 From: blagoev Date: Wed, 13 Apr 2022 10:05:08 +0300 Subject: [PATCH 41/91] fix login invocation --- lib/src/native/realm_core.dart | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index 2bc62805b..81d0a630d 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -852,20 +852,24 @@ class _RealmCore { } static void _logInCallback(Pointer userdata, Pointer user, Pointer error) { - final Completer? userHandleCompleter = userdata.toObject(); - if (userHandleCompleter == null) { + final Completer? completer = userdata.toObject(); + if (completer == null) { return; } - if (error == nullptr) { - userHandleCompleter.complete(UserHandle._(_realmLib.realm_clone(user.cast()).cast())); - } else { + + if (error != nullptr) { final message = error.ref.message.cast().toDartString(); - userHandleCompleter.completeError(RealmException(message)); + completer.completeError(RealmException(message)); + return; } - } - static void _freeCallback(Pointer userdata) { - userdata.toObject(); // TODO: release + var userClone = _realmLib.realm_clone(user.cast()); + if (userClone == nullptr) { + completer.completeError(RealmException("Error while cloning login data")); + return; + } + + completer.complete(UserHandle._(userClone.cast())); } Future logIn(Application application, Credentials credentials) async { @@ -875,7 +879,7 @@ class _RealmCore { credentials.handle._pointer, Pointer.fromFunction(_logInCallback), completer.toGCHandle(), - Pointer.fromFunction(_freeCallback), + nullptr, ); return completer.future; } From bad719b3e59ff47ee0ccfd40f90269e9f7de7dd5 Mon Sep 17 00:00:00 2001 From: blagoev Date: Wed, 13 Apr 2022 10:31:23 +0300 Subject: [PATCH 42/91] fix GC hints return error code from windows build script --- lib/src/application.dart | 3 ++- lib/src/native/realm_core.dart | 4 ++-- scripts/build.bat | 15 ++++++++++++++- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/lib/src/application.dart b/lib/src/application.dart index cf797e262..5c54b4597 100644 --- a/lib/src/application.dart +++ b/lib/src/application.dart @@ -117,7 +117,8 @@ class Application { Application(this.configuration) : _handle = realmCore.getApp(configuration); Future logIn(Credentials credentials) async { - return UserInternal.create(await realmCore.logIn(this, credentials)); + var userHandle = await realmCore.logIn(this, credentials); + return UserInternal.create(userHandle); } } diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index 81d0a630d..5f7ba17bd 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -958,7 +958,7 @@ class RealmQueryHandle extends Handle { class RealmNotificationTokenHandle extends Handle { bool released = false; - RealmNotificationTokenHandle._(Pointer pointer) : super(pointer, 1 << 32); + RealmNotificationTokenHandle._(Pointer pointer) : super(pointer, 32); void release() { if (released) { @@ -1000,7 +1000,7 @@ class AppHandle extends Handle { } class UserHandle extends Handle { - UserHandle._(Pointer pointer) : super(pointer, 256); // TODO: What should hint be? + UserHandle._(Pointer pointer) : super(pointer, 24); } extension on List { diff --git a/scripts/build.bat b/scripts/build.bat index fcda6d9bd..c67e2e7f2 100644 --- a/scripts/build.bat +++ b/scripts/build.bat @@ -11,6 +11,8 @@ SET PROJECT_ROOT=%CD% mkdir %PROJECT_ROOT%\build-windows pushd %PROJECT_ROOT%\build-windows +SET EXIT_CODE=0 + cmake ^ -G "Visual Studio 16 2019" ^ -A x64 ^ @@ -19,8 +21,19 @@ cmake ^ -DVCPKG_TARGET_TRIPLET="x64-windows-static" ^ %PROJECT_ROOT% +IF %ErrorLevel% NEQ 0 ( + SET EXIT_CODE=%ErrorLevel% + GOTO popd_all +) + cmake --build . --config MinSizeRel +IF %ErrorLevel% NEQ 0 ( + SET EXIT_CODE=%ErrorLevel% + GOTO popd_all +) + + @REM exit to caller's location with success :popd_all -popd && goto popd_all || exit /b 0 \ No newline at end of file +popd && goto popd_all || exit /b %EXIT_CODE% \ No newline at end of file From 333b99d1f71b582c9cca183ecfef1eb928f63bef Mon Sep 17 00:00:00 2001 From: blagoev Date: Wed, 13 Apr 2022 10:33:46 +0300 Subject: [PATCH 43/91] [test] build in debug --- scripts/build-linux.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build-linux.sh b/scripts/build-linux.sh index bb7754d91..c73b36f35 100755 --- a/scripts/build-linux.sh +++ b/scripts/build-linux.sh @@ -10,7 +10,7 @@ mkdir -p build-linux pushd build-linux cmake -GNinja \ - -DCMAKE_BUILD_TYPE=MinSizeRel \ + -DCMAKE_BUILD_TYPE=Debug \ .. cmake --build . \ No newline at end of file From 3083650f44e6e093498f79c76d1b1f6c925c5454 Mon Sep 17 00:00:00 2001 From: Desislava Stefanova Date: Wed, 13 Apr 2022 11:13:00 +0300 Subject: [PATCH 44/91] Fix after merge --- lib/src/application.dart | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/lib/src/application.dart b/lib/src/application.dart index 8b8e63681..b34da314b 100644 --- a/lib/src/application.dart +++ b/lib/src/application.dart @@ -23,6 +23,7 @@ import 'configuration.dart'; import 'email_password_provider.dart'; /// Specify if and how to persists user objects. +/// {@category Application} enum MetadataPersistenceMode { /// Persist [User] objects, but do not encrypt them. plaintext, @@ -37,6 +38,7 @@ enum MetadataPersistenceMode { @immutable /// A class exposing configuration options for an [Application] +/// {@category Application} class ApplicationConfiguration { /// The [appId] is the unique id that identifies the Realm application. final String appId; @@ -109,17 +111,7 @@ class ApplicationConfiguration { /// The [Application]] can be used to /// * Register uses and perform various user-related operations through authentication providers /// * Synchronize data between the local device and a remote Realm App with Synchronized Realms -class Application { - final AppHandle _handle; - final ApplicationConfiguration configuration; - - Application(this.configuration) : _handle = realmCore.getApp(configuration); -} - -extension ApplicationInternal on Application { - AppHandle get handle => _handle; -} - +/// {@category Application} class Application { final AppHandle _handle; final ApplicationConfiguration configuration; From 2603ef690c24eda9edbf866e46b28b0b8a32a07a Mon Sep 17 00:00:00 2001 From: Desislava Stefanova Date: Wed, 13 Apr 2022 16:27:39 +0300 Subject: [PATCH 45/91] Callback implementation changes --- lib/src/application.dart | 8 +++--- lib/src/email_password_provider.dart | 6 ++--- lib/src/native/realm_core.dart | 37 ++++++++++++++++++++-------- 3 files changed, 33 insertions(+), 18 deletions(-) diff --git a/lib/src/application.dart b/lib/src/application.dart index b34da314b..51912bb8f 100644 --- a/lib/src/application.dart +++ b/lib/src/application.dart @@ -116,16 +116,14 @@ class Application { final AppHandle _handle; final ApplicationConfiguration configuration; late final EmailPasswordProvider _emailPasswordProvider; - - Application(this.configuration) : _handle = realmCore.getApp(configuration) - { + + Application(this.configuration) : _handle = realmCore.getApp(configuration) { _emailPasswordProvider = EmailPasswordProvider(this); } - + EmailPasswordProvider get emailPasswordProvider => _emailPasswordProvider; } extension ApplicationInternal on Application { AppHandle get handle => _handle; } - diff --git a/lib/src/email_password_provider.dart b/lib/src/email_password_provider.dart index 20e037351..dc0e27450 100644 --- a/lib/src/email_password_provider.dart +++ b/lib/src/email_password_provider.dart @@ -22,9 +22,9 @@ import 'native/realm_core.dart'; /// It is always scoped to a particular app and can only be accessed via [emailPasswordProvider]. /// {@category Application} class EmailPasswordProvider { - final AppHandle _handle; + final Application application; - EmailPasswordProvider(Application app) : _handle = app.handle; + EmailPasswordProvider(this.application); /// Registers a new user with the given email and password. /// The [email] to register with. This will be the user's username and, if user confirmation is enabled, this will be the address for @@ -33,6 +33,6 @@ class EmailPasswordProvider { /// Returns an awaitable [Future] representing the asynchronous RegisterUser operation. Successful completion indicates that the user has been /// created on the server and can now be logged in calling [logIn] with [Credentials.emailPassword()]" Future registerUser(String email, String password) async { - return await realmCore.appEmailPasswordRegisterUser(_handle, email, password); + return realmCore.appEmailPasswordRegisterUser(application, email, password); } } diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index db78241ab..465e93804 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -850,25 +850,42 @@ class _RealmCore { final realm_app = _realmLib.invokeGetPointer(() => _realmLib.realm_app_get(appConfig._pointer, syncClientConfig._pointer)); return AppHandle._(realm_app); } - - static void app_email_password_provider_callback(Pointer userdata, Pointer error) { - final Completer? completer = userdata.toObject(); + + static void _app_email_password_provider_callback(Pointer userdata, Pointer error) { + String? message = error != nullptr ? error.ref.message.cast().toDartString() : null; + + final Completer? completer = userdata.toObject(); if (completer == null) { + if (message != null) throw RealmError(message); return; } - if (error != nullptr) { - final message = error.ref.message.cast().toDartString(); + if (message != null) { completer.completeError(RealmException(message)); } else { - completer.complete(); + completer.complete(true); + } + } + + static void _app_email_password_provider_free_userdata_callback(Pointer userdata) { + final Completer? completer = userdata.toObject(); + if (completer == null) { + return; + } + if (!completer.isCompleted) { + completer.complete(false); } } - Future appEmailPasswordRegisterUser(AppHandle application, String email, String password) { - final completer = Completer(); + Future appEmailPasswordRegisterUser(Application application, String email, String password) { + final completer = Completer(); using((arena) { - _realmLib.invokeGetBool(() => _realmLib.realm_app_email_password_provider_client_register_email(application._pointer, email.toUtf8Ptr(arena), - password.toRealmString(arena).ref, Pointer.fromFunction(app_email_password_provider_callback), completer.toGCHandle(), nullptr)); + _realmLib.invokeGetBool(() => _realmLib.realm_app_email_password_provider_client_register_email( + application.handle._pointer, + email.toUtf8Ptr(arena), + password.toRealmString(arena).ref, + Pointer.fromFunction(_app_email_password_provider_callback), + completer.toGCHandle(), + Pointer.fromFunction(_app_email_password_provider_free_userdata_callback))); }); return completer.future; } From 2490fe4ea6d0f4ae07cbc740a37b527131450deb Mon Sep 17 00:00:00 2001 From: Desislava Stefanova Date: Wed, 13 Apr 2022 16:37:34 +0300 Subject: [PATCH 46/91] Remove tests --- test/email_password_provider_test.dart | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/test/email_password_provider_test.dart b/test/email_password_provider_test.dart index 000fdb411..7e9c85c30 100644 --- a/test/email_password_provider_test.dart +++ b/test/email_password_provider_test.dart @@ -24,11 +24,5 @@ Future main([List? args]) async { print("Current PID $pid"); await setupTests(args); - - test('Email/Password - register user', () async { - final tmp = await Directory.systemTemp.createTemp(); - final configuration = ApplicationConfiguration(generateRandomString(10), baseFilePath: tmp); - final application = Application(configuration); - await application.emailPasswordProvider.registerUser("foo@bar.com", "pwd"); - }); + } From 9e990c2b7fc88a5de3d86c3d0cf1969429fe957a Mon Sep 17 00:00:00 2001 From: Desislava Stefanova Date: Thu, 14 Apr 2022 09:27:41 +0300 Subject: [PATCH 47/91] Delete old file --- lib/src/application_configuration.dart | 100 ------------------------- 1 file changed, 100 deletions(-) delete mode 100644 lib/src/application_configuration.dart diff --git a/lib/src/application_configuration.dart b/lib/src/application_configuration.dart deleted file mode 100644 index aa5f5b724..000000000 --- a/lib/src/application_configuration.dart +++ /dev/null @@ -1,100 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// 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'; - -/// Specify if and how to persists user objects. -enum MetadataPersistenceMode { - /// Persist [User] objects, but do not encrypt them. - unencrypted, - - /// Persist [User] objects in an encrypted store. - encrypted, - - /// Do not persist [User] objects. - disabled, -} - -@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. 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; - - /// 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] 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 [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, { - Uri? baseUrl, - Directory? baseFilePath, - this.defaultRequestTimeout = const Duration(milliseconds: 60000), - this.localAppName, - this.localAppVersion, - this.metadataPersistenceMode = MetadataPersistenceMode.unencrypted, - this.metadataEncryptionKey, - HttpClient? httpClient, - }) : baseUrl = baseUrl ?? Uri.parse('https://realm.mongodb.com'), - baseFilePath = baseFilePath ?? Directory.current, - httpClient = httpClient ?? HttpClient(); -} From 17be6154a122b025810c5020f4ec8d5abe82efc1 Mon Sep 17 00:00:00 2001 From: Desislava Stefanova Date: Thu, 14 Apr 2022 09:38:41 +0300 Subject: [PATCH 48/91] Delete old files --- src/realm_dart_app.h | 48 -------------------------------------------- 1 file changed, 48 deletions(-) delete mode 100644 src/realm_dart_app.h diff --git a/src/realm_dart_app.h b/src/realm_dart_app.h deleted file mode 100644 index bf90f828b..000000000 --- a/src/realm_dart_app.h +++ /dev/null @@ -1,48 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// 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. -// -//////////////////////////////////////////////////////////////////////////////// - -#ifndef REALM_DART_APP_H -#define REALM_DART_APP_H - -#include "realm.h" -#include "dart_api_dl.h" - -/** - * Completion callback for asynchronous Realm App operations that yield a user object. - * - * @param userdata The userdata the asynchronous operation was started with. - * @param user User object produced by the operation, or null if it failed. - * The pointer is alive only for the duration of the callback, - * if you wish to use it further make a copy with realm_clone(). - * @param error Pointer to an error object if the operation failed, otherwise null if it completed successfully. - * - * This is a dart specific version of the completion callback for asynchronous Realm operations. - */ -typedef void (*realm_dart_app_user_completion_func_t)(Dart_Handle userdata, realm_user_t* user, const realm_app_error_t* error); - -/** - * @brief - * - * @param completion - * @param userdata - * @return true if operation started successfully, false if an error occurred. - */ -RLM_API bool realm_dart_app_log_in_with_credentials(realm_app_t*, realm_app_credentials_t*, - realm_dart_app_user_completion_func_t completion, Dart_Handle userdata); - -#endif \ No newline at end of file From e8aa39529b5253ef7ff4eef5121083f1b31c31cf Mon Sep 17 00:00:00 2001 From: blagoev Date: Sun, 17 Apr 2022 23:21:23 +0300 Subject: [PATCH 49/91] application holds the HttpTransport, AppConfig and SyncClient handles fix header enumeration bug use invokeGetBool to check the app_log_in_with_credentials result --- lib/src/application.dart | 16 +++++++++++++--- lib/src/native/realm_core.dart | 30 +++++++++++++++--------------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/lib/src/application.dart b/lib/src/application.dart index 5c54b4597..931352bad 100644 --- a/lib/src/application.dart +++ b/lib/src/application.dart @@ -111,10 +111,20 @@ class ApplicationConfiguration { /// * Register uses and perform various user-related operations through authentication providers /// * Synchronize data between the local device and a remote Realm App with Synchronized Realms class Application { - final AppHandle _handle; + late final AppHandle _handle; final ApplicationConfiguration configuration; - - Application(this.configuration) : _handle = realmCore.getApp(configuration); + late final RealmHttpTransportHandle _httpTransportHandle; + // ignore: unused_field + late final AppConfigHandle _appConfigHandle; + // ignore: unused_field + late final SyncClientConfigHandle _syncClientConfigHandle; + + Application(this.configuration) { + _httpTransportHandle = realmCore.createHttpTransport(configuration.httpClient); + _appConfigHandle = realmCore.createAppConfig(configuration, _httpTransportHandle); + _syncClientConfigHandle = realmCore.createSyncClientConfig(configuration); + _handle = realmCore.getApp(_appConfigHandle, _syncClientConfigHandle); + } Future logIn(Credentials credentials) async { var userHandle = await realmCore.logIn(this, credentials); diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index 5f7ba17bd..d68fce3b1 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -805,12 +805,13 @@ class _RealmCore { responseRef.headers = arena(headerCnt); responseRef.num_headers = headerCnt; + int index = 0; response.headers.forEach((name, values) { - int idx = 0; for (final value in values) { - final headerRef = responseRef.headers.elementAt(idx).ref; + final headerRef = responseRef.headers.elementAt(index).ref; headerRef.name = name.toUtf8Ptr(arena); headerRef.value = value.toUtf8Ptr(arena); + index++; } }); @@ -843,12 +844,9 @@ class _RealmCore { }); } - AppHandle getApp(ApplicationConfiguration configuration) { - final httpTransport = createHttpTransport(configuration.httpClient); - final appConfig = createAppConfig(configuration, httpTransport); - final syncClientConfig = createSyncClientConfig(configuration); - final realm_app = _realmLib.invokeGetPointer(() => _realmLib.realm_app_get(appConfig._pointer, syncClientConfig._pointer)); - return AppHandle._(realm_app); + AppHandle getApp(AppConfigHandle appConfigHandle, SyncClientConfigHandle syncClientConfigHandle) { + final realmAppPtr = _realmLib.invokeGetPointer(() => _realmLib.realm_app_get(appConfigHandle._pointer, syncClientConfigHandle._pointer)); + return AppHandle._(realmAppPtr); } static void _logInCallback(Pointer userdata, Pointer user, Pointer error) { @@ -874,13 +872,15 @@ class _RealmCore { Future logIn(Application application, Credentials credentials) async { final completer = Completer(); - _realmLib.realm_app_log_in_with_credentials( - application.handle._pointer, - credentials.handle._pointer, - Pointer.fromFunction(_logInCallback), - completer.toGCHandle(), - nullptr, - ); + _realmLib.invokeGetBool( + () => _realmLib.realm_app_log_in_with_credentials( + application.handle._pointer, + credentials.handle._pointer, + Pointer.fromFunction(_logInCallback), + completer.toGCHandle(), + nullptr, + ), + "Login failed"); return completer.future; } } From 3c74ed6ea730dac58ff8277e5ae7a8b8e861767e Mon Sep 17 00:00:00 2001 From: blagoev Date: Tue, 19 Apr 2022 10:35:12 +0300 Subject: [PATCH 50/91] move and rename EmailPassword class fix completion callback --- lib/src/credentials.dart | 22 ++++++++++++++++ lib/src/email_password_provider.dart | 38 --------------------------- lib/src/native/realm_core.dart | 39 +++++++++++----------------- 3 files changed, 37 insertions(+), 62 deletions(-) delete mode 100644 lib/src/email_password_provider.dart diff --git a/lib/src/credentials.dart b/lib/src/credentials.dart index d8b892b22..7545f42a4 100644 --- a/lib/src/credentials.dart +++ b/lib/src/credentials.dart @@ -15,7 +15,9 @@ // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// + import 'native/realm_core.dart'; +import 'application.dart'; /// An enum containing all authentication providers. These have to be enabled manually for the application before they can be used. /// [Authentication Providers Docs](https://docs.mongodb.com/realm/authentication/providers/) @@ -54,3 +56,23 @@ class Credentials { extension CredentialsInternal on Credentials { RealmAppCredentialsHandle get handle => _handle; } + +/// A class, encapsulating functionality for users, logged in with [Credentials.emailPassword()]. +/// It is always scoped to a particular app. +/// {@category Application} +class EmailPasswordAuthProvider { + final Application application; + + /// Create a new EmailPasswordAuthProvider for the [application] + EmailPasswordAuthProvider(this.application); + + /// Registers a new user with the given email and password. + /// The [email] to register with. This will be the user's username and, if user confirmation is enabled, this will be the address for + /// the confirmation email. + /// The [password] to associate with the email. The password must be between 6 and 128 characters long. + /// + /// Successful completion indicates that the user has been created on the server and can now be logged in with [Credentials.emailPassword()]. + Future registerUser(String email, String password) async { + return realmCore.appEmailPasswordRegisterUser(application, email, password); + } +} diff --git a/lib/src/email_password_provider.dart b/lib/src/email_password_provider.dart deleted file mode 100644 index dc0e27450..000000000 --- a/lib/src/email_password_provider.dart +++ /dev/null @@ -1,38 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// 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 'application.dart'; -import 'native/realm_core.dart'; - -/// A class, encapsulating functionality for users, logged in with [Credentials.emailPassword()]. -/// It is always scoped to a particular app and can only be accessed via [emailPasswordProvider]. -/// {@category Application} -class EmailPasswordProvider { - final Application application; - - EmailPasswordProvider(this.application); - - /// Registers a new user with the given email and password. - /// The [email] to register with. This will be the user's username and, if user confirmation is enabled, this will be the address for - /// the confirmation email. - /// The [password] to associate with the email. The password must be between 6 and 128 characters long. - /// Returns an awaitable [Future] representing the asynchronous RegisterUser operation. Successful completion indicates that the user has been - /// created on the server and can now be logged in calling [logIn] with [Credentials.emailPassword()]" - Future registerUser(String email, String password) async { - return realmCore.appEmailPasswordRegisterUser(application, email, password); - } -} diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index 094ba5a8d..ce04c549c 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -851,41 +851,32 @@ class _RealmCore { return AppHandle._(realm_app); } - static void _app_email_password_provider_callback(Pointer userdata, Pointer error) { - String? message = error != nullptr ? error.ref.message.cast().toDartString() : null; - - final Completer? completer = userdata.toObject(); + static void void_completion_callback(Pointer userdata, Pointer error) { + final Completer? completer = userdata.toObject(); if (completer == null) { - if (message != null) throw RealmError(message); return; } - if (message != null) { - completer.completeError(RealmException(message)); - } else { - completer.complete(true); - } - } - static void _app_email_password_provider_free_userdata_callback(Pointer userdata) { - final Completer? completer = userdata.toObject(); - if (completer == null) { + if (error != nullptr) { + final message = error.ref.message.cast().toDartString(); + completer.completeError(RealmException(message)); return; } - if (!completer.isCompleted) { - completer.complete(false); - } + + completer.complete(); } Future appEmailPasswordRegisterUser(Application application, String email, String password) { - final completer = Completer(); + final completer = Completer(); using((arena) { _realmLib.invokeGetBool(() => _realmLib.realm_app_email_password_provider_client_register_email( - application.handle._pointer, - email.toUtf8Ptr(arena), - password.toRealmString(arena).ref, - Pointer.fromFunction(_app_email_password_provider_callback), - completer.toGCHandle(), - Pointer.fromFunction(_app_email_password_provider_free_userdata_callback))); + application.handle._pointer, + email.toUtf8Ptr(arena), + password.toRealmString(arena).ref, + Pointer.fromFunction(void_completion_callback), + completer.toGCHandle(), + nullptr, + )); }); return completer.future; } From 47f48a9d5cdfb15f24a8240395db79748bb24efe Mon Sep 17 00:00:00 2001 From: blagoev Date: Tue, 19 Apr 2022 10:54:22 +0300 Subject: [PATCH 51/91] EmailPasswordAuthProvider as standalone class rename AuthProvider enum to AnumProviderType --- lib/src/application.dart | 8 +------- lib/src/credentials.dart | 12 ++++++------ lib/src/realm_class.dart | 3 +-- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/lib/src/application.dart b/lib/src/application.dart index 51912bb8f..f10df1e3f 100644 --- a/lib/src/application.dart +++ b/lib/src/application.dart @@ -20,7 +20,6 @@ import 'dart:io'; import 'package:meta/meta.dart'; import 'native/realm_core.dart'; import 'configuration.dart'; -import 'email_password_provider.dart'; /// Specify if and how to persists user objects. /// {@category Application} @@ -115,13 +114,8 @@ class ApplicationConfiguration { class Application { final AppHandle _handle; final ApplicationConfiguration configuration; - late final EmailPasswordProvider _emailPasswordProvider; - Application(this.configuration) : _handle = realmCore.getApp(configuration) { - _emailPasswordProvider = EmailPasswordProvider(this); - } - - EmailPasswordProvider get emailPasswordProvider => _emailPasswordProvider; + Application(this.configuration) : _handle = realmCore.getApp(configuration); } extension ApplicationInternal on Application { diff --git a/lib/src/credentials.dart b/lib/src/credentials.dart index 7545f42a4..78b4c289c 100644 --- a/lib/src/credentials.dart +++ b/lib/src/credentials.dart @@ -22,11 +22,11 @@ import 'application.dart'; /// An enum containing all authentication providers. These have to be enabled manually for the application before they can be used. /// [Authentication Providers Docs](https://docs.mongodb.com/realm/authentication/providers/) /// {@category Application} -enum AuthProvider { - /// Mechanism for authenticating without credentials. +enum AuthProviderType { + /// For authenticating without credentials. anonymous, - /// Mechanism for authenticating with an email and a password. + /// For authenticating with an email and a password. emailPassword, } @@ -35,13 +35,13 @@ enum AuthProvider { class Credentials { late final RealmAppCredentialsHandle _handle; - final AuthProvider provider; + final AuthProviderType providerType; /// Returns a [Credentials] object that can be used to authenticate an anonymous user. /// [Anonymous Authentication Docs](https://docs.mongodb.com/realm/authentication/anonymous) Credentials.anonymous() : _handle = realmCore.createAppCredentialsAnonymous(), - provider = AuthProvider.anonymous; + providerType = AuthProviderType.anonymous; /// Returns a [Credentials] object that can be used to authenticate a user with their email and password. /// A user can login with email and password only after they have registered their account and verified their @@ -49,7 +49,7 @@ class Credentials { /// [Email/Password Authentication Docs](https://docs.mongodb.com/realm/authentication/email-password) Credentials.emailPassword(String email, String password) : _handle = realmCore.createAppCredentialsEmailPassword(email, password), - provider = AuthProvider.emailPassword; + providerType = AuthProviderType.emailPassword; } /// @nodoc diff --git a/lib/src/realm_class.dart b/lib/src/realm_class.dart index 495a0453b..da5f35af3 100644 --- a/lib/src/realm_class.dart +++ b/lib/src/realm_class.dart @@ -45,13 +45,12 @@ export 'package:realm_common/realm_common.dart' RealmPropertyType, ObjectId, UuidValue; -export 'email_password_provider.dart' show EmailPasswordProvider; export "configuration.dart" show Configuration, RealmSchema, SchemaObject; export 'list.dart' show RealmList, RealmListOfObject, RealmListChanges; export 'realm_object.dart' show RealmEntity, RealmException, RealmObject, RealmObjectChanges; export 'realm_property.dart'; export 'results.dart' show RealmResults, RealmResultsChanges; -export 'credentials.dart' show Credentials, AuthProvider, EmailPasswordProvider; +export 'credentials.dart' show Credentials, AuthProvider, EmailPasswordAuthProvider; /// A [Realm] instance represents a `Realm` database. /// From fedba5a091d839c62b3632e0b14688f076d23485 Mon Sep 17 00:00:00 2001 From: blagoev Date: Tue, 19 Apr 2022 11:09:35 +0300 Subject: [PATCH 52/91] support EmailPassword confirm user --- CHANGELOG.md | 3 ++- lib/src/credentials.dart | 9 +++++++-- lib/src/native/realm_core.dart | 17 +++++++++++++++- lib/src/realm_class.dart | 2 +- test/credentials_test.dart | 4 ++-- test/email_password_provider_test.dart | 28 -------------------------- 6 files changed, 28 insertions(+), 35 deletions(-) delete mode 100644 test/email_password_provider_test.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index ce6036abf..672fbe7fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,8 @@ x.x.x Release notes (yyyy-MM-dd) * Support should realm compact on open callback `Configuration.shouldCompactCallback` as option when configuring a Realm to determine if it should be compacted before being returned. ([#466](https://github.com/realm/realm-dart/pull/466/)) * 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 user registration with EmailPassword authentication provider. ([#452](https://github.com/realm/realm-dart/pull/452)) +* Support EmailPassword register user. ([#452](https://github.com/realm/realm-dart/pull/452)) +* Support EmailPassowrd confirm user. ### 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)) diff --git a/lib/src/credentials.dart b/lib/src/credentials.dart index 78b4c289c..4c5a897ab 100644 --- a/lib/src/credentials.dart +++ b/lib/src/credentials.dart @@ -72,7 +72,12 @@ class EmailPasswordAuthProvider { /// The [password] to associate with the email. The password must be between 6 and 128 characters long. /// /// Successful completion indicates that the user has been created on the server and can now be logged in with [Credentials.emailPassword()]. - Future registerUser(String email, String password) async { - return realmCore.appEmailPasswordRegisterUser(application, email, password); + Future registerUser(String email, String password) { + return realmCore.emailPasswordRegisterUser(application, email, password); + } + + /// Confirms a user with the given token and token id. These are typically included in the registration email. + Future confirmUser(String token, String tokenId) { + return realmCore.emailPasswordConfirmUser(application, token, tokenId); } } diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index ce04c549c..181504f34 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -866,7 +866,7 @@ class _RealmCore { completer.complete(); } - Future appEmailPasswordRegisterUser(Application application, String email, String password) { + Future emailPasswordRegisterUser(Application application, String email, String password) { final completer = Completer(); using((arena) { _realmLib.invokeGetBool(() => _realmLib.realm_app_email_password_provider_client_register_email( @@ -880,6 +880,21 @@ class _RealmCore { }); return completer.future; } + + Future emailPasswordConfirmUser(Application application, String token, String tokenId) { + final completer = Completer(); + using((arena) { + _realmLib.invokeGetBool(() => _realmLib.realm_app_email_password_provider_client_confirm_user( + application.handle._pointer, + token.toUtf8Ptr(arena), + tokenId.toUtf8Ptr(arena), + Pointer.fromFunction(void_completion_callback), + completer.toGCHandle(), + nullptr, + )); + }); + return completer.future; + } } class LastError { diff --git a/lib/src/realm_class.dart b/lib/src/realm_class.dart index da5f35af3..7f7d8c614 100644 --- a/lib/src/realm_class.dart +++ b/lib/src/realm_class.dart @@ -50,7 +50,7 @@ export 'list.dart' show RealmList, RealmListOfObject, RealmListChanges; export 'realm_object.dart' show RealmEntity, RealmException, RealmObject, RealmObjectChanges; export 'realm_property.dart'; export 'results.dart' show RealmResults, RealmResultsChanges; -export 'credentials.dart' show Credentials, AuthProvider, EmailPasswordAuthProvider; +export 'credentials.dart' show Credentials, AuthProviderType, EmailPasswordAuthProvider; /// A [Realm] instance represents a `Realm` database. /// diff --git a/test/credentials_test.dart b/test/credentials_test.dart index dc7c98323..73cd12675 100644 --- a/test/credentials_test.dart +++ b/test/credentials_test.dart @@ -29,11 +29,11 @@ Future main([List? args]) async { test('ApplicationCredentials anonymous', () { final credentials = Credentials.anonymous(); - expect(credentials.provider, AuthProvider.anonymous); + expect(credentials.providerType, AuthProviderType.anonymous); }); test('ApplicationCredentials email/password', () { final credentials = Credentials.emailPassword("test@email.com", "000000"); - expect(credentials.provider, AuthProvider.emailPassword); + expect(credentials.providerType, AuthProviderType.emailPassword); }); } diff --git a/test/email_password_provider_test.dart b/test/email_password_provider_test.dart deleted file mode 100644 index 7e9c85c30..000000000 --- a/test/email_password_provider_test.dart +++ /dev/null @@ -1,28 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// 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 '../lib/realm.dart'; -import 'test.dart'; - -Future main([List? args]) async { - print("Current PID $pid"); - - await setupTests(args); - -} From 9f2fb6d595ceba584ec390ff684ecc838ca26013 Mon Sep 17 00:00:00 2001 From: blagoev Date: Tue, 19 Apr 2022 11:11:58 +0300 Subject: [PATCH 53/91] update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 672fbe7fa..2a6ee4033 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ x.x.x Release notes (yyyy-MM-dd) * 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 EmailPassword register user. ([#452](https://github.com/realm/realm-dart/pull/452)) -* Support EmailPassowrd confirm user. +* Support EmailPassowrd confirm user. ([#478](https://github.com/realm/realm-dart/pull/478)) ### 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 e389fc1fd4d2bdfc9aff05a4d3f51a69f4019322 Mon Sep 17 00:00:00 2001 From: blagoev Date: Tue, 19 Apr 2022 11:15:52 +0300 Subject: [PATCH 54/91] support resend user confirm --- lib/src/credentials.dart | 5 +++++ lib/src/native/realm_core.dart | 14 ++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/lib/src/credentials.dart b/lib/src/credentials.dart index 4c5a897ab..b1b09217c 100644 --- a/lib/src/credentials.dart +++ b/lib/src/credentials.dart @@ -80,4 +80,9 @@ class EmailPasswordAuthProvider { Future confirmUser(String token, String tokenId) { return realmCore.emailPasswordConfirmUser(application, token, tokenId); } + + /// Resends the confirmation email for a user to the given email. + Future resendUserConfirmation(String email) { + return realmCore.emailPasswordResendUserConfirmation(application, email); + } } diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index 181504f34..57d796a3a 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -895,6 +895,20 @@ class _RealmCore { }); return completer.future; } + + Future emailPasswordResendUserConfirmation(Application application, String email) { + final completer = Completer(); + using((arena) { + _realmLib.invokeGetBool(() => _realmLib.realm_app_email_password_provider_client_resend_confirmation_email( + application.handle._pointer, + email.toUtf8Ptr(arena), + Pointer.fromFunction(void_completion_callback), + completer.toGCHandle(), + nullptr, + )); + }); + return completer.future; + } } class LastError { From d3f06ee8aaba4fa7cb9eaa2b76e984a0fe8bbf1e Mon Sep 17 00:00:00 2001 From: blagoev Date: Tue, 19 Apr 2022 11:21:03 +0300 Subject: [PATCH 55/91] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a6ee4033..5f4a3e371 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ x.x.x Release notes (yyyy-MM-dd) * Support Uuid type. ([#470](https://github.com/realm/realm-dart/pull/470)) * Support EmailPassword register user. ([#452](https://github.com/realm/realm-dart/pull/452)) * Support EmailPassowrd confirm user. ([#478](https://github.com/realm/realm-dart/pull/478)) +* Support EmailPassowrd resend user confirmation email. ([#479](https://github.com/realm/realm-dart/pull/479)) ### 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 6f445d5190f8ed1a18aa582023b64a50e2ba336e Mon Sep 17 00:00:00 2001 From: blagoev Date: Tue, 19 Apr 2022 11:26:00 +0300 Subject: [PATCH 56/91] support reset password --- lib/src/credentials.dart | 5 +++++ lib/src/native/realm_core.dart | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/lib/src/credentials.dart b/lib/src/credentials.dart index b1b09217c..3a07f2be5 100644 --- a/lib/src/credentials.dart +++ b/lib/src/credentials.dart @@ -85,4 +85,9 @@ class EmailPasswordAuthProvider { Future resendUserConfirmation(String email) { return realmCore.emailPasswordResendUserConfirmation(application, email); } + + /// Completes the reset password procedure by providing the desired new password. + Future resetPassword(String password, String token, String tokenId) { + return realmCore.emailPasswordResetPassword(application, password, token, tokenId); + } } diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index 57d796a3a..59a620357 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -909,6 +909,22 @@ class _RealmCore { }); return completer.future; } + + Future emailPasswordResetPassword(Application application, String password, String token, String tokenId) { + final completer = Completer(); + using((arena) { + _realmLib.invokeGetBool(() => _realmLib.realm_app_email_password_provider_client_reset_password( + application.handle._pointer, + password.toRealmString(arena).ref, + token.toUtf8Ptr(arena), + tokenId.toUtf8Ptr(arena), + Pointer.fromFunction(void_completion_callback), + completer.toGCHandle(), + nullptr, + )); + }); + return completer.future; + } } class LastError { From 5e91ea93c04cc97965903f09920e1eb4318a098d Mon Sep 17 00:00:00 2001 From: blagoev Date: Tue, 19 Apr 2022 11:27:33 +0300 Subject: [PATCH 57/91] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f4a3e371..9d865d47f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ x.x.x Release notes (yyyy-MM-dd) * Support EmailPassword register user. ([#452](https://github.com/realm/realm-dart/pull/452)) * Support EmailPassowrd confirm user. ([#478](https://github.com/realm/realm-dart/pull/478)) * Support EmailPassowrd resend user confirmation email. ([#479](https://github.com/realm/realm-dart/pull/479)) +* Support EmailPassowrd reset password. ([#480](https://github.com/realm/realm-dart/pull/480)) ### 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 6cc233b8b56858c108a0a057aa59e0f865209792 Mon Sep 17 00:00:00 2001 From: blagoev Date: Tue, 19 Apr 2022 11:27:58 +0300 Subject: [PATCH 58/91] fix type in changelog --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d865d47f..356b1919c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,9 +18,9 @@ x.x.x Release notes (yyyy-MM-dd) * 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 EmailPassword register user. ([#452](https://github.com/realm/realm-dart/pull/452)) -* Support EmailPassowrd confirm user. ([#478](https://github.com/realm/realm-dart/pull/478)) -* Support EmailPassowrd resend user confirmation email. ([#479](https://github.com/realm/realm-dart/pull/479)) -* Support EmailPassowrd reset password. ([#480](https://github.com/realm/realm-dart/pull/480)) +* 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)) +* Support EmailPassword reset password. ([#480](https://github.com/realm/realm-dart/pull/480)) ### 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 53393ea4d3f310507c969c1a8c696d10cd1a5de0 Mon Sep 17 00:00:00 2001 From: blagoev Date: Tue, 19 Apr 2022 11:33:29 +0300 Subject: [PATCH 59/91] support reset password email --- lib/src/credentials.dart | 9 +++++++-- lib/src/native/realm_core.dart | 16 +++++++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/lib/src/credentials.dart b/lib/src/credentials.dart index 3a07f2be5..4a9022362 100644 --- a/lib/src/credentials.dart +++ b/lib/src/credentials.dart @@ -87,7 +87,12 @@ class EmailPasswordAuthProvider { } /// Completes the reset password procedure by providing the desired new password. - Future resetPassword(String password, String token, String tokenId) { - return realmCore.emailPasswordResetPassword(application, password, token, tokenId); + Future completeResetPassword(String password, String token, String tokenId) { + return realmCore.emailPasswordCompleteResetPassword(application, password, token, tokenId); + } + + /// Sends a password reset email. + Future resetPassword(String email) { + return realmCore.emailPasswordResetPassword(application, email); } } diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index 59a620357..13e4d6678 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -910,7 +910,7 @@ class _RealmCore { return completer.future; } - Future emailPasswordResetPassword(Application application, String password, String token, String tokenId) { + Future emailPasswordCompleteResetPassword(Application application, String password, String token, String tokenId) { final completer = Completer(); using((arena) { _realmLib.invokeGetBool(() => _realmLib.realm_app_email_password_provider_client_reset_password( @@ -925,6 +925,20 @@ class _RealmCore { }); return completer.future; } + + Future emailPasswordResetPassword(Application application, String email) { + final completer = Completer(); + using((arena) { + _realmLib.invokeGetBool(() => _realmLib.realm_app_email_password_provider_client_send_reset_password_email( + application.handle._pointer, + email.toUtf8Ptr(arena), + Pointer.fromFunction(void_completion_callback), + completer.toGCHandle(), + nullptr, + )); + }); + return completer.future; + } } class LastError { From 17d6baf458efa3efdb7034d617d2c2f38daa6b53 Mon Sep 17 00:00:00 2001 From: blagoev Date: Tue, 19 Apr 2022 11:34:34 +0300 Subject: [PATCH 60/91] update changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 356b1919c..542748899 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,8 @@ x.x.x Release notes (yyyy-MM-dd) * 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)) -* Support EmailPassword reset password. ([#480](https://github.com/realm/realm-dart/pull/480)) +* Support EmailPassword complete reset password. ([#480](https://github.com/realm/realm-dart/pull/480)) +* Support EmailPassword reset password. ([#481](https://github.com/realm/realm-dart/pull/481)) ### 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 7b2d20b23eef54334bb741595386ea7fc8637754 Mon Sep 17 00:00:00 2001 From: blagoev Date: Tue, 19 Apr 2022 11:53:51 +0300 Subject: [PATCH 61/91] support calling custom reset password functions --- lib/src/credentials.dart | 5 +++++ lib/src/native/realm_core.dart | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/lib/src/credentials.dart b/lib/src/credentials.dart index 4a9022362..84981be75 100644 --- a/lib/src/credentials.dart +++ b/lib/src/credentials.dart @@ -95,4 +95,9 @@ class EmailPasswordAuthProvider { Future resetPassword(String email) { return realmCore.emailPasswordResetPassword(application, email); } + + /// Calls the reset password function, configured on the server. + Future callResetPasswordFunction(String email, String password, String functionArgsAsJSON) { + return realmCore.emailPasswordCallResetPasswordFunction(application, email, password, functionArgsAsJSON); + } } diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index 13e4d6678..31ee569b0 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -939,6 +939,22 @@ class _RealmCore { }); return completer.future; } + + Future emailPasswordCallResetPasswordFunction(Application application, String email, String password, String argsAsJSON) { + final completer = Completer(); + using((arena) { + _realmLib.invokeGetBool(() => _realmLib.realm_app_email_password_provider_client_call_reset_password_function( + application.handle._pointer, + email.toUtf8Ptr(arena), + password.toRealmString(arena).ref, + argsAsJSON.toUtf8Ptr(arena), + Pointer.fromFunction(void_completion_callback), + completer.toGCHandle(), + nullptr, + )); + }); + return completer.future; + } } class LastError { From 07717fc30c2daa46826b3b76a34f501778190cc9 Mon Sep 17 00:00:00 2001 From: blagoev Date: Tue, 19 Apr 2022 11:56:15 +0300 Subject: [PATCH 62/91] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 542748899..7b966e182 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ x.x.x Release notes (yyyy-MM-dd) * Support EmailPassword resend user confirmation email. ([#479](https://github.com/realm/realm-dart/pull/479)) * Support EmailPassword complete reset password. ([#480](https://github.com/realm/realm-dart/pull/480)) * Support EmailPassword reset password. ([#481](https://github.com/realm/realm-dart/pull/481)) +* Support EmailPassword calling custom reset password functions. ([#482](https://github.com/realm/realm-dart/pull/482)) ### 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 9fc1e1b3e519ffb4e30cb94105820e6f92d818d5 Mon Sep 17 00:00:00 2001 From: blagoev Date: Wed, 20 Apr 2022 18:41:46 +0300 Subject: [PATCH 63/91] support weak and persistent handles change user login to use persistent handle for the completer object --- lib/src/native/realm_bindings.dart | 62 +++++++++++++++++++++++++----- lib/src/native/realm_core.dart | 35 ++++++++++------- src/realm_dart.cpp | 41 +++++++++++++------- src/realm_dart.h | 10 +++-- 4 files changed, 109 insertions(+), 39 deletions(-) diff --git a/lib/src/native/realm_bindings.dart b/lib/src/native/realm_bindings.dart index 9b9fe6051..a836f64d1 100644 --- a/lib/src/native/realm_bindings.dart +++ b/lib/src/native/realm_bindings.dart @@ -7449,34 +7449,76 @@ class RealmLibrary { late final _realm_delete_finalizable = _realm_delete_finalizablePtr .asFunction(); - ffi.Pointer object_to_gc_handle( + ffi.Pointer object_to_weak_handle( Object handle, ) { - return _object_to_gc_handle( + return _object_to_weak_handle( handle, ); } - late final _object_to_gc_handlePtr = + late final _object_to_weak_handlePtr = _lookup Function(ffi.Handle)>>( - 'object_to_gc_handle'); - late final _object_to_gc_handle = _object_to_gc_handlePtr + 'object_to_weak_handle'); + late final _object_to_weak_handle = _object_to_weak_handlePtr .asFunction Function(Object)>(); - Object gc_handle_to_object( + Object weak_handle_to_object( ffi.Pointer handle, ) { - return _gc_handle_to_object( + return _weak_handle_to_object( handle, ); } - late final _gc_handle_to_objectPtr = + late final _weak_handle_to_objectPtr = _lookup)>>( - 'gc_handle_to_object'); - late final _gc_handle_to_object = _gc_handle_to_objectPtr + 'weak_handle_to_object'); + late final _weak_handle_to_object = _weak_handle_to_objectPtr .asFunction)>(); + ffi.Pointer object_to_persistent_handle( + Object handle, + ) { + return _object_to_persistent_handle( + handle, + ); + } + + late final _object_to_persistent_handlePtr = + _lookup Function(ffi.Handle)>>( + 'object_to_persistent_handle'); + late final _object_to_persistent_handle = _object_to_persistent_handlePtr + .asFunction Function(Object)>(); + + Object persistent_handle_to_object( + ffi.Pointer handle, + ) { + return _persistent_handle_to_object( + handle, + ); + } + + late final _persistent_handle_to_objectPtr = + _lookup)>>( + 'persistent_handle_to_object'); + late final _persistent_handle_to_object = _persistent_handle_to_objectPtr + .asFunction)>(); + + void delete_persistent_handle( + ffi.Pointer handle, + ) { + return _delete_persistent_handle( + handle, + ); + } + + late final _delete_persistent_handlePtr = + _lookup)>>( + 'delete_persistent_handle'); + late final _delete_persistent_handle = _delete_persistent_handlePtr + .asFunction)>(); + ffi.Pointer realm_dart_create_scheduler( int isolateId, int port, diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index 3960b6f90..ba5fc6391 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -56,9 +56,13 @@ class _RealmCore { static _RealmCore? _instance; late final int isolateKey; + late final Pointer)>> _deletePersistentHandlePtr; + _RealmCore._() { final lib = initRealm(); _realmLib = RealmLibrary(lib); + + _deletePersistentHandlePtr = lib.lookup)>>('delete_persistent_handle'); } factory _RealmCore() { @@ -164,12 +168,13 @@ class _RealmCore { } if (config.initialDataCallback != null) { - _realmLib.realm_config_set_data_initialization_function(configHandle._pointer, Pointer.fromFunction(initial_data_callback, FALSE), config.toGCHandle()); + _realmLib.realm_config_set_data_initialization_function( + configHandle._pointer, Pointer.fromFunction(initial_data_callback, FALSE), config.toWeakHandle()); } if (config.shouldCompactCallback != null) { _realmLib.realm_config_set_should_compact_on_launch_function( - configHandle._pointer, Pointer.fromFunction(should_compact_callback, 0), config.toGCHandle()); + configHandle._pointer, Pointer.fromFunction(should_compact_callback, 0), config.toWeakHandle()); } return configHandle; @@ -609,7 +614,7 @@ class _RealmCore { RealmNotificationTokenHandle subscribeResultsNotifications(RealmResultsHandle handle, NotificationsController controller, SchedulerHandle schedulerHandle) { final pointer = _realmLib.invokeGetPointer(() => _realmLib.realm_results_add_notification_callback( handle._pointer, - controller.toGCHandle(), + controller.toWeakHandle(), nullptr, nullptr, Pointer.fromFunction(collection_change_callback), @@ -623,7 +628,7 @@ class _RealmCore { RealmNotificationTokenHandle subscribeListNotifications(RealmListHandle handle, NotificationsController controller, SchedulerHandle schedulerHandle) { final pointer = _realmLib.invokeGetPointer(() => _realmLib.realm_list_add_notification_callback( handle._pointer, - controller.toGCHandle(), + controller.toWeakHandle(), nullptr, nullptr, Pointer.fromFunction(collection_change_callback), @@ -637,7 +642,7 @@ class _RealmCore { RealmNotificationTokenHandle subscribeObjectNotifications(RealmObjectHandle handle, NotificationsController controller, SchedulerHandle schedulerHandle) { final pointer = _realmLib.invokeGetPointer(() => _realmLib.realm_object_add_notification_callback( handle._pointer, - controller.toGCHandle(), + controller.toWeakHandle(), nullptr, nullptr, Pointer.fromFunction(object_change_callback), @@ -706,7 +711,7 @@ class _RealmCore { RealmHttpTransportHandle createHttpTransport(HttpClient httpClient) { return RealmHttpTransportHandle._(_realmLib.realm_http_transport_new( Pointer.fromFunction(request_callback), - httpClient.toGCHandle(), + httpClient.toWeakHandle(), nullptr, )); } @@ -850,7 +855,7 @@ class _RealmCore { } static void _logInCallback(Pointer userdata, Pointer user, Pointer error) { - final Completer? completer = userdata.toObject(); + final Completer? completer = userdata.toObject(true); if (completer == null) { return; } @@ -877,8 +882,8 @@ class _RealmCore { application.handle._pointer, credentials.handle._pointer, Pointer.fromFunction(_logInCallback), - completer.toGCHandle(), - nullptr, + completer.toPersistentHandle(), + _deletePersistentHandlePtr, ), "Login failed"); return completer.future; @@ -1160,10 +1165,10 @@ extension on Pointer { } extension on Pointer { - T? toObject() { + T? toObject([bool isPersistent = false]) { assert(this != nullptr, "Pointer is null"); - final object = _realmLib.gc_handle_to_object(this); + Object object = isPersistent ? _realmLib.persistent_handle_to_object(this) : _realmLib.weak_handle_to_object(this); assert(object is T, "$T expected"); if (object is! T) { @@ -1175,8 +1180,12 @@ extension on Pointer { } extension on Object { - Pointer toGCHandle() { - return _realmLib.object_to_gc_handle(this); + Pointer toWeakHandle() { + return _realmLib.object_to_weak_handle(this); + } + + Pointer toPersistentHandle() { + return _realmLib.object_to_persistent_handle(this); } } diff --git a/src/realm_dart.cpp b/src/realm_dart.cpp index 75a1b06a4..ba8c359bc 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) @@ -75,16 +75,16 @@ void dummy(void) { realm_app_credentials_new_anonymous(); realm_http_transport_new(nullptr, nullptr, nullptr); #if (ANDROID) - realm_android_dummy(); + realm_android_dummy(); #endif } -class GCHandle { +class WeakHandle { public: // 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)) { + WeakHandle(Dart_Handle handle, bool pers) : m_weakHandle(Dart_NewFinalizableHandle_DL(handle, this, 1, finalize_handle)) { } Dart_Handle value() { @@ -112,17 +112,32 @@ class GCHandle { */ static void finalize_handle(void* isolate_callback_data, void* peer) { - delete reinterpret_cast(peer); + delete reinterpret_cast(peer); } // TODO: HACK. Should be Dart_WeakPersistentHandle when hack removed Dart_FinalizableHandle m_weakHandle; }; -RLM_API void* object_to_gc_handle(Dart_Handle handle) { - return new GCHandle(handle, false); +RLM_API void* object_to_weak_handle(Dart_Handle handle) { + return new WeakHandle(handle, false); } -RLM_API Dart_Handle gc_handle_to_object(void* handle) { - return reinterpret_cast(handle)->value(); +RLM_API Dart_Handle weak_handle_to_object(void* handle) { + return reinterpret_cast(handle)->value(); } + +RLM_API void* object_to_persistent_handle(Dart_Handle handle) { + return reinterpret_cast(Dart_NewPersistentHandle_DL(handle)); +} + +RLM_API Dart_Handle persistent_handle_to_object(void* handle) { + Dart_PersistentHandle persistentHandle = reinterpret_cast(handle); + return Dart_HandleFromPersistent_DL(persistentHandle); +} + +RLM_API void delete_persistent_handle(void* handle) { + Dart_PersistentHandle persistentHandle = reinterpret_cast(handle); + Dart_DeletePersistentHandle_DL(persistentHandle); +} + diff --git a/src/realm_dart.h b/src/realm_dart.h index 5d368a9ff..dc4462669 100644 --- a/src/realm_dart.h +++ b/src/realm_dart.h @@ -28,8 +28,12 @@ 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* object_to_gc_handle(Dart_Handle handle); -RLM_API Dart_Handle gc_handle_to_object(void* handle); +RLM_API void* object_to_weak_handle(Dart_Handle handle); +RLM_API Dart_Handle weak_handle_to_object(void* handle); + +RLM_API void* object_to_persistent_handle(Dart_Handle handle); +RLM_API Dart_Handle persistent_handle_to_object(void* handle); +RLM_API void delete_persistent_handle(void* handle); + #endif // REALM_DART_H \ No newline at end of file From 0ed9ada733bcf4bec34ecd46fba71513adf9949d Mon Sep 17 00:00:00 2001 From: blagoev Date: Wed, 20 Apr 2022 18:55:55 +0300 Subject: [PATCH 64/91] add api doc --- lib/src/application.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/src/application.dart b/lib/src/application.dart index 931352bad..43bf6d98c 100644 --- a/lib/src/application.dart +++ b/lib/src/application.dart @@ -126,6 +126,7 @@ class Application { _handle = realmCore.getApp(_appConfigHandle, _syncClientConfigHandle); } + /// Logs in a user with the given credentials. Future logIn(Credentials credentials) async { var userHandle = await realmCore.logIn(this, credentials); return UserInternal.create(userHandle); From a94f2225bc4866fae1db524975a01358a46790e5 Mon Sep 17 00:00:00 2001 From: blagoev Date: Wed, 20 Apr 2022 19:03:27 +0300 Subject: [PATCH 65/91] add user api doc add application category --- lib/src/application.dart | 3 +++ lib/src/user.dart | 11 +++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/src/application.dart b/lib/src/application.dart index 43bf6d98c..4c332f873 100644 --- a/lib/src/application.dart +++ b/lib/src/application.dart @@ -38,6 +38,7 @@ enum MetadataPersistenceMode { @immutable /// A class exposing configuration options for an [Application] +/// {@category Application} class ApplicationConfiguration { /// The [appId] is the unique id that identifies the Realm application. final String appId; @@ -110,6 +111,7 @@ class ApplicationConfiguration { /// The [Application]] can be used to /// * Register uses and perform various user-related operations through authentication providers /// * Synchronize data between the local device and a remote Realm App with Synchronized Realms +/// {@category Application} class Application { late final AppHandle _handle; final ApplicationConfiguration configuration; @@ -133,6 +135,7 @@ class Application { } } +/// @nodoc extension ApplicationInternal on Application { AppHandle get handle => _handle; } diff --git a/lib/src/user.dart b/lib/src/user.dart index a5ec0647b..86e1716ac 100644 --- a/lib/src/user.dart +++ b/lib/src/user.dart @@ -18,12 +18,19 @@ import 'native/realm_core.dart'; +/// This class represents a user in a MongoDB Realm app. +/// A user can log in to the server and, if access is granted, it is possible to synchronize the local and the remote Realm. +/// Moreover, synchronization is halted when the user is logged out. It is possible to persist a user. By retrieving a user, there is no need to log in again. +/// Persisting a user between sessions, the user's credentials are stored +/// locally on the device, and should be treated as sensitive data. +/// {@category Application} class User { final UserHandle _handle; - User._(this._handle); + User._(this._handle); } +/// @nodoc extension UserInternal on User { static User create(UserHandle handle) => User._(handle); -} \ No newline at end of file +} From ab1f03e9c0b2e8e0361a743c6afee23186ae4dbf Mon Sep 17 00:00:00 2001 From: blagoev Date: Wed, 20 Apr 2022 19:04:08 +0300 Subject: [PATCH 66/91] revert build type for linux --- scripts/build-linux.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build-linux.sh b/scripts/build-linux.sh index c73b36f35..bb7754d91 100755 --- a/scripts/build-linux.sh +++ b/scripts/build-linux.sh @@ -10,7 +10,7 @@ mkdir -p build-linux pushd build-linux cmake -GNinja \ - -DCMAKE_BUILD_TYPE=Debug \ + -DCMAKE_BUILD_TYPE=MinSizeRel \ .. cmake --build . \ No newline at end of file From bcbc52239f0d521fcd6c184738a9873439326471 Mon Sep 17 00:00:00 2001 From: blagoev Date: Wed, 20 Apr 2022 19:08:09 +0300 Subject: [PATCH 67/91] add ctor api doc --- lib/src/application.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/src/application.dart b/lib/src/application.dart index 4c332f873..f40151c6e 100644 --- a/lib/src/application.dart +++ b/lib/src/application.dart @@ -121,6 +121,7 @@ class Application { // ignore: unused_field late final SyncClientConfigHandle _syncClientConfigHandle; + /// Create an app with a particular [AppConfiguration] Application(this.configuration) { _httpTransportHandle = realmCore.createHttpTransport(configuration.httpClient); _appConfigHandle = realmCore.createAppConfig(configuration, _httpTransportHandle); From d8effa6f8549b893c19b30b73ad979f971375ec2 Mon Sep 17 00:00:00 2001 From: blagoev Date: Wed, 20 Apr 2022 19:12:05 +0300 Subject: [PATCH 68/91] remove dead arg --- src/realm_dart.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/realm_dart.cpp b/src/realm_dart.cpp index ba8c359bc..c788f8f6b 100644 --- a/src/realm_dart.cpp +++ b/src/realm_dart.cpp @@ -84,7 +84,7 @@ class WeakHandle { // 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)) {} - WeakHandle(Dart_Handle handle, bool pers) : m_weakHandle(Dart_NewFinalizableHandle_DL(handle, this, 1, finalize_handle)) { + WeakHandle(Dart_Handle handle) : m_weakHandle(Dart_NewFinalizableHandle_DL(handle, this, 1, finalize_handle)) { } Dart_Handle value() { @@ -120,7 +120,7 @@ class WeakHandle { }; RLM_API void* object_to_weak_handle(Dart_Handle handle) { - return new WeakHandle(handle, false); + return new WeakHandle(handle); } RLM_API Dart_Handle weak_handle_to_object(void* handle) { From 188cf973ee53f38609094266dd285aab1f6ba46b Mon Sep 17 00:00:00 2001 From: blagoev Date: Wed, 20 Apr 2022 19:26:55 +0300 Subject: [PATCH 69/91] expose correct class, fix tests --- lib/src/credentials.dart | 2 +- lib/src/realm_class.dart | 2 +- test/credentials_test.dart | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/src/credentials.dart b/lib/src/credentials.dart index 78b4c289c..63a4cc9d8 100644 --- a/lib/src/credentials.dart +++ b/lib/src/credentials.dart @@ -35,7 +35,7 @@ enum AuthProviderType { class Credentials { late final RealmAppCredentialsHandle _handle; - final AuthProviderType providerType; + final AuthProviderType provider; /// Returns a [Credentials] object that can be used to authenticate an anonymous user. /// [Anonymous Authentication Docs](https://docs.mongodb.com/realm/authentication/anonymous) diff --git a/lib/src/realm_class.dart b/lib/src/realm_class.dart index da5f35af3..7f7d8c614 100644 --- a/lib/src/realm_class.dart +++ b/lib/src/realm_class.dart @@ -50,7 +50,7 @@ export 'list.dart' show RealmList, RealmListOfObject, RealmListChanges; export 'realm_object.dart' show RealmEntity, RealmException, RealmObject, RealmObjectChanges; export 'realm_property.dart'; export 'results.dart' show RealmResults, RealmResultsChanges; -export 'credentials.dart' show Credentials, AuthProvider, EmailPasswordAuthProvider; +export 'credentials.dart' show Credentials, AuthProviderType, EmailPasswordAuthProvider; /// A [Realm] instance represents a `Realm` database. /// diff --git a/test/credentials_test.dart b/test/credentials_test.dart index dc7c98323..48c0d1c2c 100644 --- a/test/credentials_test.dart +++ b/test/credentials_test.dart @@ -29,11 +29,11 @@ Future main([List? args]) async { test('ApplicationCredentials anonymous', () { final credentials = Credentials.anonymous(); - expect(credentials.provider, AuthProvider.anonymous); + expect(credentials.provider, AuthProviderType.anonymous); }); test('ApplicationCredentials email/password', () { final credentials = Credentials.emailPassword("test@email.com", "000000"); - expect(credentials.provider, AuthProvider.emailPassword); + expect(credentials.provider, AuthProviderType.emailPassword); }); } From 4b0afecd18233d553e2c3b514950a690bec42ae8 Mon Sep 17 00:00:00 2001 From: blagoev Date: Wed, 20 Apr 2022 19:37:23 +0300 Subject: [PATCH 70/91] fix build --- lib/src/credentials.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/credentials.dart b/lib/src/credentials.dart index 63a4cc9d8..98eec4bc0 100644 --- a/lib/src/credentials.dart +++ b/lib/src/credentials.dart @@ -41,7 +41,7 @@ class Credentials { /// [Anonymous Authentication Docs](https://docs.mongodb.com/realm/authentication/anonymous) Credentials.anonymous() : _handle = realmCore.createAppCredentialsAnonymous(), - providerType = AuthProviderType.anonymous; + provider = AuthProviderType.anonymous; /// Returns a [Credentials] object that can be used to authenticate a user with their email and password. /// A user can login with email and password only after they have registered their account and verified their @@ -49,7 +49,7 @@ class Credentials { /// [Email/Password Authentication Docs](https://docs.mongodb.com/realm/authentication/email-password) Credentials.emailPassword(String email, String password) : _handle = realmCore.createAppCredentialsEmailPassword(email, password), - providerType = AuthProviderType.emailPassword; + provider = AuthProviderType.emailPassword; } /// @nodoc From c8848bffc6f89dea49b917321ed99f334e1db0f4 Mon Sep 17 00:00:00 2001 From: blagoev Date: Wed, 20 Apr 2022 21:16:08 +0300 Subject: [PATCH 71/91] use persistent handle for register user call --- lib/src/native/realm_core.dart | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index ccc3cb245..a03befa9c 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -61,7 +61,7 @@ class _RealmCore { _RealmCore._() { final lib = initRealm(); _realmLib = RealmLibrary(lib); - + _deletePersistentHandlePtr = lib.lookup)>>('delete_persistent_handle'); } @@ -888,7 +888,6 @@ class _RealmCore { "Login failed"); return completer.future; } -} static void void_completion_callback(Pointer userdata, Pointer error) { final Completer? completer = userdata.toObject(); @@ -913,8 +912,8 @@ class _RealmCore { email.toUtf8Ptr(arena), password.toRealmString(arena).ref, Pointer.fromFunction(void_completion_callback), - completer.toGCHandle(), - nullptr, + completer.toPersistentHandle(), + _deletePersistentHandlePtr, )); }); return completer.future; From 577b79f93490487f7b4aaa866488b8dd8035c904 Mon Sep 17 00:00:00 2001 From: blagoev Date: Thu, 21 Apr 2022 12:21:52 +0300 Subject: [PATCH 72/91] await inside realm_core login rename to _deletePersistentHandleFuncPtr to be more clear what that is --- lib/src/native/realm_core.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index a03befa9c..60953adbe 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -56,13 +56,13 @@ class _RealmCore { static _RealmCore? _instance; late final int isolateKey; - late final Pointer)>> _deletePersistentHandlePtr; + late final Pointer)>> _deletePersistentHandleFuncPtr; _RealmCore._() { final lib = initRealm(); _realmLib = RealmLibrary(lib); - _deletePersistentHandlePtr = lib.lookup)>>('delete_persistent_handle'); + _deletePersistentHandleFuncPtr = lib.lookup)>>('delete_persistent_handle'); } factory _RealmCore() { @@ -883,10 +883,10 @@ class _RealmCore { credentials.handle._pointer, Pointer.fromFunction(_logInCallback), completer.toPersistentHandle(), - _deletePersistentHandlePtr, + _deletePersistentHandleFuncPtr, ), "Login failed"); - return completer.future; + return await completer.future; } static void void_completion_callback(Pointer userdata, Pointer error) { @@ -913,7 +913,7 @@ class _RealmCore { password.toRealmString(arena).ref, Pointer.fromFunction(void_completion_callback), completer.toPersistentHandle(), - _deletePersistentHandlePtr, + _deletePersistentHandleFuncPtr, )); }); return completer.future; From 59ef6cdba4b88692fe24f7f09a3ff22f3897e661 Mon Sep 17 00:00:00 2001 From: blagoev Date: Thu, 21 Apr 2022 12:26:06 +0300 Subject: [PATCH 73/91] revert to non async method --- lib/src/native/realm_core.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index 60953adbe..724123c3b 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -875,7 +875,7 @@ class _RealmCore { completer.complete(UserHandle._(userClone.cast())); } - Future logIn(Application application, Credentials credentials) async { + Future logIn(Application application, Credentials credentials) { final completer = Completer(); _realmLib.invokeGetBool( () => _realmLib.realm_app_log_in_with_credentials( @@ -886,7 +886,7 @@ class _RealmCore { _deletePersistentHandleFuncPtr, ), "Login failed"); - return await completer.future; + return completer.future; } static void void_completion_callback(Pointer userdata, Pointer error) { From 3438f38f0824a953217ddb773f5ef979cd1535ca Mon Sep 17 00:00:00 2001 From: blagoev Date: Thu, 21 Apr 2022 12:35:42 +0300 Subject: [PATCH 74/91] use persistent handle --- lib/src/native/realm_core.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index 23a2e6356..3bc5aa77a 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -927,8 +927,8 @@ class _RealmCore { token.toUtf8Ptr(arena), tokenId.toUtf8Ptr(arena), Pointer.fromFunction(void_completion_callback), - completer.toGCHandle(), - nullptr, + completer.toPersistentHandle(), + _deletePersistentHandleFuncPtr, )); }); return completer.future; From 5a1523e4f291202af3d5174dded248d3d348efbb Mon Sep 17 00:00:00 2001 From: blagoev Date: Thu, 21 Apr 2022 12:41:34 +0300 Subject: [PATCH 75/91] use named arg --- lib/src/native/realm_core.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index ba5fc6391..32927953b 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -855,7 +855,7 @@ class _RealmCore { } static void _logInCallback(Pointer userdata, Pointer user, Pointer error) { - final Completer? completer = userdata.toObject(true); + final Completer? completer = userdata.toObject(isPersistent: true); if (completer == null) { return; } @@ -1165,7 +1165,7 @@ extension on Pointer { } extension on Pointer { - T? toObject([bool isPersistent = false]) { + T? toObject({bool isPersistent = false}) { assert(this != nullptr, "Pointer is null"); Object object = isPersistent ? _realmLib.persistent_handle_to_object(this) : _realmLib.weak_handle_to_object(this); From ef5e89d18f5ce2801f502686360602392b9d7695 Mon Sep 17 00:00:00 2001 From: blagoev Date: Thu, 21 Apr 2022 12:50:18 +0300 Subject: [PATCH 76/91] rename methods remove dead env vars usage --- test/test.dart | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/test/test.dart b/test/test.dart index e3064f9bd..140a7cde4 100644 --- a/test/test.dart +++ b/test/test.dart @@ -103,7 +103,7 @@ void xtest(String? name, dynamic Function() testFunction) { Future setupTests(List? args) async { parseTestNameFromArguments(args); - await setupBaas(); + await setupServer(); setUp(() { final path = generateRandomRealmPath(); @@ -182,7 +182,7 @@ void parseTestNameFromArguments(List? arguments) { } } -Future setupBaas() async { +Future setupServer() async { final baasUrl = Platform.environment['BAAS_URL']; if (baasUrl == null) { return; @@ -199,21 +199,19 @@ Future setupBaas() async { } @isTest -Future syncTest( +Future serverTest( String name, FutureOr Function(ApplicationConfiguration configuration) testFunction, { String appName = 'flexible', dynamic skip, }) async { final url = Uri.tryParse(Platform.environment['BAAS_URL'] ?? 'https://realm-dev.mongodb.com'); - final apiKey = Platform.environment['BAAS_API_KEY']; - final projectId = Platform.environment['BAAS_PROJECT_ID']; if (skip == null) { - skip = url == null || apiKey == null || projectId == null; + skip = url == null; } else if (skip is bool) { - skip = skip || url == null || apiKey == null || projectId == null; + skip = skip || url == null; } test(name, () async { From 8c0deb40bb31d958e99d99f230a283ec9888fb33 Mon Sep 17 00:00:00 2001 From: blagoev Date: Thu, 21 Apr 2022 12:58:37 +0300 Subject: [PATCH 77/91] use string skip if url not present in test env --- lib/src/user.dart | 2 +- test/test.dart | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/src/user.dart b/lib/src/user.dart index 86e1716ac..3a738689c 100644 --- a/lib/src/user.dart +++ b/lib/src/user.dart @@ -19,7 +19,7 @@ import 'native/realm_core.dart'; /// This class represents a user in a MongoDB Realm app. -/// A user can log in to the server and, if access is granted, it is possible to synchronize the local and the remote Realm. +/// A user can log in to the server and, if access is granted, it is possible to synchronize the local Realm to MongoDB. /// Moreover, synchronization is halted when the user is logged out. It is possible to persist a user. By retrieving a user, there is no need to log in again. /// Persisting a user between sessions, the user's credentials are stored /// locally on the device, and should be treated as sensitive data. diff --git a/test/test.dart b/test/test.dart index 140a7cde4..dae0e290e 100644 --- a/test/test.dart +++ b/test/test.dart @@ -213,6 +213,10 @@ Future serverTest( else if (skip is bool) { skip = skip || url == null; } + + if (skip && url == null) { + skip = "Server URL not present"; + } test(name, () async { final app = baasApps[appName] ?? baasApps.values.first; From b37247dc9470a600037940fb6778bda1cec86150 Mon Sep 17 00:00:00 2001 From: blagoev Date: Thu, 21 Apr 2022 15:09:07 +0300 Subject: [PATCH 78/91] move creation of httpTransportHandle, appConfigHandle and sycnClientConfigHandle inside getApp since they are not needed anywhere else. This allows the GC to collect them when out of scope. Native instances are preserved by Core --- lib/src/application.dart | 15 +++------------ lib/src/native/realm_core.dart | 13 ++++++++----- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/lib/src/application.dart b/lib/src/application.dart index f40151c6e..180872607 100644 --- a/lib/src/application.dart +++ b/lib/src/application.dart @@ -113,21 +113,12 @@ class ApplicationConfiguration { /// * Synchronize data between the local device and a remote Realm App with Synchronized Realms /// {@category Application} class Application { - late final AppHandle _handle; + final AppHandle _handle; final ApplicationConfiguration configuration; - late final RealmHttpTransportHandle _httpTransportHandle; - // ignore: unused_field - late final AppConfigHandle _appConfigHandle; - // ignore: unused_field - late final SyncClientConfigHandle _syncClientConfigHandle; /// Create an app with a particular [AppConfiguration] - Application(this.configuration) { - _httpTransportHandle = realmCore.createHttpTransport(configuration.httpClient); - _appConfigHandle = realmCore.createAppConfig(configuration, _httpTransportHandle); - _syncClientConfigHandle = realmCore.createSyncClientConfig(configuration); - _handle = realmCore.getApp(_appConfigHandle, _syncClientConfigHandle); - } + Application(this.configuration) : + _handle = realmCore.getApp(configuration); /// Logs in a user with the given credentials. Future logIn(Credentials credentials) async { diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index 32927953b..41d9ab9de 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -61,7 +61,7 @@ class _RealmCore { _RealmCore._() { final lib = initRealm(); _realmLib = RealmLibrary(lib); - + _deletePersistentHandlePtr = lib.lookup)>>('delete_persistent_handle'); } @@ -668,7 +668,7 @@ class _RealmCore { }); } - AppConfigHandle createAppConfig(ApplicationConfiguration configuration, RealmHttpTransportHandle httpTransport) { + AppConfigHandle _createAppConfig(ApplicationConfiguration configuration, RealmHttpTransportHandle httpTransport) { return using((arena) { final app_id = configuration.appId.toUtf8Ptr(arena); final handle = AppConfigHandle._(_realmLib.realm_app_config_new(app_id, httpTransport._pointer)); @@ -708,7 +708,7 @@ class _RealmCore { }); } - RealmHttpTransportHandle createHttpTransport(HttpClient httpClient) { + RealmHttpTransportHandle _createHttpTransport(HttpClient httpClient) { return RealmHttpTransportHandle._(_realmLib.realm_http_transport_new( Pointer.fromFunction(request_callback), httpClient.toWeakHandle(), @@ -834,7 +834,7 @@ class _RealmCore { }); } - SyncClientConfigHandle createSyncClientConfig(ApplicationConfiguration configuration) { + SyncClientConfigHandle _createSyncClientConfig(ApplicationConfiguration configuration) { return using((arena) { final handle = SyncClientConfigHandle._(_realmLib.realm_sync_client_config_new()); @@ -849,7 +849,10 @@ class _RealmCore { }); } - AppHandle getApp(AppConfigHandle appConfigHandle, SyncClientConfigHandle syncClientConfigHandle) { + AppHandle getApp(ApplicationConfiguration configuration) { + final httpTransportHandle = _createHttpTransport(configuration.httpClient); + final appConfigHandle = _createAppConfig(configuration, httpTransportHandle); + final syncClientConfigHandle = _createSyncClientConfig(configuration); final realmAppPtr = _realmLib.invokeGetPointer(() => _realmLib.realm_app_get(appConfigHandle._pointer, syncClientConfigHandle._pointer)); return AppHandle._(realmAppPtr); } From 9c7127dbba081c71dfa444a8b83a382c41cd7e03 Mon Sep 17 00:00:00 2001 From: blagoev Date: Thu, 21 Apr 2022 15:14:01 +0300 Subject: [PATCH 79/91] rename TO BAAS --- test/application_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/application_test.dart b/test/application_test.dart index 2d91bd9c9..881931896 100644 --- a/test/application_test.dart +++ b/test/application_test.dart @@ -59,7 +59,7 @@ Future main([List? args]) async { expect(application.configuration, configuration); }); - syncTest('Application log in', (configuration) async { + baasTest('Application log in', (configuration) async { final application = Application(configuration); final credentials = Credentials.anonymous(); final user = await application.logIn(credentials); From 7cc27d0bd1e7f635762b107ef6c132311297dff4 Mon Sep 17 00:00:00 2001 From: blagoev Date: Thu, 21 Apr 2022 15:32:51 +0300 Subject: [PATCH 80/91] rename to BAAS --- test/test.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/test.dart b/test/test.dart index dae0e290e..c798465a7 100644 --- a/test/test.dart +++ b/test/test.dart @@ -103,7 +103,7 @@ void xtest(String? name, dynamic Function() testFunction) { Future setupTests(List? args) async { parseTestNameFromArguments(args); - await setupServer(); + await setupBaas(); setUp(() { final path = generateRandomRealmPath(); @@ -182,7 +182,7 @@ void parseTestNameFromArguments(List? arguments) { } } -Future setupServer() async { +Future setupBaas() async { final baasUrl = Platform.environment['BAAS_URL']; if (baasUrl == null) { return; @@ -199,7 +199,7 @@ Future setupServer() async { } @isTest -Future serverTest( +Future baasTest( String name, FutureOr Function(ApplicationConfiguration configuration) testFunction, { String appName = 'flexible', @@ -215,7 +215,7 @@ Future serverTest( } if (skip && url == null) { - skip = "Server URL not present"; + skip = "BAAS URL not present"; } test(name, () async { From ec217bbc005c4446fb4fd47849032617bd8e01dc Mon Sep 17 00:00:00 2001 From: blagoev Date: Thu, 21 Apr 2022 15:48:22 +0300 Subject: [PATCH 81/91] use non async login method --- lib/src/native/realm_core.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index 41d9ab9de..41fa77fdd 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -878,7 +878,7 @@ class _RealmCore { completer.complete(UserHandle._(userClone.cast())); } - Future logIn(Application application, Credentials credentials) async { + Future logIn(Application application, Credentials credentials) { final completer = Completer(); _realmLib.invokeGetBool( () => _realmLib.realm_app_log_in_with_credentials( From 6392968b64c149e89de3ac9035d0a1aca53fcbe1 Mon Sep 17 00:00:00 2001 From: blagoev Date: Thu, 21 Apr 2022 15:59:34 +0300 Subject: [PATCH 82/91] set skip to error if url is null --- test/test.dart | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/test/test.dart b/test/test.dart index c798465a7..1abcfe8c9 100644 --- a/test/test.dart +++ b/test/test.dart @@ -208,14 +208,10 @@ Future baasTest( final url = Uri.tryParse(Platform.environment['BAAS_URL'] ?? 'https://realm-dev.mongodb.com'); if (skip == null) { - skip = url == null; + skip = url == null ? "BAAS URL not present" : true; } else if (skip is bool) { - skip = skip || url == null; - } - - if (skip && url == null) { - skip = "BAAS URL not present"; + skip = skip || url == null ? "BAAS URL not present" : true; } test(name, () async { From 503996558ee83b9a88826adaf1a5f947da532949 Mon Sep 17 00:00:00 2001 From: blagoev Date: Thu, 21 Apr 2022 18:57:02 +0300 Subject: [PATCH 83/91] use persistent handle --- lib/src/native/realm_core.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index 62765e4b9..ac73be6b3 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -886,7 +886,7 @@ class _RealmCore { credentials.handle._pointer, Pointer.fromFunction(_logInCallback), completer.toPersistentHandle(), - _deletePersistentHandlePtr, + _deletePersistentHandleFuncPtr, ), "Login failed"); return completer.future; @@ -944,8 +944,8 @@ class _RealmCore { application.handle._pointer, email.toUtf8Ptr(arena), Pointer.fromFunction(void_completion_callback), - completer.toGCHandle(), - nullptr, + completer.toPersistentHandle(), + _deletePersistentHandleFuncPtr, )); }); return completer.future; From 3196b887c78fe47bccae67e4f38051921d80aaad Mon Sep 17 00:00:00 2001 From: blagoev Date: Thu, 21 Apr 2022 19:01:11 +0300 Subject: [PATCH 84/91] use persistent handle --- lib/src/native/realm_core.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index c41475e32..80db6dbe5 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -960,8 +960,8 @@ class _RealmCore { token.toUtf8Ptr(arena), tokenId.toUtf8Ptr(arena), Pointer.fromFunction(void_completion_callback), - completer.toGCHandle(), - nullptr, + completer.toPersistentHandle(), + _deletePersistentHandleFuncPtr, )); }); return completer.future; @@ -1243,7 +1243,7 @@ extension on Pointer { } extension on Pointer { - T? toObject([bool isPersistent = false]) { + T? toObject({bool isPersistent = false}) { assert(this != nullptr, "Pointer is null"); Object object = isPersistent ? _realmLib.persistent_handle_to_object(this) : _realmLib.weak_handle_to_object(this); From 2507a00ce5c655cef40c5d448ca33df59a28ec61 Mon Sep 17 00:00:00 2001 From: blagoev Date: Thu, 21 Apr 2022 19:05:15 +0300 Subject: [PATCH 85/91] fix --- lib/src/native/realm_core.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index ce13aa486..b9473ea00 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -886,7 +886,7 @@ class _RealmCore { credentials.handle._pointer, Pointer.fromFunction(_logInCallback), completer.toPersistentHandle(), - _deletePersistentHandlePtr, + _deletePersistentHandleFuncPtr, ), "Login failed"); return completer.future; @@ -1198,7 +1198,7 @@ extension on Pointer { } extension on Pointer { - T? toObject([bool isPersistent = false]) { + T? toObject({bool isPersistent = false}) { assert(this != nullptr, "Pointer is null"); Object object = isPersistent ? _realmLib.persistent_handle_to_object(this) : _realmLib.weak_handle_to_object(this); From 46a1dbb51f64dda482b3290abf1116d21a7b3ccb Mon Sep 17 00:00:00 2001 From: blagoev Date: Thu, 21 Apr 2022 19:23:12 +0300 Subject: [PATCH 86/91] remove dead file --- flutter/realm_flutter/tests/test_driver/realm_test.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/flutter/realm_flutter/tests/test_driver/realm_test.dart b/flutter/realm_flutter/tests/test_driver/realm_test.dart index e81ed6d18..e800f3f39 100644 --- a/flutter/realm_flutter/tests/test_driver/realm_test.dart +++ b/flutter/realm_flutter/tests/test_driver/realm_test.dart @@ -13,7 +13,6 @@ import '../test/list_test.dart' as list_tests; import '../test/results_test.dart' as results_tests; import '../test/credentials_test.dart' as credentials_tests; import '../test/application_test.dart' as application_tests; -import '../test/email_password_provider_test.dart' as email_password_provider_test; Future main(List args) async { final Completer completer = Completer(); From 622ad31b0d047599a90ddf1d86228c5b87a2866e Mon Sep 17 00:00:00 2001 From: blagoev Date: Thu, 21 Apr 2022 19:40:18 +0300 Subject: [PATCH 87/91] remove dead file --- flutter/realm_flutter/tests/test_driver/realm_test.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/flutter/realm_flutter/tests/test_driver/realm_test.dart b/flutter/realm_flutter/tests/test_driver/realm_test.dart index e81ed6d18..e800f3f39 100644 --- a/flutter/realm_flutter/tests/test_driver/realm_test.dart +++ b/flutter/realm_flutter/tests/test_driver/realm_test.dart @@ -13,7 +13,6 @@ import '../test/list_test.dart' as list_tests; import '../test/results_test.dart' as results_tests; import '../test/credentials_test.dart' as credentials_tests; import '../test/application_test.dart' as application_tests; -import '../test/email_password_provider_test.dart' as email_password_provider_test; Future main(List args) async { final Completer completer = Completer(); From 892d9c1236310bd8e7fa20293903030a668a60bb Mon Sep 17 00:00:00 2001 From: blagoev Date: Thu, 21 Apr 2022 20:03:58 +0300 Subject: [PATCH 88/91] remove dead code --- flutter/realm_flutter/tests/test_driver/realm_test.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/flutter/realm_flutter/tests/test_driver/realm_test.dart b/flutter/realm_flutter/tests/test_driver/realm_test.dart index e800f3f39..80ac1f1e4 100644 --- a/flutter/realm_flutter/tests/test_driver/realm_test.dart +++ b/flutter/realm_flutter/tests/test_driver/realm_test.dart @@ -25,7 +25,6 @@ Future main(List args) async { await results_tests.main(args); await credentials_tests.main(args); await application_tests.main(args); - await email_password_provider_test.main(args); tearDown(() { if (Invoker.current?.liveTest.state.result == test_api.Result.error || Invoker.current?.liveTest.state.result == test_api.Result.failure) { From 3aaf3978cb69cf41ace6149fa0d5a174a995c98f Mon Sep 17 00:00:00 2001 From: blagoev Date: Thu, 21 Apr 2022 21:29:09 +0300 Subject: [PATCH 89/91] use persistent handle --- lib/src/native/realm_core.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index 72de9184c..51fd1a4e2 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -974,8 +974,8 @@ class _RealmCore { application.handle._pointer, email.toUtf8Ptr(arena), Pointer.fromFunction(void_completion_callback), - completer.toGCHandle(), - nullptr, + completer.toPersistentHandle(), + _deletePersistentHandleFuncPtr, )); }); return completer.future; From c686868e8c54f6891c31ffdac1f28928078acc47 Mon Sep 17 00:00:00 2001 From: blagoev Date: Thu, 21 Apr 2022 21:30:22 +0300 Subject: [PATCH 90/91] use persistent handle --- lib/src/native/realm_core.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index afff52680..ea71ec00a 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -990,8 +990,8 @@ class _RealmCore { password.toRealmString(arena).ref, argsAsJSON.toUtf8Ptr(arena), Pointer.fromFunction(void_completion_callback), - completer.toGCHandle(), - nullptr, + completer.toPersistentHandle(), + _deletePersistentHandleFuncPtr, )); }); return completer.future; From f161d29d94babc3fddf3248015aeda381fdc74e5 Mon Sep 17 00:00:00 2001 From: blagoev Date: Fri, 22 Apr 2022 10:34:12 +0300 Subject: [PATCH 91/91] change functionArgs to a Map --- lib/src/credentials.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/src/credentials.dart b/lib/src/credentials.dart index 6adb6da6e..cbdc11333 100644 --- a/lib/src/credentials.dart +++ b/lib/src/credentials.dart @@ -16,6 +16,8 @@ // //////////////////////////////////////////////////////////////////////////////// +import 'dart:convert'; + import 'native/realm_core.dart'; import 'application.dart'; @@ -97,7 +99,7 @@ class EmailPasswordAuthProvider { } /// Calls the reset password function, configured on the server. - Future callResetPasswordFunction(String email, String password, String functionArgsAsJSON) { - return realmCore.emailPasswordCallResetPasswordFunction(application, email, password, functionArgsAsJSON); + Future callResetPasswordFunction(String email, String password, Map functionArgs) { + return realmCore.emailPasswordCallResetPasswordFunction(application, email, password, jsonEncode(functionArgs)); } }