Skip to content

Commit

Permalink
Expose dynamic Realm API
Browse files Browse the repository at this point in the history
Delete unneeded test

Update changelog
  • Loading branch information
nirinchev committed Jun 14, 2022
1 parent 4e266f8 commit 09168af
Show file tree
Hide file tree
Showing 12 changed files with 591 additions and 109 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
## vNext

**This project is in the Beta stage. The API should be quite stable, but occasional breaking changes may be made.**

### Breaking Changes
* Changed the name of `Configuration.schema` to `Configuration.schemaObjects` and changed its type to `Iterable<SchemaObject>`. You can now access the Realm's schema via the new `Realm.schema` property. [#495](https://github.com/realm/realm-dart/pull/495))

### Enhancements
* Added `DisconnectedSyncConfiguration` for opening a synchronized realm in a disconnected state. This configuration allows a synchronized realm to be opened by a secondary process, while a primary process handles synchronization. ([#621](https://github.com/realm/realm-dart/pull/621))
* Support better default paths on Flutter. ([#665](https://github.com/realm/realm-dart/pull/665))
* Support `Configuration.defaultRealmName` for setting the default realm name. ([#665](https://github.com/realm/realm-dart/pull/665))
* Support `Configuration.defaultRealmPath` for setting a custom default path for realms. ([#665](https://github.com/realm/realm-dart/pull/665))
* Support `Configuration.defaultStoragePath ` for getting the platform specific storage paths. ([#665](https://github.com/realm/realm-dart/pull/665))
* Expose an API for string-based access to the objects in the `Realm`. Those are primarily intended to be used during migrations, but are available at all times for advanced use cases. [#495](https://github.com/realm/realm-dart/pull/495))
* Expose an API for string-based access to the objects in the `Realm`. Those are primarily intended to be used during migrations, but are available at all times for advanced use cases. [#495](https://github.com/realm/realm-dart/pull/495))
* Added `Realm.schema` property exposing the Realm's schema as passed through the Configuration or read from disk. [#495](https://github.com/realm/realm-dart/pull/495))

## 0.3.1+beta (2022-06-07)

Expand Down Expand Up @@ -73,6 +80,7 @@
* Support SyncErrorHandler in FlexibleSyncConfiguration. ([#577](https://github.com/realm/realm-dart/pull/577))
* Support SyncClientResetHandler in FlexibleSyncConfiguration. ([#608](https://github.com/realm/realm-dart/pull/608))
* [Dart] Added `Realm.Shutdown` method to allow normal process exit in Dart applications. ([#617](https://github.com/realm/realm-dart/pull/617))
* Support logout user. ([#476](https://github.com/realm/realm-dart/pull/476))

### 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: 2 additions & 1 deletion generator/test/good_test_data/primary_key.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ class _IntPK {
@PrimaryKey()
late int id;
}

@RealmModel()
class _StringPK {
@PrimaryKey()
Expand All @@ -21,4 +22,4 @@ class _ObjectIdPK {
class _UuidPK {
@PrimaryKey()
late Uuid id;
}
}
29 changes: 12 additions & 17 deletions lib/src/configuration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,13 @@ typedef InitialDataCallback = void Function(Realm realm);
/// Configuration used to create a [Realm] instance
/// {@category Configuration}
abstract class Configuration {

/// The default realm filename to be used.
/// The default realm filename to be used.
static String get defaultRealmName => _path.basename(defaultRealmPath);
static set defaultRealmName(String name) => defaultRealmPath = _path.join(_path.dirname(defaultRealmPath), _path.basename(name));


/// A collection of [SchemaObject] that will be used to construct the
/// [RealmSchema] once the Realm is opened.
final Iterable<SchemaObject> schemaObjects;

/// The platform dependent path used to store realm files
///
Expand All @@ -63,23 +65,23 @@ abstract class Configuration {
/// On Dart standalone Windows, macOS and Linux this is the current directory.
static String get defaultStoragePath {
if (isFlutterPlatform) {
return realmCore.getAppDirectory();
return realmCore.getAppDirectory();
}

return Directory.current.path;
}

/// The platform dependent path to the default realm file.
///
///
/// If set it should contain the path and the name of the realm file. Ex. "~/mypath/myrealm.realm"
/// [defaultStoragePath] can be used to build this path.
static late String defaultRealmPath = _path.join(defaultStoragePath, 'default.realm');

Configuration._(
List<SchemaObject> schemaObjects, {
this.schemaObjects, {
String? path,
this.fifoFilesFallbackPath,
}) : schema = RealmSchema(schemaObjects) {
}) {
this.path = path ?? _path.join(_path.dirname(_defaultPath), _path.basename(defaultRealmName));
}

Expand All @@ -101,9 +103,6 @@ abstract class Configuration {
/// If omitted the [defaultPath] for the platform will be used.
late final String path;

/// The [RealmSchema] for this [Configuration]
final RealmSchema schema;

//TODO: Not supported yet.
// /// The key used to encrypt the entire [Realm].
// ///
Expand Down Expand Up @@ -286,7 +285,7 @@ extension FlexibleSyncConfigurationInternal on FlexibleSyncConfiguration {

/// [DisconnectedSyncConfiguration] is used to open [Realm] instances that are synchronized
/// with MongoDB Atlas, without establishing a connection to Atlas App Services. This allows
/// for the synchronized realm to be opened in multiple processes concurrently, as long as
/// for the synchronized realm to be opened in multiple processes concurrently, as long as
/// only one of them uses a [FlexibleSyncConfiguration] to sync changes.
/// {@category Configuration}
class DisconnectedSyncConfiguration extends Configuration {
Expand Down Expand Up @@ -332,12 +331,8 @@ class RealmSchema extends Iterable<SchemaObject> {
late final List<SchemaObject> _schema;

/// Initializes [RealmSchema] instance representing ```schemaObjects``` collection
RealmSchema(List<SchemaObject> schemaObjects) {
if (schemaObjects.isEmpty) {
throw RealmError("No schema specified");
}
_schema = schemaObjects;
RealmSchema(Iterable<SchemaObject> schemaObjects) {
_schema = schemaObjects.toList();
}
@override
Expand Down
21 changes: 16 additions & 5 deletions lib/src/list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,26 @@ import 'results.dart';
///
/// {@category Realm}
abstract class RealmList<T extends Object> with RealmEntity implements List<T> {
late final RealmObjectMetadata? _metadata;

/// Gets a value indicating whether this collection is still valid to use.
///
/// Indicates whether the [Realm] instance hasn't been closed,
/// if it represents a to-many relationship
/// and it's parent object hasn't been deleted.
bool get isValid;

factory RealmList._(RealmListHandle handle, Realm realm) => ManagedRealmList._(handle, realm);
factory RealmList._(RealmListHandle handle, Realm realm, RealmObjectMetadata? metadata) => ManagedRealmList._(handle, realm, metadata);
factory RealmList(Iterable<T> items) => UnmanagedRealmList(items);
}

class ManagedRealmList<T extends Object> extends collection.ListBase<T> with RealmEntity implements RealmList<T> {
final RealmListHandle _handle;

ManagedRealmList._(this._handle, Realm realm) {
@override
late final RealmObjectMetadata? _metadata;

ManagedRealmList._(this._handle, Realm realm, this._metadata) {
setRealm(realm);
}

Expand All @@ -67,7 +72,7 @@ class ManagedRealmList<T extends Object> extends collection.ListBase<T> with Rea
final value = realmCore.listGetElementAt(this, index);

if (value is RealmObjectHandle) {
return realm.createObject(T, value) as T;
return realm.createObject(T, value, _metadata!) as T;
}

return value as T;
Expand Down Expand Up @@ -100,6 +105,12 @@ class UnmanagedRealmList<T extends Object> extends collection.ListBase<T> with R
}
}

@override
RealmObjectMetadata? get _metadata => throw RealmException("Unmanaged lists don't have metadata associated with them.");

@override
set _metadata(RealmObjectMetadata? _) => throw RealmException("Unmanaged lists don't have metadata associated with them.");

@override
int get length => _unmanaged.length;

Expand Down Expand Up @@ -132,7 +143,7 @@ extension RealmListOfObject<T extends RealmObject> on RealmList<T> {
RealmResults<T> query(String query, [List<Object> arguments = const []]) {
final managedList = asManaged();
final handle = realmCore.queryList(managedList, query, arguments);
return RealmResultsInternal.create<T>(handle, realm);
return RealmResultsInternal.create<T>(handle, realm, _metadata);
}

/// Allows listening for changes when the contents of this collection changes.
Expand All @@ -149,7 +160,7 @@ extension RealmListInternal<T extends Object> on RealmList<T> {

RealmListHandle get handle => asManaged()._handle;

static RealmList<T> create<T extends Object>(RealmListHandle handle, Realm realm) => RealmList<T>._(handle, realm);
static RealmList<T> create<T extends Object>(RealmListHandle handle, Realm realm, RealmObjectMetadata? metadata) => RealmList<T>._(handle, realm, metadata);

static void setValue(RealmListHandle handle, Realm realm, int index, Object? value) {
if (index < 0) {
Expand Down
76 changes: 69 additions & 7 deletions lib/src/native/realm_core.dart
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,14 @@ class _RealmCore {

ConfigHandle _createConfig(Configuration config) {
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);
if (config.schemaObjects.isNotEmpty) {
final schemaHandle = _createSchema(config.schemaObjects);
_realmLib.realm_config_set_schema(configHandle._pointer, schemaHandle._pointer);
}

_realmLib.realm_config_set_path(configHandle._pointer, config.path.toUtf8Ptr(arena));
_realmLib.realm_config_set_scheduler(configHandle._pointer, scheduler.handle._pointer);

Expand Down Expand Up @@ -450,6 +453,53 @@ class _RealmCore {
return RealmHandle._(realmPtr);
}

RealmSchema readSchema(Realm realm) {
return using((Arena arena) {
final classKeys = _getValues<Uint32, int>(
arena,
(size) => arena<Uint32>(size),
(valuesPtr, size, outSize) => _realmLib.realm_get_class_keys(realm.handle._pointer, valuesPtr, size, outSize),
(values, index) => values.elementAt(index).value);

final result = classKeys.map((e) => _getSchemaForClassKey(realm, e, arena)).toList();
return RealmSchema(result);
});
}

SchemaObject _getSchemaForClassKey(Realm realm, int classKey, Arena arena) {
final classInfo = arena<realm_class_info>();
_realmLib.invokeGetBool(() => _realmLib.realm_get_class(realm.handle._pointer, classKey, classInfo));

final name = classInfo.ref.name.cast<Utf8>().toDartString();

final nativeProperties = _getValues<realm_property_info, realm_property_info>(
arena,
(size) => arena<realm_property_info>(size),
(valuesPtr, size, outSize) => _realmLib.realm_get_class_properties(realm.handle._pointer, classKey, valuesPtr, size, outSize),
(values, index) => values.elementAt(index).ref);

final properties = nativeProperties.map((e) => e.toSchemaProperty()).toList();

return SchemaObject(RealmObject, name, properties);
}

List<TReturn> _getValues<T extends NativeType, TReturn>(Arena arena, Pointer<T> Function(int size) createPtr,
bool Function(Pointer<T> valuesPtr, int size, Pointer<IntPtr> outSize) getValue, TReturn Function(Pointer<T> values, int index) getAtIndex) {
// TODO: this is a hack to get the size of the array and should not be needed once https://github.com/realm/realm-core/issues/5430 is addressed.
final valuesCount = arena<IntPtr>();
_realmLib.invokeGetBool(() => getValue(nullptr, 0, valuesCount));

final valuesPtr = createPtr(valuesCount.value);
_realmLib.invokeGetBool(() => getValue(valuesPtr, valuesCount.value, valuesCount));

final result = <TReturn>[];
for (var i = 0; i < valuesCount.value; i++) {
result.add(getAtIndex(valuesPtr, i));
}

return result;
}

void deleteRealmFiles(String path) {
using((Arena arena) {
final realm_deleted = arena<Uint8>();
Expand Down Expand Up @@ -489,7 +539,7 @@ class _RealmCore {
_realmLib.invokeGetBool(() => _realmLib.realm_refresh(realm.handle._pointer), "Could not refresh");
}

RealmClassMetadata getClassMetadata(Realm realm, String className, Type classType) {
RealmObjectMetadata getObjectMedata(Realm realm, String className, Type classType) {
return using((Arena arena) {
final found = arena<Uint8>();
final classInfo = arena<realm_class_info_t>();
Expand All @@ -501,11 +551,11 @@ class _RealmCore {
}

final primaryKey = classInfo.ref.primary_key.cast<Utf8>().toRealmDartString(treatEmptyAsNull: true);
return RealmClassMetadata(classType, classInfo.ref.key, primaryKey);
return RealmObjectMetadata(className, classType, primaryKey, classInfo.ref.key, _getPropertyMetadata(realm, classInfo.ref.key));
});
}

Map<String, RealmPropertyMetadata> getPropertyMetadata(Realm realm, int classKey) {
Map<String, RealmPropertyMetadata> _getPropertyMetadata(Realm realm, int classKey) {
return using((Arena arena) {
final propertyCountPtr = arena<IntPtr>();
_realmLib.invokeGetBool(
Expand All @@ -521,7 +571,8 @@ class _RealmCore {
for (var i = 0; i < propertyCount; i++) {
final property = propertiesPtr.elementAt(i);
final propertyName = property.ref.name.cast<Utf8>().toRealmDartString()!;
final propertyMeta = RealmPropertyMetadata(property.ref.key, RealmCollectionType.values.elementAt(property.ref.collection_type));
final objectType = property.ref.link_target.cast<Utf8>().toRealmDartString(treatEmptyAsNull: true);
final propertyMeta = RealmPropertyMetadata(property.ref.key, objectType, RealmCollectionType.values.elementAt(property.ref.collection_type));
result[propertyName] = propertyMeta;
}
return result;
Expand Down Expand Up @@ -2017,6 +2068,17 @@ extension on List<UserState> {
}
}

extension on realm_property_info {
SchemaProperty toSchemaProperty() {
final linkTarget = link_target == nullptr ? null : link_target.cast<Utf8>().toDartString();
return SchemaProperty(name.cast<Utf8>().toDartString(), RealmPropertyType.values[type],
optional: flags & realm_property_flags.RLM_PROPERTY_NULLABLE == realm_property_flags.RLM_PROPERTY_NULLABLE,
primaryKey: flags & realm_property_flags.RLM_PROPERTY_PRIMARY_KEY == realm_property_flags.RLM_PROPERTY_PRIMARY_KEY,
linkTarget: linkTarget == null || linkTarget.isEmpty ? null : linkTarget,
collectionType: RealmCollectionType.values[collection_type]);
}
}

enum _CustomErrorCode {
noError(0),
unknownHttp(998),
Expand Down Expand Up @@ -2118,7 +2180,7 @@ extension LevelExt on Level {
}

extension PlatformEx on Platform {
static String fromEnvironment(String name, {String defaultValue = "" }) {
static String fromEnvironment(String name, {String defaultValue = ""}) {
final result = Platform.environment[name];
if (result == null) {
return defaultValue;
Expand Down
Loading

0 comments on commit 09168af

Please sign in to comment.