Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Application class #446

Merged
merged 42 commits into from
Apr 13, 2022
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
453e018
Rollback Dart_WeakHandle finalizer hack
nielsenko Mar 14, 2022
8430d42
Use dart main/flutter beta in github actions.
nielsenko Mar 22, 2022
e847de2
Add dart specific support for realm_http_transport_new
nielsenko Mar 16, 2022
6a5c9c4
Wire up realm_core
nielsenko Mar 18, 2022
fca9182
Apply suggestions from code review
nielsenko Mar 28, 2022
435e484
Copy all info from realm_http_request before starting async request
nielsenko Mar 28, 2022
d7207d9
Added ApplicationConfiguration
nielsenko Mar 18, 2022
ea8d7ef
Added Application class with async logIn method (includes stubs for U…
nielsenko Mar 25, 2022
a54b1a8
Use common GCHandle class for userdata on all callbacks
nielsenko Mar 30, 2022
6ae70e2
Support hard gc handles as well, as suggested by Yavor. Ie. handles t…
nielsenko Apr 7, 2022
c81e0c0
Backport to dart 2.16 where we have to work around a finalization iss…
nielsenko Apr 1, 2022
6899dc8
Add support for http_transport
nielsenko Mar 18, 2022
9a17d45
Added ApplicationConfiguration
nielsenko Mar 18, 2022
73e2e4c
Update docs on public interface
nielsenko Apr 7, 2022
0baa62f
Address PR feedback
nielsenko Apr 11, 2022
20e95af
Added Application class with async logIn method (includes stubs for U…
nielsenko Apr 8, 2022
d63f205
Add testWithBass function
nielsenko Apr 8, 2022
0cdf4b7
Merge branch 'master' into realm_app_config
desistefanova Apr 11, 2022
b9cc163
Update CHANGELOG.md
desistefanova Apr 11, 2022
8448ffd
Merge branch 'realm_app_config' into realm_app
desistefanova Apr 11, 2022
7ff02ef
Commit after merge
desistefanova Apr 11, 2022
fc5e481
Fixes after merge
desistefanova Apr 11, 2022
c1cb35e
Merge branch 'realm_app'
desistefanova Apr 12, 2022
e07be97
Fixes after merge
desistefanova Apr 12, 2022
b7ddc96
Remove user and login
desistefanova Apr 12, 2022
938a168
size of handles
desistefanova Apr 12, 2022
ef644c5
workflow chanel = stable
desistefanova Apr 12, 2022
a132bf4
Delete realm_dart_http_transport
desistefanova Apr 12, 2022
4e15ea7
remove import dart:async from tests
desistefanova Apr 12, 2022
c453fde
Merge branch 'master' into realm_app
desistefanova Apr 12, 2022
348ed3e
Fixes after merge
desistefanova Apr 12, 2022
2778ff2
Fix after merge
desistefanova Apr 12, 2022
a359038
Merge branch 'master' into realm_app
desistefanova Apr 12, 2022
1489eee
Merge branch 'master' into realm_app
desistefanova Apr 12, 2022
9fd8fec
Fix after merge
desistefanova Apr 12, 2022
07be0ae
Fix after merge
desistefanova Apr 12, 2022
8c213f9
fixup application
blagoev Apr 12, 2022
a673a91
change default application dir
blagoev Apr 12, 2022
96324de
trigger actions
blagoev Apr 12, 2022
5b63103
trigger
blagoev Apr 12, 2022
7f00a6c
fix application config and app tests
blagoev Apr 12, 2022
e857860
change filesPath to current dir
blagoev Apr 12, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ x.x.x Release notes (yyyy-MM-dd)
* Support anonymous application credentials ([#443](https://github.com/realm/realm-dart/pull/443/))
* Added a property `Configuration.initialDataCallback`. This is a callback executed when a Realm file is first created and allows you to populate some initial data necessary for your application. ([#298](https://github.com/realm/realm-dart/issues/298))
* Support application configuration ([#306](https://github.com/realm/realm-dart/pull/306/))
* Support 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))
Expand Down
3 changes: 3 additions & 0 deletions flutter/realm_flutter/ios/Classes/RealmPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,11 @@ void dummy(void) {
realm_results_get_object(NULL, 0);
realm_list_size(NULL, 0);
realm_results_snapshot(NULL);
realm_http_transport_new(NULL, NULL, NULL);
realm_app_credentials_new_anonymous();
realm_app_config_new(NULL, NULL);
realm_app_config_new(NULL, NULL);
realm_sync_client_config_new();
}

@end
6 changes: 4 additions & 2 deletions flutter/realm_flutter/tests/test_driver/realm_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +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/credentials_test.dart' as credentials;
import '../test/credentials_test.dart' as credentials_tests;
import '../test/application_test.dart' as application_tests;

Future<String> main(List<String> args) async {
final Completer<String> completer = Completer<String>();
Expand All @@ -22,7 +23,8 @@ Future<String> main(List<String> 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_tests.main(args);

tearDown(() {
if (Invoker.current?.liveTest.state.result == test_api.Result.error || Invoker.current?.liveTest.state.result == test_api.Result.failure) {
Expand Down
13 changes: 13 additions & 0 deletions lib/src/application.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import 'dart:io';
import 'package:meta/meta.dart';
import 'native/realm_core.dart';

/// Specify if and how to persists user objects.
enum MetadataPersistenceMode {
Expand Down Expand Up @@ -98,3 +99,15 @@ class ApplicationConfiguration {
baseFilePath = baseFilePath ?? Directory.current,
httpClient = httpClient ?? HttpClient();
}

class Application {
final AppHandle _handle;
final ApplicationConfiguration configuration;

Application(this.configuration) : _handle = realmCore.getApp(configuration);
}

extension ApplicationInternal on Application {
AppHandle get handle => _handle;
}

100 changes: 100 additions & 0 deletions lib/src/application_configuration.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
////////////////////////////////////////////////////////////////////////////////
//
// 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<int>? 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();
}
47 changes: 44 additions & 3 deletions lib/src/native/realm_core.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -28,6 +29,7 @@ import 'package:ffi/ffi.dart' hide StringUtf8Pointer, StringUtf16Pointer;
import 'package:pub_semver/pub_semver.dart';

import '../application.dart';
import '../credentials.dart';
import '../collections.dart';
import '../init.dart';
import '../list.dart';
Expand Down Expand Up @@ -653,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));
Expand Down Expand Up @@ -810,6 +812,28 @@ class _RealmCore {
}
});
}

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 {
Expand Down Expand Up @@ -915,10 +939,27 @@ class RealmHttpTransportHandle extends Handle<realm_http_transport> {
}

class AppConfigHandle extends Handle<realm_app_config> {
AppConfigHandle._(Pointer<realm_app_config> pointer) : super(pointer, 256); // TODO: What should hint be?
AppConfigHandle._(Pointer<realm_app_config> pointer) : super(pointer, 8);
}

class SyncClientConfigHandle extends Handle<realm_sync_client_config> {
SyncClientConfigHandle._(Pointer<realm_sync_client_config> pointer) : super(pointer, 8);
}

class AppHandle extends Handle<realm_app> {
AppHandle._(Pointer<realm_app> pointer) : super(pointer, 16);
}

extension on List<int> {
Pointer<Uint8> toUint8Ptr(Allocator allocator) {
final nativeSize = length + 1;
final result = allocator<Uint8>(nativeSize);
final Uint8List native = result.asTypedList(nativeSize);
native.setAll(0, this); // copy
native.last = 0; // zero terminate
return result;
}

Pointer<Int8> toInt8Ptr(Allocator allocator) {
final nativeSize = length + 1;
final result = allocator<Uint8>(nativeSize);
Expand All @@ -932,7 +973,7 @@ extension on List<int> {
extension _StringEx on String {
Pointer<Int8> toUtf8Ptr(Allocator allocator) {
final units = utf8.encode(this);
return units.toInt8Ptr(allocator);
return units.toInt8Ptr(allocator).cast();
}

Pointer<realm_string_t> toRealmString(Allocator allocator) {
Expand Down
2 changes: 1 addition & 1 deletion lib/src/realm_class.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import 'realm_object.dart';
import 'results.dart';

// always expose with `show` to explicitly control the public API surface
export 'application.dart' show ApplicationConfiguration, MetadataPersistenceMode;
export 'application.dart' show ApplicationConfiguration, MetadataPersistenceMode, Application;
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;
Expand Down
1 change: 1 addition & 0 deletions src/realm_dart.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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_config_new(nullptr, nullptr);
realm_sync_client_config_new();
realm_app_credentials_new_anonymous();
realm_http_transport_new(nullptr, nullptr, nullptr);
#if (ANDROID)
Expand Down
48 changes: 48 additions & 0 deletions src/realm_dart_app.h
Original file line number Diff line number Diff line change
@@ -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
11 changes: 9 additions & 2 deletions test/application_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import 'test.dart';
Future<void> main([List<String>? args]) async {
print("Current PID $pid");

setupTests(args);
await setupTests(args);

test('ApplicationConfiguration can be created', () {
final a = ApplicationConfiguration('myapp');
Expand All @@ -47,8 +47,15 @@ Future<void> main([List<String>? 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 application = Application(configuration);
expect(application.configuration, configuration);
});
}
1 change: 1 addition & 0 deletions test/test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -195,3 +195,4 @@ Future<void> setupBaas() async {

baasApps.addAll(await client.getOrCreateApps());
}