Skip to content

Commit

Permalink
application login user (#469)
Browse files Browse the repository at this point in the history
* Add dart specific support for realm_http_transport_new
* Copy all info from realm_http_request before starting async request
* Added ApplicationConfiguration
* Added Application class with async logIn method (includes stubs for User and Credentials)
* Add support for http_transport
* Added ApplicationConfiguration
* Added Application class with async logIn method (includes stubs for User)
* Add baasTest function
* Fix header enumeration bug
* Use invokeGetBool to check the app_log_in_with_credentials result
* Support weak and persistent handles
* Change user login to use persistent handle for the completer object
* 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
* use non async login method
* set skip to error if url is null

Co-authored-by: Kasper Overgård Nielsen <[email protected]>
Co-authored-by: blagoev <[email protected]>
Co-authored-by: Kasper Overgård Nielsen <[email protected]>
  • Loading branch information
4 people authored Apr 21, 2022
1 parent d51867f commit b21f01f
Show file tree
Hide file tree
Showing 11 changed files with 263 additions and 53 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ 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 application login. ([#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))
Expand Down
15 changes: 14 additions & 1 deletion lib/src/application.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import 'dart:io';
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.
Expand All @@ -36,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;
Expand Down Expand Up @@ -108,13 +111,23 @@ 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 {
final AppHandle _handle;
final ApplicationConfiguration configuration;

Application(this.configuration) : _handle = realmCore.getApp(configuration);
/// Create an app with a particular [AppConfiguration]
Application(this.configuration) :
_handle = realmCore.getApp(configuration);

/// Logs in a user with the given credentials.
Future<User> logIn(Credentials credentials) async {
var userHandle = await realmCore.logIn(this, credentials);
return UserInternal.create(userHandle);
}
}

/// @nodoc
extension ApplicationInternal on Application {
AppHandle get handle => _handle;
}
62 changes: 52 additions & 10 deletions lib/src/native/realm_bindings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7449,34 +7449,76 @@ class RealmLibrary {
late final _realm_delete_finalizable = _realm_delete_finalizablePtr
.asFunction<void Function(Dart_FinalizableHandle, Object)>();

ffi.Pointer<ffi.Void> object_to_gc_handle(
ffi.Pointer<ffi.Void> 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<ffi.NativeFunction<ffi.Pointer<ffi.Void> 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<ffi.Pointer<ffi.Void> Function(Object)>();

Object gc_handle_to_object(
Object weak_handle_to_object(
ffi.Pointer<ffi.Void> 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<ffi.NativeFunction<ffi.Handle Function(ffi.Pointer<ffi.Void>)>>(
'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<Object Function(ffi.Pointer<ffi.Void>)>();

ffi.Pointer<ffi.Void> object_to_persistent_handle(
Object handle,
) {
return _object_to_persistent_handle(
handle,
);
}

late final _object_to_persistent_handlePtr =
_lookup<ffi.NativeFunction<ffi.Pointer<ffi.Void> Function(ffi.Handle)>>(
'object_to_persistent_handle');
late final _object_to_persistent_handle = _object_to_persistent_handlePtr
.asFunction<ffi.Pointer<ffi.Void> Function(Object)>();

Object persistent_handle_to_object(
ffi.Pointer<ffi.Void> handle,
) {
return _persistent_handle_to_object(
handle,
);
}

late final _persistent_handle_to_objectPtr =
_lookup<ffi.NativeFunction<ffi.Handle Function(ffi.Pointer<ffi.Void>)>>(
'persistent_handle_to_object');
late final _persistent_handle_to_object = _persistent_handle_to_objectPtr
.asFunction<Object Function(ffi.Pointer<ffi.Void>)>();

void delete_persistent_handle(
ffi.Pointer<ffi.Void> handle,
) {
return _delete_persistent_handle(
handle,
);
}

late final _delete_persistent_handlePtr =
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>>(
'delete_persistent_handle');
late final _delete_persistent_handle = _delete_persistent_handlePtr
.asFunction<void Function(ffi.Pointer<ffi.Void>)>();

ffi.Pointer<realm_scheduler_t> realm_dart_create_scheduler(
int isolateId,
int port,
Expand Down
91 changes: 70 additions & 21 deletions lib/src/native/realm_core.dart
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,13 @@ class _RealmCore {
static _RealmCore? _instance;
late final int isolateKey;

late final Pointer<NativeFunction<Void Function(Pointer<Void>)>> _deletePersistentHandlePtr;

_RealmCore._() {
final lib = initRealm();
_realmLib = RealmLibrary(lib);

_deletePersistentHandlePtr = lib.lookup<NativeFunction<Void Function(Pointer<Void>)>>('delete_persistent_handle');
}

factory _RealmCore() {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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),
Expand All @@ -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),
Expand All @@ -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),
Expand All @@ -663,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));
Expand Down Expand Up @@ -703,10 +708,10 @@ class _RealmCore {
});
}

RealmHttpTransportHandle createHttpTransport(HttpClient httpClient) {
RealmHttpTransportHandle _createHttpTransport(HttpClient httpClient) {
return RealmHttpTransportHandle._(_realmLib.realm_http_transport_new(
Pointer.fromFunction(request_callback),
httpClient.toGCHandle(),
httpClient.toWeakHandle(),
nullptr,
));
}
Expand Down Expand Up @@ -805,12 +810,13 @@ class _RealmCore {
responseRef.headers = arena<realm_http_header>(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++;
}
});

Expand All @@ -828,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());

Expand All @@ -844,11 +850,46 @@ 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);
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);
}

static void _logInCallback(Pointer<Void> userdata, Pointer<realm_user> user, Pointer<realm_app_error> error) {
final Completer<UserHandle>? completer = userdata.toObject(isPersistent: true);
if (completer == null) {
return;
}

if (error != nullptr) {
final message = error.ref.message.cast<Utf8>().toDartString();
completer.completeError(RealmException(message));
return;
}

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<UserHandle> logIn(Application application, Credentials credentials) {
final completer = Completer<UserHandle>();
_realmLib.invokeGetBool(
() => _realmLib.realm_app_log_in_with_credentials(
application.handle._pointer,
credentials.handle._pointer,
Pointer.fromFunction(_logInCallback),
completer.toPersistentHandle(),
_deletePersistentHandlePtr,
),
"Login failed");
return completer.future;
}
}

Expand Down Expand Up @@ -925,7 +966,7 @@ class RealmQueryHandle extends Handle<realm_query> {

class RealmNotificationTokenHandle extends Handle<realm_notification_token> {
bool released = false;
RealmNotificationTokenHandle._(Pointer<realm_notification_token> pointer) : super(pointer, 1 << 32);
RealmNotificationTokenHandle._(Pointer<realm_notification_token> pointer) : super(pointer, 32);

void release() {
if (released) {
Expand Down Expand Up @@ -966,6 +1007,10 @@ class AppHandle extends Handle<realm_app> {
AppHandle._(Pointer<realm_app> pointer) : super(pointer, 16);
}

class UserHandle extends Handle<realm_user> {
UserHandle._(Pointer<realm_user> pointer) : super(pointer, 24);
}

extension on List<int> {
Pointer<Int8> toInt8Ptr(Allocator allocator) {
return toUint8Ptr(allocator).cast();
Expand Down Expand Up @@ -1123,10 +1168,10 @@ extension on Pointer<IntPtr> {
}

extension on Pointer<Void> {
T? toObject<T extends Object>() {
T? toObject<T extends Object>({bool isPersistent = false}) {
assert(this != nullptr, "Pointer<Void> 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) {
Expand All @@ -1138,8 +1183,12 @@ extension on Pointer<Void> {
}

extension on Object {
Pointer<Void> toGCHandle() {
return _realmLib.object_to_gc_handle(this);
Pointer<Void> toWeakHandle() {
return _realmLib.object_to_weak_handle(this);
}

Pointer<Void> toPersistentHandle() {
return _realmLib.object_to_persistent_handle(this);
}
}

Expand Down
1 change: 1 addition & 0 deletions lib/src/realm_class.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ 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.
///
Expand Down
36 changes: 36 additions & 0 deletions lib/src/user.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
////////////////////////////////////////////////////////////////////////////////
//
// 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';

/// 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 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.
/// {@category Application}
class User {
final UserHandle _handle;

User._(this._handle);
}

/// @nodoc
extension UserInternal on User {
static User create(UserHandle handle) => User._(handle);
}
Loading

0 comments on commit b21f01f

Please sign in to comment.