diff --git a/.github/workflows/build-native.yml b/.github/workflows/build-native.yml index 064c6ccc5..c880b3edb 100644 --- a/.github/workflows/build-native.yml +++ b/.github/workflows/build-native.yml @@ -36,7 +36,6 @@ jobs: - name: Check cache id: check-cache - if: ${{ !contains(github.head_ref, 'release/') }} uses: actions/cache@v4 with: path: ./packages/realm_dart/binary/** @@ -44,7 +43,7 @@ jobs: - name: Setup Ninja - if: contains(github.head_ref, 'release/') || steps.check-cache.outputs.cache-hit != 'true' + if: steps.check-cache.outputs.cache-hit != 'true' uses: seanmiddleditch/gha-setup-ninja@1815f2d05c2cd60c2d900f89843139b8dde09f4c - name: Setup Android NDK @@ -56,7 +55,7 @@ jobs: run: sudo xcode-select -s /Applications/Xcode_14.0.1.app - name: Build - if: ${{ contains(github.head_ref, 'release/') || steps.check-cache.outputs.cache-hit != 'true' }} + if: steps.check-cache.outputs.cache-hit != 'true' run: | cmake --preset ${{ startsWith(matrix.build, 'ios-') && 'ios' || matrix.build }} cmake --build --preset ${{ matrix.build }} --config Release ${{ startsWith(matrix.build, 'android-') && '--target strip' || '' }} diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index 1e5361045..88195cb55 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -47,7 +47,7 @@ jobs: with: find: "const libraryVersion = '[^']*';" replace: "const libraryVersion = '${{ steps.update-changelog.outputs.new-version }}';" - include: 'package/realm_dart/lib/src/native/realm_core.dart' + include: 'packages/realm_dart/lib/src/native/realm_core.dart' - name: Make sure we updated libraryVersion in realm_core.dart run: | @@ -62,7 +62,7 @@ jobs: with: find: 'RLM_API const char\* realm_dart_library_version\(\) \{ return "[^"]*"; \}' replace: 'RLM_API const char* realm_dart_library_version() { return "${{ steps.update-changelog.outputs.new-version }}"; }' - include: 'package/realm_dart/src/realm_dart.cpp' + include: 'packages/realm_dart/src/realm_dart.cpp' - name: Make sure we updated realm_dart_library_version in realm_dart.cpp run: | @@ -78,7 +78,7 @@ jobs: echo "core-version=$pkgVersion" >> $GITHUB_OUTPUT echo "Realm Core version: $pkgVersion" shell: bash - working-directory: package/realm_dart/src/realm-core + working-directory: packages/realm_dart/src/realm-core - name: Update realmCoreVersion in metrics_command.dart id: update-realmCoreVersion @@ -86,7 +86,7 @@ jobs: with: find: "const realmCoreVersion = '[^']*';" replace: "const realmCoreVersion = '${{ steps.get-core-version.outputs.core-version }}';" - include: 'package/realm_dart/lib/src/cli/metrics/metrics_command.dart' + include: 'packages/realm_dart/lib/src/cli/metrics/metrics_command.dart' - name: Make sure we updated realmCoreVersion in metrics run: | @@ -99,7 +99,7 @@ jobs: uses: peter-evans/create-pull-request@7380612b49221684fefa025244f2ef4008ae50ad #! 3.10.1 with: branch: release/${{ steps.update-changelog.outputs.new-version }} - base: main + base: ${{ github.ref_name }} title: '[Release ${{ steps.update-changelog.outputs.new-version }}]' draft: false body: An automated PR for next release. diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index e8eb00c72..1d505fc78 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -25,7 +25,7 @@ jobs: - name: Read version id: get-version run: | - pkgVersion=$(sed -ne "s/^version: \(.*\)/\1/p" pubspec.yaml) + pkgVersion=$(sed -ne "s/^version: \(.*\)/\1/p" packages/realm_dart/pubspec.yaml) pkgSuffix="${{ github.event.inputs.environment != 'Production' && format('-{0}', github.sha) || '' }}" echo "version=$pkgVersion$pkgSuffix" >> $GITHUB_OUTPUT shell: bash @@ -40,20 +40,22 @@ jobs: with: workflow: ci.yml commit: ${{ github.sha }} - path: ${{ github.workspace }}/packages/realm_dart/binary/ + path: ${{ github.workspace }}/binary/ workflow_conclusion: completed # The rename is necessary because action-download-artifact will put things in a folder named the same # as the artifact name - i.e. librealm-linux, etc. - name: Archive binaries run: | - mkdir -p release + mkdir -p ${{ github.workspace }}/release - for directory in binary/*; do - targetFile="release/${directory#*-}.tar.gz" - dart run realm_dart archive --source-dir "$directory" --output-file "${{ github.workspace }}/$targetFile" + for directory in ${{ github.workspace }}/binary/*; do + artifactFolder=$(basename $directory) + targetFile="${{ github.workspace }}/release/${artifactFolder#*-}.tar.gz" + dart run realm_dart archive --source-dir "$directory" --output-file "$targetFile" done working-directory: packages/realm_dart + shell: bash - name: Update realm_* path dependencies (Production) uses: jacobtomlinson/gha-find-replace@b76729678e8d52dadb12e0e16454a93e301a919d #! 2.0.0 @@ -88,7 +90,7 @@ jobs: - name: Package realm_common run: | - cp -Lr ../package/realm_common . + cp -Lr ../packages/realm_common . cd realm_common dart pub publish --dry-run || true working-directory: release @@ -104,12 +106,9 @@ jobs: - name: Cleanup binary symlinks run: | rm packages/realm/android/src/main/cpp/lib - rm packages/realm/ios/src rm packages/realm/ios/realm_dart.xcframework rm packages/realm/linux/binary rm packages/realm/windows/binary - rm packages/realm/macos/librealm_dart.dylib - rm packages/realm/example/binary - name: Package realm (flutter) run: | @@ -121,8 +120,8 @@ jobs: - name: Package realm_dart run: | mkdir realm_dart - cp -Lr ../package/realm_dart/bin ../package/realm_dart/example ../package/realm_dart/lib ../package/realm_dart/test realm_dart - rsync -vt ../* realm_dart/ + cp -Lr ../packages/realm_dart/bin ../packages/realm_dart/example ../packages/realm_dart/lib ../packages/realm_dart/test realm_dart + rsync -vt ../packages/realm_dart/* realm_dart/ cd realm_dart dart pub publish --dry-run || true working-directory: release @@ -152,7 +151,7 @@ jobs: - name: Upload release folder to S3 id: upload-to-s3 run: | - tar -zcvf packages.tar.gz common generator realm_dart realm ExtractedChangelog.md + tar -zcvf packages.tar.gz realm_common realm_generator realm_dart realm ExtractedChangelog.md rm -rf common generator realm_dart realm ExtractedChangelog.md s3_folder="static.realm.io/downloads/dart/${{ steps.get-version.outputs.version }}" diff --git a/CHANGELOG.md b/CHANGELOG.md index 008534309..9f964dc76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,21 @@ -## vNext (TBD) +## vNext -### Breaking changes +### Breaking Changes +* `RealmValue.type` is now an enum of type `RealmValueType` rather than `Type`. If you need the runtime type of the value wrapped in `RealmValue`, use `RealmValue.value.runtimeType`. (Issue [#1505](https://github.com/realm/realm-dart/issues/1505)) +* Renamed `RealmValue.uint8List` constructor to `RealmValue.binary`. (PR [#1469](https://github.com/realm/realm-dart/pull/1469)) +* Removed the following deprecated classes and members: + * `AppConfiguration.localAppName` - was unused and had no effect + * `AppConfiguration.localAppVersion` - was unused and had no effect + * `ClientResetError.isFatal` - it was always `true` + * `ClientResetError.sessionErrorCode` + * `SyncError.codeValue` - can be accessed through `SyncError.code.code` + * `SyncError.category` - categories were deprecated in `1.6.0` + * `SyncError.detailedMessage` - was always empty + * `SyncError` constructor and `SyncError.create` factory - sync errors are created internally by the SDK and are not supposed to be constructed by users + * `SyncClientError`, `SyncConnectionError`, `SyncSessionError`, `SyncResolveError`, `SyncWebSocketError`, `GeneralSyncError` - consolidated into `SyncError` as part of the error simplification in `1.6.0` + * `RealmProperty.indexed` - replaced by `RealmProperty.indexType` + * `SyncErrorCategory`, `SyncClientErrorCode`, `SyncConnectionErrorCode`, `SyncSessionErrorCode`, `SyncResolveErrorCode`, `SyncWebsocketErrorCode`, `GeneralSyncErrorCode` - consolidated into `SyncErrorCode` as part of the error simplification in `1.6.0` + * `User.provider` - the provider is associated with each identity, so the value was incorrect for users who had more than one identity * The generated parts are now named `.realm.dart` instead of `.g.dart`. This is because the builder is now a `PartBuilder`, instead of a `SharedPartBuilder`. To migrate to this version you need to update all the part declarations to match, ie. `part 'x.g.dart` becomes `part x.realm.dart` and rerun the generator. This makes it easier to combine builders. Here is an example of combining with `dart_mappable`: @@ -30,16 +45,65 @@ ``` ### Enhancements -* None +* Added `isCollectionDeleted` to `RealmListChanges`, `RealmSetChanges`, and `RealmMapChanges` which will be `true` if the parent object, containing the collection has been deleted. (Core 14.0.0) +* Added `isCleared` to `RealmMapChanges` which will be `true` if the map has been cleared. (Core 14.0.0) +* Querying a specific entry in a collection (in particular 'first and 'last') is supported. (Core 14.0.0) + ```dart + class _Owner { + late List<_Dog> dogs; + } + + realm.query('dogs[1].age = 5'); // Query all owners whose second dog element is 5 years old + realm.query('dogs[FIRST].age = 5'); // Query all owners whose first dog is 5 years old + realm.query('dogs[LAST].age = 5'); // Query all owners whose last dog is 5 years old + realm.query('dogs[SIZE] = 10'); // Query all owners who have 10 dogs + ``` +* Added support for storing lists and maps inside a `RealmValue` property. (Issue [#1504](https://github.com/realm/realm-dart/issues/1504)) + ```dart + class _Container { + late RealmValue anything; + } + + realm.write(() { + realm.add(Container(anything: RealmValue.from([1, 'foo', 3.14]))); + }); + + final container = realm.all().first; + + final list = container.anything.asList(); // will throw if cast is invalid + for (final item in containerValue) { + switch (item.type) { + case RealmValueType.int: + print('Integer: ${item.value as int}'); + break; + case RealmValueType.string: + print('String: ${item.value as String}'); + break; + case RealmValueType.double: + print('Double: ${item.value as double}'); + break; + } + } + + final subscription = list.changes.listen((event) { + // The list changed + }); + ``` +* Added `RealmValueType` enum that contains all the possible types that can be wrapped by a `RealmValue`. (PR [#1469](https://github.com/realm/realm-dart/pull/1469)) +* Added support for accessing `Set` and `Map` types using the dynamic object API - `obj.dynamic.getSet/getMap`. (PR [#1533](https://github.com/realm/realm-dart/pull/1533)) + ### Fixed +* If you have more than 8388606 links pointing to one specific object, the program will crash. (Core 14.0.0) +* A Realm generated on a non-apple ARM 64 device and copied to another platform (and vice-versa) were non-portable due to a sorting order difference. This impacts strings or binaries that have their first difference at a non-ascii character. These items may not be found in a set, or in an indexed column if the strings had a long common prefix (> 200 characters). (Core 14.0.0) * Ctor arguments appear in random order on generated classes, if the realm model contains many properties. (PR [#1531](https://github.com/realm/realm-dart/pull/1531)) ### Compatibility -* Realm Studio: 13.0.0 or later. +* Realm Studio: 14.0.0 or later. +* Fileformat: Generates files with format v24. Reads and automatically upgrade from fileformat v10. If you want to upgrade from an earlier file format version you will have to use RealmCore v13.x.y or earlier. ### Internal -* Using Core x.y.z. +* Using Core 14.0.0 ## 1.9.0 (2024-02-02) diff --git a/packages/realm/example/pubspec.yaml b/packages/realm/example/pubspec.yaml index 038864dc6..768d677ec 100644 --- a/packages/realm/example/pubspec.yaml +++ b/packages/realm/example/pubspec.yaml @@ -1,6 +1,6 @@ name: realm_example description: Demonstrates how to use the Realm SDK for Flutter. -version: 1.9.0 +version: 2.0.0-alpha.2 # The following line prevents the package from being accidentally published to # pub.dev using `pub publish`. This is preferred for private packages. diff --git a/packages/realm/ios/realm.podspec b/packages/realm/ios/realm.podspec index e8ec4f5a7..c5bee8091 100644 --- a/packages/realm/ios/realm.podspec +++ b/packages/realm/ios/realm.podspec @@ -19,7 +19,7 @@ puts "bundleId is #{bundleId}" Pod::Spec.new do |s| s.name = 'realm' - s.version = '1.9.0' + s.version = '2.0.0-alpha.2' s.summary = 'The official Realm SDK for Flutter' s.description = <<-DESC Realm is a mobile database - an alternative to SQLite and key-value stores. diff --git a/packages/realm/macos/realm.podspec b/packages/realm/macos/realm.podspec index b4c163b0a..b054aec32 100644 --- a/packages/realm/macos/realm.podspec +++ b/packages/realm/macos/realm.podspec @@ -36,7 +36,7 @@ puts "bundleId is #{bundleId}" Pod::Spec.new do |s| s.name = 'realm' - s.version = '1.9.0' + s.version = '2.0.0-alpha.2' s.summary = 'The official Realm SDK for Flutter' s.description = <<-DESC Realm is a mobile database - an alternative to SQLite and key-value stores. diff --git a/packages/realm/pubspec.yaml b/packages/realm/pubspec.yaml index a31726457..c4d482d81 100644 --- a/packages/realm/pubspec.yaml +++ b/packages/realm/pubspec.yaml @@ -1,6 +1,6 @@ name: realm description: The official Realm SDK for Flutter. Realm is a mobile database - an alternative to SQLite and key-value stores. -version: 1.9.0 +version: 2.0.0-alpha.2 homepage: https://www.realm.io repository: https://github.com/realm/realm-dart diff --git a/packages/realm/tests/pubspec.yaml b/packages/realm/tests/pubspec.yaml index fd153d16d..1d981c27e 100644 --- a/packages/realm/tests/pubspec.yaml +++ b/packages/realm/tests/pubspec.yaml @@ -3,7 +3,7 @@ description: A new Flutter project. publish_to: "none" -version: 1.9.0 +version: 2.0.0-alpha.2 environment: sdk: ^3.0.0 diff --git a/packages/realm_common/lib/src/realm_types.dart b/packages/realm_common/lib/src/realm_types.dart index 5f57e59cf..c41f8bdb4 100644 --- a/packages/realm_common/lib/src/realm_types.dart +++ b/packages/realm_common/lib/src/realm_types.dart @@ -16,7 +16,6 @@ // //////////////////////////////////////////////////////////////////////////////// -import 'dart:ffi'; import 'dart:math'; import 'dart:typed_data'; import 'package:objectid/objectid.dart'; @@ -157,6 +156,51 @@ abstract class EmbeddedObjectMarker implements RealmObjectBaseMarker {} /// @nodoc abstract class AsymmetricObjectMarker implements RealmObjectBaseMarker {} +/// An enum describing the possible types that can be wrapped inside [RealmValue] +enum RealmValueType { + /// The [RealmValue] represents `null` + nullValue, + + /// The [RealmValue] represents a [boolean] value + boolean, + + /// The [RealmValue] represents a [String] value + string, + + /// The [RealmValue] represents an [int] value + int, + + /// The [RealmValue] represents a [double] value + double, + + /// The [RealmValue] represents a `RealmObject` instance value + object, + + /// The [RealmValue] represents an [ObjectId] value + objectId, + + /// The [RealmValue] represents a [DateTime] value + dateTime, + + /// The [RealmValue] represents a [Decimal128] value + decimal, + + /// The [RealmValue] represents an [Uuid] value + uuid, + + /// The [RealmValue] represents a binary ([Uint8List]) value + binary, + + /// The [RealmValue] represents a `List` + list, + + /// The [RealmValue] represents a `Map` + map; + + /// Returns `true` if the enum value represents a collection - i.e. it's [list] or [map]. + bool get isCollection => this == RealmValueType.list || this == RealmValueType.map; +} + /// A type that can represent any valid realm data type, except collections and embedded objects. /// /// You can use [RealmValue] to declare fields on realm models, in which case it must be non-nullable, @@ -188,60 +232,77 @@ abstract class AsymmetricObjectMarker implements RealmObjectBaseMarker {} /// ``` class RealmValue { final Object? value; - Type get type => value.runtimeType; + + final RealmValueType type; + + /// Casts [value] to [T]. An exception will be thrown if the value is not convertible to [T]. T as() => value as T; // better for code completion // This is private, so user cannot accidentally construct an invalid instance - const RealmValue._(this.value); + const RealmValue._(this.value, this.type); - const RealmValue.nullValue() : this._(null); - const RealmValue.bool(bool b) : this._(b); - const RealmValue.string(String text) : this._(text); - const RealmValue.int(int i) : this._(i); - const RealmValue.double(double d) : this._(d); + const RealmValue.nullValue() : this._(null, RealmValueType.nullValue); + const RealmValue.bool(bool b) : this._(b, RealmValueType.boolean); + const RealmValue.string(String text) : this._(text, RealmValueType.string); + const RealmValue.int(int i) : this._(i, RealmValueType.int); + const RealmValue.double(double d) : this._(d, RealmValueType.double); // TODO: RealmObjectMarker introduced to avoid dependency inversion. It would be better if we could use RealmObject directly. https://github.com/realm/realm-dart/issues/701 - const RealmValue.realmObject(RealmObjectMarker o) : this._(o); - const RealmValue.dateTime(DateTime timestamp) : this._(timestamp); - const RealmValue.objectId(ObjectId id) : this._(id); - const RealmValue.decimal128(Decimal128 decimal) : this._(decimal); - const RealmValue.uuid(Uuid uuid) : this._(uuid); - const RealmValue.uint8List(Uint8List binary) : this._(binary); - - /// Will throw [ArgumentError] + const RealmValue.realmObject(RealmObjectMarker o) : this._(o, RealmValueType.object); + const RealmValue.dateTime(DateTime timestamp) : this._(timestamp, RealmValueType.dateTime); + const RealmValue.objectId(ObjectId id) : this._(id, RealmValueType.objectId); + const RealmValue.decimal128(Decimal128 decimal) : this._(decimal, RealmValueType.decimal); + const RealmValue.uuid(Uuid uuid) : this._(uuid, RealmValueType.uuid); + const RealmValue.binary(Uint8List binary) : this._(binary, RealmValueType.binary); + const RealmValue.list(List list) : this._(list, RealmValueType.list); + const RealmValue.map(Map map) : this._(map, RealmValueType.map); + + /// Constructs a RealmValue from an arbitrary object. Collections will be converted recursively as long + /// as all their values are compatible. + /// + /// Throws [ArgumentError] if any of the values inside the graph cannot be stored in a [RealmValue]. factory RealmValue.from(Object? object) { - if (object == null || - object is bool || - object is String || - object is int || - object is Float || - object is double || - object is RealmObjectMarker || - object is DateTime || - object is ObjectId || - object is Decimal128 || - object is Uuid || - object is Uint8List) { - return RealmValue._(object); - } else { - throw ArgumentError.value(object, 'object', 'Unsupported type'); - } + return switch (object) { + null => RealmValue.nullValue(), + bool b => RealmValue.bool(b), + String text => RealmValue.string(text), + int i => RealmValue.int(i), + double d => RealmValue.double(d), + RealmObjectMarker o => RealmValue.realmObject(o), + DateTime d => RealmValue.dateTime(d), + ObjectId id => RealmValue.objectId(id), + Decimal128 decimal => RealmValue.decimal128(decimal), + Uuid uuid => RealmValue.uuid(uuid), + Uint8List binary => RealmValue.binary(binary), + Map d => RealmValue.map(d), + Map d => RealmValue.map(d.map((key, value) => MapEntry(key, RealmValue.from(value)))), + List l => RealmValue.list(l), + List l => RealmValue.list(l.map((o) => RealmValue.from(o)).toList()), + Iterable i => RealmValue.list(i.toList()), + Iterable i => RealmValue.list(i.map((o) => RealmValue.from(o)).toList()), + _ => throw ArgumentError.value(object.runtimeType, 'object', 'Unsupported type'), + }; } @override operator ==(Object? other) { + // We always return false when comparing two RealmValue collections. + if (type.isCollection) { + return false; + } + if (other is RealmValue) { if (value is Uint8List && other.value is Uint8List) { return ListEquality().equals(value as Uint8List, other.value as Uint8List); } - return value == other.value; + return type == other.type && value == other.value; } return value == other; } @override - int get hashCode => value.hashCode; + int get hashCode => Object.hash(type, value); @override String toString() => 'RealmValue($value)'; diff --git a/packages/realm_common/pubspec.yaml b/packages/realm_common/pubspec.yaml index a014e236e..f0988e752 100644 --- a/packages/realm_common/pubspec.yaml +++ b/packages/realm_common/pubspec.yaml @@ -3,7 +3,7 @@ description: >- Hosts the common code shared between realm, realm_dart and realm_generator packages. This package is part of the official Realm Flutter and Realm Dart SDKs. -version: 1.9.0 +version: 2.0.0-alpha.2 homepage: https://www.realm.io repository: https://github.com/realm/realm-dart diff --git a/packages/realm_dart/CMakePresets.json b/packages/realm_dart/CMakePresets.json index 72414c218..a3ff6e63b 100644 --- a/packages/realm_dart/CMakePresets.json +++ b/packages/realm_dart/CMakePresets.json @@ -210,4 +210,4 @@ "configuration": "Debug" } ] -} +} \ No newline at end of file diff --git a/packages/realm_dart/lib/src/app.dart b/packages/realm_dart/lib/src/app.dart index d22c0c729..bf0c5f62c 100644 --- a/packages/realm_dart/lib/src/app.dart +++ b/packages/realm_dart/lib/src/app.dart @@ -109,22 +109,6 @@ class AppConfiguration { /// the WebSocket handshake. Defaults to 2 minutes. final Duration maxConnectionTimeout; - /// The [localAppName] is the friendly name identifying the current client application. - /// - /// This is typically used to differentiate between client applications that use the same - /// [Atlas App Services](https://www.mongodb.com/docs/atlas/app-services/) application. - /// - /// 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. - @Deprecated("localAppName is not used.") - final String? localAppName; - - /// The [localAppVersion] can be specified, if you wish to distinguish different client versions of the - /// same application. - @Deprecated("localAppVersion is not used.") - final String? localAppVersion; - /// Enumeration that specifies how and if logged-in User objects are persisted across application launches. final MetadataPersistenceMode metadataPersistenceMode; @@ -149,8 +133,6 @@ class AppConfiguration { Uri? baseUrl, Directory? baseFilePath, this.defaultRequestTimeout = const Duration(seconds: 60), - this.localAppName, - this.localAppVersion, this.metadataEncryptionKey, this.metadataPersistenceMode = MetadataPersistenceMode.plaintext, this.maxConnectionTimeout = const Duration(minutes: 2), diff --git a/packages/realm_dart/lib/src/cli/metrics/metrics_command.dart b/packages/realm_dart/lib/src/cli/metrics/metrics_command.dart index ef41659fd..b0d551df8 100644 --- a/packages/realm_dart/lib/src/cli/metrics/metrics_command.dart +++ b/packages/realm_dart/lib/src/cli/metrics/metrics_command.dart @@ -32,7 +32,7 @@ import 'options.dart'; import '../common/utils.dart'; // stamped into the library by the build system (see prepare-release.yml) -const realmCoreVersion = '13.26.0'; +const realmCoreVersion = '14.0.0'; class MetricsCommand extends Command { @override diff --git a/packages/realm_dart/lib/src/collections.dart b/packages/realm_dart/lib/src/collections.dart index 999806734..73a29645a 100644 --- a/packages/realm_dart/lib/src/collections.dart +++ b/packages/realm_dart/lib/src/collections.dart @@ -44,8 +44,9 @@ class CollectionChanges { final List modificationsAfter; final List moves; final bool isCleared; + final bool isDeleted; - const CollectionChanges(this.deletions, this.insertions, this.modifications, this.modificationsAfter, this.moves, this.isCleared); + const CollectionChanges(this.deletions, this.insertions, this.modifications, this.modificationsAfter, this.moves, this.isCleared, this.isDeleted); } /// @nodoc @@ -53,8 +54,10 @@ class MapChanges { final List deletions; final List insertions; final List modifications; + final bool isCleared; + final bool isDeleted; - const MapChanges(this.deletions, this.insertions, this.modifications); + const MapChanges(this.deletions, this.insertions, this.modifications, this.isCleared, this.isDeleted); } /// Describes the changes in a Realm collection since the last time the notification callback was invoked. @@ -91,4 +94,6 @@ extension RealmCollectionChangesInternal on RealmCollectionChanges { void keepAlive() { _handle.keepAlive(); } + + CollectionChanges get changes => _changes; } diff --git a/packages/realm_dart/lib/src/configuration.dart b/packages/realm_dart/lib/src/configuration.dart index 16659997e..dc59c3ec2 100644 --- a/packages/realm_dart/lib/src/configuration.dart +++ b/packages/realm_dart/lib/src/configuration.dart @@ -601,10 +601,6 @@ enum ClientResyncModeInternal { class ClientResetError extends SyncError { final App? _app; - /// If true the received error is fatal. - @Deprecated("This will be removed in the future.") - final bool isFatal = true; - /// The path to the original copy of the realm when the client reset was triggered. /// This realm may contain unsynced changes. final String? originalFilePath; @@ -612,27 +608,6 @@ class ClientResetError extends SyncError { /// The path where the backup copy of the realm will be placed once the client reset process is complete. final String? backupFilePath; - /// The [SyncSessionErrorCode] value indicating the type of the sync error. - /// This property will be [SyncSessionErrorCode.unknown] if `onManualResetFallback` occurs on client reset. - @Deprecated("This will be removed in the future.") - SyncSessionErrorCode get sessionErrorCode => SyncSessionErrorCode.unknown; - - @Deprecated("ClientResetError constructor is deprecated and will be removed in the future") - ClientResetError( - String message, - this._app, { - SyncErrorCategory category = SyncErrorCategory.client, - int? errorCodeValue, - this.backupFilePath, - this.originalFilePath, - String? detailedMessage, - }) : super( - message, - category, - errorCodeValue ?? SyncClientErrorCode.autoClientResetFailure.code, - detailedMessage: detailedMessage, - ); - ClientResetError._( String message, SyncErrorCode code, @@ -673,31 +648,6 @@ class SyncError extends RealmError { SyncError._(String message, this.code, this.innerError) : super(message); - /// The numeric code value indicating the type of the sync error. - @Deprecated("Errors of SyncError subclasses will be created base on the error code. Error codes won't be returned anymore.") - int get codeValue => code.code; - - /// The category of the sync error - @Deprecated("SyncErrorCategory enum is deprecated.") - late SyncErrorCategory category = SyncErrorCategory.system; - - /// Detailed error message. - /// In case of server error, it contains the link to the server log. - @Deprecated("Detailed message is empty. Use `message` property.") - late String? detailedMessage; - - @Deprecated("SyncError constructor is deprecated and will be removed in the future") - SyncError(String message, this.category, int codeValue, {this.detailedMessage}) - : code = SyncErrorCode.fromInt(codeValue), - innerError = null, - super(message); - - /// Creates a specific type of [SyncError] instance based on the [category] and the [code] supplied. - @Deprecated("This method is deprecated and will be removed in the future") - static SyncError create(String message, SyncErrorCategory category, int code, {bool isFatal = false}) { - return SyncError._(message, SyncErrorCode.fromInt(code), null); - } - final Object? innerError; @override @@ -772,139 +722,3 @@ extension SyncErrorInternal on SyncError { }; } } - -// Deprecated errors - to be removed in 2.0 - -/// An error type that describes a session-level error condition. -/// {@category Sync} -@Deprecated("Use SyncError.") -class SyncClientError extends SyncError { - /// If true the received error is fatal. - final bool isFatal; - - @Deprecated("SyncClientError constructor is deprecated and will be removed in the future") - SyncClientError( - String message, - SyncErrorCategory category, - SyncClientErrorCode errorCode, { - String? detailedMessage, - this.isFatal = false, - }) : super(message, category, errorCode.code, detailedMessage: detailedMessage); - - @override - String toString() { - return "SyncClientError message: $message category: $category code: $code isFatal: $isFatal"; - } -} - -/// An error type that describes a connection-level error condition. -/// {@category Sync} -@Deprecated("Use SyncError.") -class SyncConnectionError extends SyncError { - /// If true the received error is fatal. - final bool isFatal; - - @Deprecated("SyncConnectionError constructor is deprecated and will be removed in the future") - SyncConnectionError( - String message, - SyncErrorCategory category, - SyncConnectionErrorCode errorCode, { - String? detailedMessage, - this.isFatal = false, - }) : super(message, category, errorCode.code, detailedMessage: detailedMessage); - - @override - String toString() { - return "SyncConnectionError message: $message category: $category code: $code isFatal: $isFatal"; - } -} - -/// An error type that describes a session-level error condition. -/// {@category Sync} -@Deprecated("Use SyncError.") -class SyncSessionError extends SyncError { - /// If true the received error is fatal. - final bool isFatal; - - @Deprecated("SyncSessionError constructor is deprecated and will be removed in the future") - SyncSessionError( - String message, - SyncErrorCategory category, - SyncSessionErrorCode errorCode, { - String? detailedMessage, - this.isFatal = false, - }) : super(message, category, errorCode.code, detailedMessage: detailedMessage); - - @override - String toString() { - return "SyncSessionError message: $message category: $category code: $code isFatal: $isFatal"; - } -} - -/// Network resolution error -/// -/// This class is deprecated and it will be removed. The sync errors caused by network resolution problems -/// will be received as [SyncWebSocketError]. -@Deprecated("Use SyncError.") -class SyncResolveError extends SyncError { - SyncResolveError( - String message, - SyncErrorCategory category, - SyncResolveErrorCode errorCode, - ) : super(message, category, errorCode.index); - - @override - String toString() { - return "SyncResolveError message: $message category: $category code: $code"; - } -} - -/// Web socket error -@Deprecated("Use SyncError.") -class SyncWebSocketError extends SyncError { - @Deprecated("SyncWebSocketError constructor is deprecated and will be removed in the future") - SyncWebSocketError( - String message, - SyncErrorCategory category, - SyncWebSocketErrorCode errorCode, { - String? detailedMessage, - }) : super(message, category, errorCode.code, detailedMessage: detailedMessage); - - @override - String toString() { - return "SyncWebSocketError message: $message category: $category code: $code"; - } -} - -/// A general or unknown sync error -@Deprecated("Use SyncError.") -class GeneralSyncError extends SyncError { - @Deprecated("GeneralSyncError constructor is deprecated and will be removed in the future") - GeneralSyncError( - String message, - SyncErrorCategory category, - int code, { - String? detailedMessage, - }) : super(message, category, code, detailedMessage: detailedMessage); - - @override - String toString() { - return "GeneralSyncError message: $message category: $category code: $code"; - } -} - -/// General sync error codes -@Deprecated("Use SyncError.") -enum GeneralSyncErrorCode { - /// Unknown Sync error code - unknown(9999); - - static final Map _valuesMap = {for (var value in GeneralSyncErrorCode.values) value.code: value}; - - static GeneralSyncErrorCode fromInt(int code) { - return GeneralSyncErrorCode._valuesMap[code] ?? GeneralSyncErrorCode.unknown; - } - - final int code; - const GeneralSyncErrorCode(this.code); -} diff --git a/packages/realm_dart/lib/src/list.dart b/packages/realm_dart/lib/src/list.dart index bf7533d8a..e97892538 100644 --- a/packages/realm_dart/lib/src/list.dart +++ b/packages/realm_dart/lib/src/list.dart @@ -97,6 +97,7 @@ class ManagedRealmList with RealmEntity, ListMixin impleme if (element is RealmObjectBase && !element.isManaged) { throw RealmStateError('Cannot call remove on a managed list with an element that is an unmanaged object'); } + final index = indexOf(element); if (index < 0) { return false; @@ -173,6 +174,17 @@ class ManagedRealmList with RealmEntity, ListMixin impleme if (element is RealmObjectBase && !element.isManaged) { throw RealmStateError('Cannot call indexOf on a managed list with an element that is an unmanaged object'); } + + if (element is RealmValue) { + if (element.type.isCollection) { + return -1; + } + + if (element.value is RealmObjectBase && !(element.value as RealmObjectBase).isManaged) { + return -1; + } + } + if (start < 0) start = 0; final index = realmCore.listFind(this, element); return index < start ? -1 : index; // to align with dart list semantics @@ -205,7 +217,13 @@ class ManagedRealmList with RealmEntity, ListMixin impleme } class UnmanagedRealmList extends collection.DelegatingList with RealmEntity implements RealmList { - UnmanagedRealmList([Iterable? items]) : super(List.from(items ?? [])); + final List _base; + + UnmanagedRealmList([Iterable? items]) : this._(List.from(items ?? [])); + + UnmanagedRealmList._(List items) + : _base = items, + super(items); @override RealmObjectMetadata? get _metadata => throw RealmException("Unmanaged lists don't have metadata associated with them."); @@ -224,6 +242,14 @@ class UnmanagedRealmList extends collection.DelegatingList @override Stream> get changes => throw RealmStateError("Unmanaged lists don't support changes"); + + @override + bool operator ==(Object? other) { + return _base == other; + } + + @override + int get hashCode => _base.hashCode; } // The query operations on lists, only work for list of objects (core restriction), @@ -264,6 +290,10 @@ extension RealmListInternal on RealmList { RealmObjectMetadata? get metadata => asManaged()._metadata; + static RealmList createFromList(List items) { + return UnmanagedRealmList._(items); + } + static RealmList create(RealmListHandle handle, Realm realm, RealmObjectMetadata? metadata) => RealmList._(handle, realm, metadata); static void setValue(RealmListHandle handle, Realm realm, int index, Object? value, {bool update = false, bool insert = false}) { @@ -288,19 +318,14 @@ extension RealmListInternal on RealmList { return; } - if (value is RealmValue) { - value = value.value; + if (value is RealmValue && value.type.isCollection) { + realmCore.listAddCollectionAt(handle, realm, index, value, insert || index >= length); + return; } - if (value is RealmObject && !value.isManaged) { - realm.add(value, update: update); - } + realm.addUnmanagedRealmObjectFromValue(value, update); - if (insert || index >= length) { - realmCore.listInsertElementAt(handle, index, value); - } else { - realmCore.listSetElementAt(handle, index, value); - } + realmCore.listAddElementAt(handle, index, value, insert || index >= length); } on Exception catch (e) { throw RealmException("Error setting value at index $index. Error: $e"); } @@ -313,6 +338,9 @@ class RealmListChanges extends RealmCollectionChanges { final RealmList list; RealmListChanges._(super.handle, this.list); + + /// `true` if the underlying list was deleted. + bool get isCollectionDeleted => changes.isDeleted; } /// @nodoc diff --git a/packages/realm_dart/lib/src/map.dart b/packages/realm_dart/lib/src/map.dart index 6f79591d9..fc3c64bbc 100644 --- a/packages/realm_dart/lib/src/map.dart +++ b/packages/realm_dart/lib/src/map.dart @@ -49,7 +49,13 @@ abstract class RealmMap with RealmEntity implements MapBase extends collection.DelegatingMap with RealmEntity implements RealmMap { - UnmanagedRealmMap([Map? items]) : super(Map.from(items ?? {})); + final Map _base; + + UnmanagedRealmMap([Map? items]) : this._(Map.from(items ?? {})); + + UnmanagedRealmMap._(Map items) + : _base = items, + super(items); @override bool get isValid => true; @@ -59,6 +65,14 @@ class UnmanagedRealmMap extends collection.DelegatingMap> get changes => throw RealmStateError("Unmanaged maps don't support changes"); + + @override + bool operator ==(Object? other) { + return _base == other; + } + + @override + int get hashCode => _base.hashCode; } class ManagedRealmMap with RealmEntity, MapMixin implements RealmMap { @@ -172,8 +186,14 @@ class ManagedRealmMap with RealmEntity, MapMixin i return false; } - if (value is RealmValue && value.value is RealmObjectBase && !(value.value as RealmObjectBase).isManaged) { - return false; + if (value is RealmValue) { + if (value.value is RealmObjectBase && !(value.value as RealmObjectBase).isManaged) { + return false; + } + + if (value.type.isCollection) { + return false; + } } return realmCore.mapContainsValue(this, value); @@ -200,6 +220,12 @@ class RealmMapChanges { /// The keys of the map, whose corresponding values were modified in this version. List get modified => _changes.modifications; + + /// `true` if the map was cleared. + bool get isCleared => _changes.isCleared; + + /// `true` if the underlying map was deleted. + bool get isCollectionDeleted => _changes.isDeleted; } // The query operations on maps only work for maps of objects (core restriction), @@ -240,6 +266,8 @@ extension RealmMapInternal on RealmMap { RealmObjectMetadata? get metadata => asManaged()._metadata; + static RealmMap createFromMap(Map map) => UnmanagedRealmMap._(map); + static RealmMap create(RealmMapHandle handle, Realm realm, RealmObjectMetadata? metadata) => ManagedRealmMap._(handle, realm, metadata); @@ -255,13 +283,12 @@ extension RealmMapInternal on RealmMap { return; } - if (value is RealmValue) { - value = value.value; + if (value is RealmValue && value.type.isCollection) { + realmCore.mapInsertCollection(handle, realm, key, value); + return; } - if (value is RealmObject && !value.isManaged) { - realm.add(value, update: update); - } + realm.addUnmanagedRealmObjectFromValue(value, update); realmCore.mapInsertValue(handle, key, value); } on Exception catch (e) { diff --git a/packages/realm_dart/lib/src/native/realm_bindings.dart b/packages/realm_dart/lib/src/native/realm_bindings.dart index f3bab45dc..84dfe75b8 100644 --- a/packages/realm_dart/lib/src/native/realm_bindings.dart +++ b/packages/realm_dart/lib/src/native/realm_bindings.dart @@ -2198,6 +2198,7 @@ class RealmLibrary { /// @param out_num_modifications The number of modifications. May be NULL. /// @param out_num_moves The number of moved elements. May be NULL. /// @param out_collection_was_cleared a flag to signal if the collection has been cleared. May be NULL + /// @param out_collection_was_deleted a flag to signal if the collection has been deleted. May be NULL void realm_collection_changes_get_num_changes( ffi.Pointer arg0, ffi.Pointer out_num_deletions, @@ -2205,6 +2206,7 @@ class RealmLibrary { ffi.Pointer out_num_modifications, ffi.Pointer out_num_moves, ffi.Pointer out_collection_was_cleared, + ffi.Pointer out_collection_was_deleted, ) { return _realm_collection_changes_get_num_changes( arg0, @@ -2213,6 +2215,7 @@ class RealmLibrary { out_num_modifications, out_num_moves, out_collection_was_cleared, + out_collection_was_deleted, ); } @@ -2224,6 +2227,7 @@ class RealmLibrary { ffi.Pointer, ffi.Pointer, ffi.Pointer, + ffi.Pointer, ffi.Pointer)>>( 'realm_collection_changes_get_num_changes'); late final _realm_collection_changes_get_num_changes = @@ -2234,6 +2238,7 @@ class RealmLibrary { ffi.Pointer, ffi.Pointer, ffi.Pointer, + ffi.Pointer, ffi.Pointer)>(); /// Get the number of various types of changes in a collection notification, @@ -2609,6 +2614,22 @@ class RealmLibrary { _realm_config_get_schema_versionPtr .asFunction)>(); + /// True if you can open the file without a file_format_upgrade + bool realm_config_needs_file_format_upgrade( + ffi.Pointer arg0, + ) { + return _realm_config_needs_file_format_upgrade( + arg0, + ); + } + + late final _realm_config_needs_file_format_upgradePtr = _lookup< + ffi.NativeFunction)>>( + 'realm_config_needs_file_format_upgrade'); + late final _realm_config_needs_file_format_upgrade = + _realm_config_needs_file_format_upgradePtr + .asFunction)>(); + /// Allocate a new configuration with default options. ffi.Pointer realm_config_new() { return _realm_config_new(); @@ -4410,6 +4431,7 @@ class RealmLibrary { /// @param insertions_size size of the list of inserted keys /// @param modifications list of modified keys /// @param modification_size size of the list of modified keys + /// @param collection_was_cleared whether or not the collection was cleared void realm_dictionary_get_changed_keys( ffi.Pointer changes, ffi.Pointer deletions, @@ -4418,6 +4440,7 @@ class RealmLibrary { ffi.Pointer insertions_size, ffi.Pointer modifications, ffi.Pointer modification_size, + ffi.Pointer collection_was_cleared, ) { return _realm_dictionary_get_changed_keys( changes, @@ -4427,6 +4450,7 @@ class RealmLibrary { insertions_size, modifications, modification_size, + collection_was_cleared, ); } @@ -4439,7 +4463,8 @@ class RealmLibrary { ffi.Pointer, ffi.Pointer, ffi.Pointer, - ffi.Pointer)>>('realm_dictionary_get_changed_keys'); + ffi.Pointer, + ffi.Pointer)>>('realm_dictionary_get_changed_keys'); late final _realm_dictionary_get_changed_keys = _realm_dictionary_get_changed_keysPtr.asFunction< void Function( @@ -4449,7 +4474,8 @@ class RealmLibrary { ffi.Pointer, ffi.Pointer, ffi.Pointer, - ffi.Pointer)>(); + ffi.Pointer, + ffi.Pointer)>(); /// Returns the number of changes occurred to the dictionary passed as argument /// @@ -4457,17 +4483,20 @@ class RealmLibrary { /// @param out_deletions_size number of deletions /// @param out_insertion_size number of insertions /// @param out_modification_size number of modifications + /// @param out_was_deleted a flag to signal if the dictionary has been deleted. void realm_dictionary_get_changes( ffi.Pointer changes, ffi.Pointer out_deletions_size, ffi.Pointer out_insertion_size, ffi.Pointer out_modification_size, + ffi.Pointer out_was_deleted, ) { return _realm_dictionary_get_changes( changes, out_deletions_size, out_insertion_size, out_modification_size, + out_was_deleted, ); } @@ -4477,14 +4506,38 @@ class RealmLibrary { ffi.Pointer, ffi.Pointer, ffi.Pointer, - ffi.Pointer)>>('realm_dictionary_get_changes'); + ffi.Pointer, + ffi.Pointer)>>('realm_dictionary_get_changes'); late final _realm_dictionary_get_changes = _realm_dictionary_get_changesPtr.asFunction< void Function( ffi.Pointer, ffi.Pointer, ffi.Pointer, - ffi.Pointer)>(); + ffi.Pointer, + ffi.Pointer)>(); + + /// Fetch a dictioanry from a dictionary. + /// @return a valid dictionary that needs to be deleted by the caller or nullptr in case of an error. + ffi.Pointer realm_dictionary_get_dictionary( + ffi.Pointer dictionary, + realm_value_t key, + ) { + return _realm_dictionary_get_dictionary( + dictionary, + key, + ); + } + + late final _realm_dictionary_get_dictionaryPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, + realm_value_t)>>('realm_dictionary_get_dictionary'); + late final _realm_dictionary_get_dictionary = + _realm_dictionary_get_dictionaryPtr.asFunction< + ffi.Pointer Function( + ffi.Pointer, realm_value_t)>(); /// Return the list of keys stored in the dictionary /// @@ -4537,6 +4590,27 @@ class RealmLibrary { ffi.Pointer Function( ffi.Pointer, realm_value_t)>(); + /// Fetch a list from a dictionary. + /// @return a valid list that needs to be deleted by the caller or nullptr in case of an error. + ffi.Pointer realm_dictionary_get_list( + ffi.Pointer dictionary, + realm_value_t key, + ) { + return _realm_dictionary_get_list( + dictionary, + key, + ); + } + + late final _realm_dictionary_get_listPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, + realm_value_t)>>('realm_dictionary_get_list'); + late final _realm_dictionary_get_list = + _realm_dictionary_get_listPtr.asFunction< + ffi.Pointer Function( + ffi.Pointer, realm_value_t)>(); + /// Get the property that this dictionary came from. /// /// @return True if no exception occurred. @@ -4599,6 +4673,26 @@ class RealmLibrary { bool Function(ffi.Pointer, realm_value_t, realm_value_t, ffi.Pointer, ffi.Pointer)>(); + ffi.Pointer realm_dictionary_insert_dictionary( + ffi.Pointer arg0, + realm_value_t arg1, + ) { + return _realm_dictionary_insert_dictionary( + arg0, + arg1, + ); + } + + late final _realm_dictionary_insert_dictionaryPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, + realm_value_t)>>('realm_dictionary_insert_dictionary'); + late final _realm_dictionary_insert_dictionary = + _realm_dictionary_insert_dictionaryPtr.asFunction< + ffi.Pointer Function( + ffi.Pointer, realm_value_t)>(); + /// Insert an embedded object. /// /// @return A non-NULL pointer if the object was created successfully. @@ -4621,6 +4715,30 @@ class RealmLibrary { ffi.Pointer Function( ffi.Pointer, realm_value_t)>(); + /// Insert a collection inside a dictionary (only available for mixed types) + /// + /// @param dictionary valid ptr to a dictionary of mixed + /// @param key the mixed representing a key for a dictionary (only string) + /// @return pointer to a valid collection that has been just inserted at the key passed as argument + ffi.Pointer realm_dictionary_insert_list( + ffi.Pointer dictionary, + realm_value_t key, + ) { + return _realm_dictionary_insert_list( + dictionary, + key, + ); + } + + late final _realm_dictionary_insert_listPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, + realm_value_t)>>('realm_dictionary_insert_list'); + late final _realm_dictionary_insert_list = + _realm_dictionary_insert_listPtr.asFunction< + ffi.Pointer Function( + ffi.Pointer, realm_value_t)>(); + /// Check if a list is valid. /// /// @return True if the list is valid. @@ -5904,6 +6022,30 @@ class RealmLibrary { bool Function( ffi.Pointer, int, ffi.Pointer)>(); + /// Returns a nested dictionary if such collection exists, NULL otherwise. + /// + /// @param list pointer to the list that containes the nested collection into + /// @param index position of collection in the list + /// @return a pointer to the the nested dictionary found at index passed as argument + ffi.Pointer realm_list_get_dictionary( + ffi.Pointer list, + int index, + ) { + return _realm_list_get_dictionary( + list, + index, + ); + } + + late final _realm_list_get_dictionaryPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Size)>>('realm_list_get_dictionary'); + late final _realm_list_get_dictionary = + _realm_list_get_dictionaryPtr.asFunction< + ffi.Pointer Function( + ffi.Pointer, int)>(); + /// Get object identified at index /// /// @return A non-NULL pointer if value is an object. @@ -5926,6 +6068,28 @@ class RealmLibrary { ffi.Pointer Function( ffi.Pointer, int)>(); + /// Returns a nested list if such collection exists, NULL otherwise. + /// + /// @param list pointer to the list that containes the nested list + /// @param index index of collection in the list + /// @return a pointer to the the nested list found at the index passed as argument + ffi.Pointer realm_list_get_list( + ffi.Pointer list, + int index, + ) { + return _realm_list_get_list( + list, + index, + ); + } + + late final _realm_list_get_listPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Size)>>('realm_list_get_list'); + late final _realm_list_get_list = _realm_list_get_listPtr.asFunction< + ffi.Pointer Function(ffi.Pointer, int)>(); + /// Get the property that this list came from. /// /// @return True if no exception occurred. @@ -5970,6 +6134,25 @@ class RealmLibrary { late final _realm_list_insert = _realm_list_insertPtr.asFunction< bool Function(ffi.Pointer, int, realm_value_t)>(); + ffi.Pointer realm_list_insert_dictionary( + ffi.Pointer list, + int index, + ) { + return _realm_list_insert_dictionary( + list, + index, + ); + } + + late final _realm_list_insert_dictionaryPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Size)>>('realm_list_insert_dictionary'); + late final _realm_list_insert_dictionary = + _realm_list_insert_dictionaryPtr.asFunction< + ffi.Pointer Function( + ffi.Pointer, int)>(); + /// Insert an embedded object at a given position. /// /// @return A non-NULL pointer if the object was created successfully. @@ -5992,6 +6175,28 @@ class RealmLibrary { ffi.Pointer Function( ffi.Pointer, int)>(); + /// Insert a collection inside a list (only available for mixed types) + /// + /// @param list valid ptr to a list of mixed + /// @param index position in the list where to add the collection + /// @return pointer to a valid collection that has been just inserted at the index passed as argument + ffi.Pointer realm_list_insert_list( + ffi.Pointer list, + int index, + ) { + return _realm_list_insert_list( + list, + index, + ); + } + + late final _realm_list_insert_listPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Size)>>('realm_list_insert_list'); + late final _realm_list_insert_list = _realm_list_insert_listPtr.asFunction< + ffi.Pointer Function(ffi.Pointer, int)>(); + /// Check if a list is valid. /// /// @return True if the list is valid. @@ -6103,6 +6308,25 @@ class RealmLibrary { late final _realm_list_set = _realm_list_setPtr.asFunction< bool Function(ffi.Pointer, int, realm_value_t)>(); + ffi.Pointer realm_list_set_dictionary( + ffi.Pointer list, + int index, + ) { + return _realm_list_set_dictionary( + list, + index, + ); + } + + late final _realm_list_set_dictionaryPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Size)>>('realm_list_set_dictionary'); + late final _realm_list_set_dictionary = + _realm_list_set_dictionaryPtr.asFunction< + ffi.Pointer Function( + ffi.Pointer, int)>(); + /// Create an embedded object at a given position. /// /// @return A non-NULL pointer if the object was created successfully. @@ -6123,6 +6347,30 @@ class RealmLibrary { late final _realm_list_set_embedded = _realm_list_set_embeddedPtr.asFunction< ffi.Pointer Function(ffi.Pointer, int)>(); + /// Set a collection inside a list (only available for mixed types). + /// If the list already contains a collection of the requested type, the + /// operation is idempotent. + /// + /// @param list valid ptr to a list where a nested collection needs to be set + /// @param index position in the list where to set the collection + /// @return a valid ptr representing the collection just set + ffi.Pointer realm_list_set_list( + ffi.Pointer list, + int index, + ) { + return _realm_list_set_list( + list, + index, + ); + } + + late final _realm_list_set_listPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Size)>>('realm_list_set_list'); + late final _realm_list_set_list = _realm_list_set_listPtr.asFunction< + ffi.Pointer Function(ffi.Pointer, int)>(); + /// Get the size of a list, in number of elements. /// /// This function may fail if the object owning the list has been deleted. @@ -7937,6 +8185,46 @@ class RealmLibrary { bool Function( ffi.Pointer, int, ffi.Pointer)>(); + /// Returns an instance of realm_dictionary for the index passed as argument. + /// @return A valid ptr to a dictionary instance or nullptr in case of errors + ffi.Pointer realm_results_get_dictionary( + ffi.Pointer arg0, + int index, + ) { + return _realm_results_get_dictionary( + arg0, + index, + ); + } + + late final _realm_results_get_dictionaryPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Size)>>('realm_results_get_dictionary'); + late final _realm_results_get_dictionary = + _realm_results_get_dictionaryPtr.asFunction< + ffi.Pointer Function( + ffi.Pointer, int)>(); + + /// Returns an instance of realm_list at the index passed as argument. + /// @return A valid ptr to a list instance or nullptr in case of errors + ffi.Pointer realm_results_get_list( + ffi.Pointer arg0, + int index, + ) { + return _realm_results_get_list( + arg0, + index, + ); + } + + late final _realm_results_get_listPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Size)>>('realm_results_get_list'); + late final _realm_results_get_list = _realm_results_get_listPtr.asFunction< + ffi.Pointer Function(ffi.Pointer, int)>(); + /// Get the matching object at @a index in the results. /// /// If the result is "live" (not a snapshot), this may rerun the query if things @@ -8520,6 +8808,24 @@ class RealmLibrary { late final _realm_set_clear = _realm_set_clearPtr.asFunction)>(); + ffi.Pointer realm_set_dictionary( + ffi.Pointer arg0, + int arg1, + ) { + return _realm_set_dictionary( + arg0, + arg1, + ); + } + + late final _realm_set_dictionaryPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, + realm_property_key_t)>>('realm_set_dictionary'); + late final _realm_set_dictionary = _realm_set_dictionaryPtr.asFunction< + ffi.Pointer Function( + ffi.Pointer, int)>(); + /// Create an embedded object in a given property. /// /// @return A non-NULL pointer if the object was created successfully. @@ -8726,6 +9032,47 @@ class RealmLibrary { late final _realm_set_is_valid = _realm_set_is_validPtr .asFunction)>(); + /// Assign a JSON formatted string to a Mixed property. Underlying structures will be created as needed + /// + /// @param json_string The new value for the property. + /// @return True if no exception occurred. + bool realm_set_json( + ffi.Pointer arg0, + int arg1, + ffi.Pointer json_string, + ) { + return _realm_set_json( + arg0, + arg1, + json_string, + ); + } + + late final _realm_set_jsonPtr = _lookup< + ffi.NativeFunction< + ffi.Bool Function(ffi.Pointer, realm_property_key_t, + ffi.Pointer)>>('realm_set_json'); + late final _realm_set_json = _realm_set_jsonPtr.asFunction< + bool Function(ffi.Pointer, int, ffi.Pointer)>(); + + /// Create a collection in a given Mixed property. + ffi.Pointer realm_set_list( + ffi.Pointer arg0, + int arg1, + ) { + return _realm_set_list( + arg0, + arg1, + ); + } + + late final _realm_set_listPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, + realm_property_key_t)>>('realm_set_list'); + late final _realm_set_list = _realm_set_listPtr.asFunction< + ffi.Pointer Function(ffi.Pointer, int)>(); + /// Install the default logger void realm_set_log_callback( realm_log_func_t arg0, @@ -8763,6 +9110,23 @@ class RealmLibrary { late final _realm_set_log_level = _realm_set_log_levelPtr.asFunction(); + void realm_set_log_level_category( + ffi.Pointer arg0, + int arg1, + ) { + return _realm_set_log_level_category( + arg0, + arg1, + ); + } + + late final _realm_set_log_level_categoryPtr = _lookup< + ffi + .NativeFunction, ffi.Int32)>>( + 'realm_set_log_level_category'); + late final _realm_set_log_level_category = _realm_set_log_level_categoryPtr + .asFunction, int)>(); + /// In a set of objects, delete all objects in the set and clear the set. In a /// set of values, clear the set. /// @@ -9828,6 +10192,28 @@ class RealmLibrary { _realm_sync_session_get_connection_statePtr .asFunction)>(); + /// Gets the file ident/salt currently assigned to the realm by sync. Callers should supply a pointer token + /// a realm_salted_file_ident_t for this function to fill out. + void realm_sync_session_get_file_ident( + ffi.Pointer arg0, + ffi.Pointer out, + ) { + return _realm_sync_session_get_file_ident( + arg0, + out, + ); + } + + late final _realm_sync_session_get_file_identPtr = _lookup< + ffi.NativeFunction< + ffi.Void Function(ffi.Pointer, + ffi.Pointer)>>( + 'realm_sync_session_get_file_ident'); + late final _realm_sync_session_get_file_ident = + _realm_sync_session_get_file_identPtr.asFunction< + void Function(ffi.Pointer, + ffi.Pointer)>(); + /// Get the filesystem path of the realm file backing this session. ffi.Pointer realm_sync_session_get_file_path( ffi.Pointer arg0, @@ -12340,6 +12726,16 @@ typedef Dartrealm_return_string_func_tFunction = void Function( ffi.Pointer, ffi.Pointer); +final class realm_salted_file_ident extends ffi.Struct { + @ffi.Uint64() + external int ident; + + @ffi.Int64() + external int salt; +} + +typedef realm_salted_file_ident_t = realm_salted_file_ident; + final class realm_scheduler extends ffi.Opaque {} typedef realm_scheduler_can_deliver_notifications_func_t = ffi.Pointer< @@ -12868,6 +13264,8 @@ abstract class realm_value_type { static const int RLM_TYPE_OBJECT_ID = 9; static const int RLM_TYPE_LINK = 10; static const int RLM_TYPE_UUID = 11; + static const int RLM_TYPE_LIST = 12; + static const int RLM_TYPE_DICTIONARY = 13; } final class realm_version_id extends ffi.Struct { diff --git a/packages/realm_dart/lib/src/native/realm_core.dart b/packages/realm_dart/lib/src/native/realm_core.dart index ed0821b79..fa300777e 100644 --- a/packages/realm_dart/lib/src/native/realm_core.dart +++ b/packages/realm_dart/lib/src/native/realm_core.dart @@ -90,7 +90,7 @@ final _pluginLib = () { }(); // stamped into the library by the build system (see prepare-release.yml) -const libraryVersion = '1.9.0'; +const libraryVersion = '2.0.0-alpha.2'; _RealmCore realmCore = _RealmCore(); @@ -1046,17 +1046,23 @@ class _RealmCore { 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); + return realm_value.toDartValue(object.realm, () => _realmLib.realm_get_list(object.handle._pointer, propertyKey), + () => _realmLib.realm_get_dictionary(object.handle._pointer, propertyKey)); }); } void setProperty(RealmObjectBase object, int propertyKey, Object? value, bool isDefault) { - return using((Arena arena) { + using((Arena arena) { final realm_value = _toRealmValue(value, arena); _realmLib.invokeGetBool(() => _realmLib.realm_set_value(object.handle._pointer, propertyKey, realm_value.ref, isDefault)); }); } + void objectSetCollection(RealmObjectBase object, int propertyKey, RealmValue value) { + _createCollection(object.realm, value, () => _realmLib.realm_set_list(object.handle._pointer, propertyKey), + () => _realmLib.realm_set_dictionary(object.handle._pointer, propertyKey)); + } + String objectToString(RealmObjectBase object) { return _realmLib.realm_object_to_string(object.handle._pointer).cast().toRealmDartString(freeRealmMemory: true)!; } @@ -1238,7 +1244,8 @@ class _RealmCore { return using((Arena arena) { final realm_value = arena(); _realmLib.invokeGetBool(() => _realmLib.realm_results_get(results.handle._pointer, index, realm_value)); - return realm_value.toDartValue(results.realm); + return realm_value.toDartValue(results.realm, () => _realmLib.realm_results_get_list(results.handle._pointer, index), + () => _realmLib.realm_results_get_dictionary(results.handle._pointer, index)); }); } @@ -1246,6 +1253,8 @@ class _RealmCore { return using((Arena arena) { final out_index = arena(); final out_found = arena(); + + // TODO: how should this behave for collections final realm_value = _toRealmValue(value, arena); _realmLib.invokeGetBool( () => _realmLib.realm_results_find( @@ -1287,6 +1296,7 @@ class _RealmCore { final out_num_modifications = arena(); final out_num_moves = arena(); final out_collection_cleared = arena(); + final out_collection_was_deleted = arena(); _realmLib.realm_collection_changes_get_num_changes( changes._pointer, out_num_deletions, @@ -1294,6 +1304,7 @@ class _RealmCore { out_num_modifications, out_num_moves, out_collection_cleared, + out_collection_was_deleted, ); final deletionsCount = out_num_deletions != nullptr ? out_num_deletions.value : 0; @@ -1335,6 +1346,7 @@ class _RealmCore { out_modification_indexes_after.toIntList(modificationCount), moves, out_collection_cleared.value, + out_collection_was_deleted.value, ); }); } @@ -1344,11 +1356,13 @@ class _RealmCore { final out_num_deletions = arena(); final out_num_insertions = arena(); final out_num_modifications = arena(); + final out_collection_was_deleted = arena(); _realmLib.realm_dictionary_get_changes( changes._pointer, out_num_deletions, out_num_insertions, out_num_modifications, + out_collection_was_deleted, ); final deletionsCount = out_num_deletions != nullptr ? out_num_deletions.value : 0; @@ -1358,6 +1372,7 @@ class _RealmCore { final out_deletion_indexes = arena(deletionsCount); final out_insertion_indexes = arena(insertionCount); final out_modification_indexes = arena(modificationCount); + final out_collection_was_cleared = arena(); _realmLib.realm_dictionary_get_changed_keys( changes._pointer, @@ -1367,10 +1382,11 @@ class _RealmCore { out_num_insertions, out_modification_indexes, out_num_modifications, + out_collection_was_cleared, ); return MapChanges(out_deletion_indexes.toStringList(deletionsCount), out_insertion_indexes.toStringList(insertionCount), - out_modification_indexes.toStringList(modificationCount)); + out_modification_indexes.toStringList(modificationCount), out_collection_was_cleared.value, out_collection_was_deleted.value); }); } @@ -1406,22 +1422,21 @@ class _RealmCore { 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); + return realm_value.toDartValue( + list.realm, () => _realmLib.realm_list_get_list(list.handle._pointer, index), () => _realmLib.realm_list_get_dictionary(list.handle._pointer, index)); }); } - void listSetElementAt(RealmListHandle handle, int index, Object? value) { + void listAddElementAt(RealmListHandle handle, int index, Object? value, bool insert) { using((Arena arena) { final realm_value = _toRealmValue(value, arena); - _realmLib.invokeGetBool(() => _realmLib.realm_list_set(handle._pointer, index, realm_value.ref)); + _realmLib.invokeGetBool(() => (insert ? _realmLib.realm_list_insert : _realmLib.realm_list_set)(handle._pointer, index, realm_value.ref)); }); } - void listInsertElementAt(RealmListHandle handle, int index, Object? value) { - using((Arena arena) { - final realm_value = _toRealmValue(value, arena); - _realmLib.invokeGetBool(() => _realmLib.realm_list_insert(handle._pointer, index, realm_value.ref)); - }); + void listAddCollectionAt(RealmListHandle handle, Realm realm, int index, RealmValue value, bool insert) { + _createCollection(realm, value, () => (insert ? _realmLib.realm_list_insert_list : _realmLib.realm_list_set_list)(handle._pointer, index), + () => (insert ? _realmLib.realm_list_insert_dictionary : _realmLib.realm_list_set_dictionary)(handle._pointer, index)); } RealmObjectHandle listSetEmbeddedObjectAt(Realm realm, RealmListHandle handle, int index) { @@ -1450,6 +1465,8 @@ class _RealmCore { return using((Arena arena) { final out_index = arena(); final out_found = arena(); + + // TODO: how should this behave for collections final realm_value = _toRealmValue(value, arena); _realmLib.invokeGetBool( () => _realmLib.realm_list_find( @@ -1490,13 +1507,15 @@ class _RealmCore { return using((Arena arena) { final realm_value = arena(); _realmLib.invokeGetBool(() => _realmLib.realm_set_get(realmSet.handle._pointer, index, realm_value)); - final result = realm_value.toDartValue(realmSet.realm); + final result = realm_value.toDartValue( + realmSet.realm, () => throw RealmException('Sets cannot contain collections'), () => throw RealmException('Sets cannot contain collections')); return result; }); } bool realmSetFind(RealmSet realmSet, Object? value) { return using((Arena arena) { + // TODO: how should this behave for collections final realm_value = _toRealmValue(value, arena); final out_index = arena(); final out_found = arena(); @@ -1507,6 +1526,7 @@ class _RealmCore { bool realmSetErase(RealmSet realmSet, Object? value) { return using((Arena arena) { + // TODO: do we support sets containing mixed collections final realm_value = _toRealmValue(value, arena); final out_erased = arena(); _realmLib.invokeGetBool(() => _realmLib.realm_set_erase(realmSet.handle._pointer, realm_value.ref, out_erased)); @@ -1570,7 +1590,8 @@ class _RealmCore { final out_found = arena(); _realmLib.invokeGetBool(() => _realmLib.realm_dictionary_find(map.handle._pointer, key_value.ref, realm_value, out_found)); if (out_found.value) { - return realm_value.toDartValue(map.realm); + return realm_value.toDartValue(map.realm, () => _realmLib.realm_dictionary_get_list(map.handle._pointer, key_value.ref), + () => _realmLib.realm_dictionary_get_dictionary(map.handle._pointer, key_value.ref)); } return null; @@ -1610,9 +1631,10 @@ class _RealmCore { bool mapContainsValue(ManagedRealmMap map, Object? value) { return using((Arena arena) { - final key_value = _toRealmValue(value, arena); + // TODO: how should this behave for collections + final value_value = _toRealmValue(value, arena); final out_index = arena(); - _realmLib.invokeGetBool(() => _realmLib.realm_dictionary_contains_value(map.handle._pointer, key_value.ref, out_index)); + _realmLib.invokeGetBool(() => _realmLib.realm_dictionary_contains_value(map.handle._pointer, value_value.ref, out_index)); return out_index.value > -1; }); } @@ -1628,8 +1650,16 @@ class _RealmCore { void mapInsertValue(RealmMapHandle handle, String key, Object? value) { using((Arena arena) { final key_value = _toRealmValue(key, arena); - final realm_value = _toRealmValue(value, arena); - _realmLib.invokeGetBool(() => _realmLib.realm_dictionary_insert(handle._pointer, key_value.ref, realm_value.ref, nullptr, nullptr)); + final value_value = _toRealmValue(value, arena); + _realmLib.invokeGetBool(() => _realmLib.realm_dictionary_insert(handle._pointer, key_value.ref, value_value.ref, nullptr, nullptr)); + }); + } + + void mapInsertCollection(RealmMapHandle handle, Realm realm, String key, RealmValue value) { + using((Arena arena) { + final key_value = _toRealmValue(key, arena); + _createCollection(realm, value, () => _realmLib.realm_dictionary_insert_list(handle._pointer, key_value.ref), + () => _realmLib.realm_dictionary_insert_dictionary(handle._pointer, key_value.ref)); }); } @@ -2994,6 +3024,36 @@ class _RealmCore { final configHandle = _createConfig(config); _realmLib.invokeGetBool(() => _realmLib.realm_convert_with_config(realm.handle._pointer, configHandle._pointer, false)); } + + void _createCollection(Realm realm, RealmValue value, Pointer Function() createList, Pointer Function() createMap) { + CollectionHandleBase? collectionHandle; + try { + switch (value.collectionType) { + case RealmCollectionType.list: + final listPointer = _realmLib.invokeGetPointer(createList); + final listHandle = RealmListHandle._(listPointer, realm.handle); + collectionHandle = listHandle; + + final list = realm.createList(listHandle, null); + for (final item in value.value as List) { + list.add(item); + } + case RealmCollectionType.map: + final mapPointer = _realmLib.invokeGetPointer(createMap); + final mapHandle = RealmMapHandle._(mapPointer, realm.handle); + collectionHandle = mapHandle; + + final map = realm.createMap(mapHandle, null); + for (final kvp in (value.value as Map).entries) { + map[kvp.key] = kvp.value; + } + default: + throw RealmStateError('_createCollection invoked with type that is not list or map.'); + } + } finally { + collectionHandle?.release(); + } + } } class LastError { @@ -3116,6 +3176,10 @@ abstract class RootedHandleBase extends HandleBase { } } +abstract class CollectionHandleBase extends RootedHandleBase { + CollectionHandleBase(RealmHandle root, Pointer pointer, int size) : super(root, pointer, size); +} + class SchemaHandle extends HandleBase { SchemaHandle._(Pointer pointer) : super(pointer, 24); @@ -3182,7 +3246,7 @@ class RealmResultsHandle extends RootedHandleBase { RealmResultsHandle._(Pointer pointer, RealmHandle root) : super(root, pointer, 872); } -class RealmListHandle extends RootedHandleBase { +class RealmListHandle extends CollectionHandleBase { RealmListHandle._(Pointer pointer, RealmHandle root) : super(root, pointer, 88); } @@ -3190,7 +3254,7 @@ class RealmSetHandle extends RootedHandleBase { RealmSetHandle._(Pointer pointer, RealmHandle root) : super(root, pointer, 96); } -class RealmMapHandle extends RootedHandleBase { +class RealmMapHandle extends CollectionHandleBase { RealmMapHandle._(Pointer pointer, RealmHandle root) : super(root, pointer, 96); // TODO: check size } @@ -3327,6 +3391,10 @@ extension _RealmLibraryEx on RealmLibrary { Pointer _toRealmValue(Object? value, Allocator allocator) { final realm_value = allocator(); + if (value is RealmValue && value.type.isCollection) { + throw RealmError( + "Don't use _toPrimitiveValue if the value may contain collections. Use storeValue instead. This is a bug in the Realm Flutter SDK and should be reported to https://github.com/realm/realm-dart/issues/new"); + } _intoRealmValue(value, realm_value.ref, allocator); return realm_value; } @@ -3355,6 +3423,8 @@ void _intoRealmQueryArg(Object? value, Pointer realm_query_ar void _intoRealmValueHack(Object? value, realm_value realm_value, Allocator allocator) { if (value is GeoShape) { _intoRealmValue(value.toString(), realm_value, allocator); + } else if (value is RealmValueType) { + _intoRealmValue(value.toQueryArgString(), realm_value, allocator); } else { _intoRealmValue(value, realm_value, allocator); } @@ -3409,8 +3479,6 @@ void _intoRealmValue(Object? value, realm_value realm_value, Allocator allocator realm_value.values.timestamp.seconds = seconds; realm_value.values.timestamp.nanoseconds = nanoseconds; realm_value.type = realm_value_type.RLM_TYPE_TIMESTAMP; - } else if (value is RealmValue) { - return _intoRealmValue(value.value, realm_value, allocator); } else if (value is Decimal128) { realm_value.values.decimal128 = value.value; realm_value.type = realm_value_type.RLM_TYPE_DECIMAL128; @@ -3419,22 +3487,32 @@ void _intoRealmValue(Object? value, realm_value realm_value, Allocator allocator realm_value.values.binary.size = value.length; realm_value.values.binary.data = allocator(value.length); realm_value.values.binary.data.asTypedList(value.length).setAll(0, value); + } else if (value is RealmValue) { + if (value.type == List) { + realm_value.type = realm_value_type.RLM_TYPE_LIST; + } else if (value.type == Map) { + realm_value.type = realm_value_type.RLM_TYPE_DICTIONARY; + } else { + return _intoRealmValue(value.value, realm_value, allocator); + } } else { throw RealmException("Property type ${value.runtimeType} not supported"); } } extension on Pointer { - Object? toDartValue([Realm? realm]) { + Object? toDartValue(Realm realm, Pointer Function()? getList, Pointer Function()? getMap) { if (this == nullptr) { throw RealmException("Can not convert nullptr realm_value to Dart value"); } - return ref.toDartValue(realm); + return ref.toDartValue(realm: realm, getList: getList, getMap: getMap); } } extension on realm_value_t { - Object? toDartValue([Realm? realm]) { + Object? toPrimitiveValue() => toDartValue(realm: null, getList: null, getMap: null); + + Object? toDartValue({required Realm? realm, required Pointer Function()? getList, required Pointer Function()? getMap}) { switch (type) { case realm_value_type.RLM_TYPE_NULL: return null; @@ -3471,6 +3549,22 @@ extension on realm_value_t { case realm_value_type.RLM_TYPE_UUID: final listInt = values.uuid.bytes.toList(16); return Uuid.fromBytes(Uint8List.fromList(listInt).buffer); + case realm_value_type.RLM_TYPE_LIST: + if (getList == null || realm == null) { + throw RealmException('toDartValue called with a list argument but without a list getter'); + } + + final listPointer = _realmLib.invokeGetPointer(() => getList()); + final listHandle = RealmListHandle._(listPointer, realm.handle); + return realm.createList(listHandle, null); + case realm_value_type.RLM_TYPE_DICTIONARY: + if (getMap == null || realm == null) { + throw RealmException('toDartValue called with a list argument but without a list getter'); + } + + final mapPointer = _realmLib.invokeGetPointer(() => getMap()); + final mapHandle = RealmMapHandle._(mapPointer, realm.handle); + return realm.createMap(mapHandle, null); default: throw RealmException("realm_value_type $type not supported"); } @@ -3597,7 +3691,7 @@ extension on Pointer { final compensatingWrite = this[i]; final reason = compensatingWrite.reason.cast().toDartString(); final object_name = compensatingWrite.object_name.cast().toDartString(); - final primary_key = compensatingWrite.primary_key.toDartValue(); + final primary_key = compensatingWrite.primary_key.toPrimitiveValue(); compensatingWrites.add(CompensatingWriteInfo(object_name, reason, RealmValue.from(primary_key))); } return compensatingWrites; @@ -3798,3 +3892,23 @@ extension on realm_error { return LastError(error, message, user_code_error.toUserCodeError()); } } + +extension on RealmValueType { + String toQueryArgString() { + return switch (this) { + RealmValueType.nullValue => 'null', + RealmValueType.boolean => 'bool', + RealmValueType.string => 'string', + RealmValueType.int => 'int', + RealmValueType.double => 'double', + RealmValueType.object => 'link', + RealmValueType.objectId => 'objectid', + RealmValueType.dateTime => 'date', + RealmValueType.decimal => 'decimal', + RealmValueType.uuid => 'uuid', + RealmValueType.binary => 'binary', + RealmValueType.list => 'array', + RealmValueType.map => 'object', + }; + } +} diff --git a/packages/realm_dart/lib/src/realm_class.dart b/packages/realm_dart/lib/src/realm_class.dart index e7655195a..6f33d1760 100644 --- a/packages/realm_dart/lib/src/realm_class.dart +++ b/packages/realm_dart/lib/src/realm_class.dart @@ -19,6 +19,7 @@ import 'dart:async'; import 'dart:ffi'; import 'dart:io'; +import 'dart:typed_data'; import 'package:cancellation_token/cancellation_token.dart'; import 'package:collection/collection.dart'; @@ -62,6 +63,7 @@ export 'package:realm_common/realm_common.dart' RealmStateError, RealmUnsupportedSetError, RealmValue, + RealmValueType, Uuid; // always expose with `show` to explicitly control the public API surface @@ -1060,3 +1062,33 @@ class RealmAsyncOpenProgressNotificationsController implements ProgressNotificat _tokenHandle = null; } } + +/// @nodoc +extension RealmValueInternal on RealmValue { + RealmCollectionType? get collectionType { + if (type == RealmValueType.list) return RealmCollectionType.list; + if (type == RealmValueType.map) return RealmCollectionType.map; + return null; + } +} + +/// Extensions on RealmValue providing convenience conversion operators +extension RealmValueConvenience on RealmValue { + /// Casts [value] to a List. It will throw an exception if [value] is not a list. + RealmList asList() { + if (value is RealmList) { + return as>(); + } + + return RealmListInternal.createFromList(as>()); + } + + /// Casts [value] to a Map. It will throw an exception if [value] is not a map. + RealmMap asMap() { + if (value is RealmMap) { + return as>(); + } + + return RealmMapInternal.createFromMap(as>()); + } +} diff --git a/packages/realm_dart/lib/src/realm_object.dart b/packages/realm_dart/lib/src/realm_object.dart index b1b313306..41045f202 100644 --- a/packages/realm_dart/lib/src/realm_object.dart +++ b/packages/realm_dart/lib/src/realm_object.dart @@ -198,7 +198,20 @@ class RealmCoreAccessor implements RealmAccessor { case RealmCollectionType.set: final handle = realmCore.getSetProperty(object, propertyMeta.key); final setMetadata = propertyMeta.objectType == null ? null : object.realm.metadata.getByName(propertyMeta.objectType!); - return RealmSetInternal.create(handle, object.realm, setMetadata); + if (setMetadata != null && _isTypeGenericObject()) { + switch (setMetadata.schema.baseType) { + case ObjectType.realmObject: + return object.realm.createSet(handle, setMetadata); + case ObjectType.embeddedObject: + return object.realm.createSet(handle, setMetadata); + case ObjectType.asymmetricObject: + return object.realm.createSet(handle, setMetadata); + default: + throw RealmError('Set of ${setMetadata.schema.baseType} is not supported yet'); + } + } + + return object.realm.createSet(handle, setMetadata); case RealmCollectionType.map: final handle = realmCore.getMapProperty(object, propertyMeta.key); final mapMetadata = propertyMeta.objectType == null ? null : object.realm.metadata.getByName(propertyMeta.objectType!); @@ -260,6 +273,11 @@ class RealmCoreAccessor implements RealmAccessor { void set(RealmObjectBase object, String name, Object? value, {bool isDefault = false, bool update = false}) { final propertyMeta = metadata[name]; try { + if (value is RealmValue && value.type.isCollection) { + realmCore.objectSetCollection(object, propertyMeta.key, value); + return; + } + if (value is RealmList) { final handle = realmCore.getListProperty(object, propertyMeta.key); if (update) { @@ -766,6 +784,22 @@ class DynamicRealmObject { return RealmObjectBase.get(_obj, name) as RealmList; } + /// Gets a set by the property name. If a generic type is specified, the property + /// type will be validated against the type. Otherwise, a `RealmSet` will be + /// returned. + RealmSet getSet(String name) { + _validatePropertyType(name, RealmCollectionType.set); + return RealmObjectBase.get(_obj, name) as RealmSet; + } + + /// Gets a map by the property name. If a generic type is specified, the property + /// type will be validated against the type. Otherwise, a `RealmMap` will be + /// returned. + RealmMap getMap(String name) { + _validatePropertyType(name, RealmCollectionType.map); + return RealmObjectBase.get(_obj, name) as RealmMap; + } + RealmPropertyMetadata? _validatePropertyType(String name, RealmCollectionType expectedCollectionType) { final accessor = _obj.accessor; if (accessor is RealmCoreAccessor) { @@ -781,7 +815,7 @@ class DynamicRealmObject { // If the user passed in a type argument, we should validate its nullability; if they invoked // the method without a type arg, we don't - if (T != _typeOf() && prop.isNullable != null is T) { + if (T != _typeOf() && T != _typeOf() && prop.isNullable != null is T) { throw RealmException( "Property '$name' on class '${accessor.metadata.schema.name}' is ${prop.isNullable ? 'nullable' : 'required'} but the generic argument passed to get is $T."); } diff --git a/packages/realm_dart/lib/src/realm_property.dart b/packages/realm_dart/lib/src/realm_property.dart index e43ed48ca..eaea2e7fb 100644 --- a/packages/realm_dart/lib/src/realm_property.dart +++ b/packages/realm_dart/lib/src/realm_property.dart @@ -34,10 +34,6 @@ class SchemaProperty { /// `true` if the property is a primary key final bool primaryKey; - /// `true` if the property is indexed - @Deprecated("Use indexType instead") - bool get indexed => indexType == RealmIndexType.regular; - /// Returns the index type for this property final RealmIndexType? indexType; diff --git a/packages/realm_dart/lib/src/results.dart b/packages/realm_dart/lib/src/results.dart index 06db973e0..d754de2f3 100644 --- a/packages/realm_dart/lib/src/results.dart +++ b/packages/realm_dart/lib/src/results.dart @@ -81,6 +81,17 @@ class RealmResults extends Iterable with RealmEntity imple if (element is RealmObjectBase && !element.isManaged) { throw RealmStateError('Cannot call $methodName on a results with an element that is an unmanaged object'); } + + if (element is RealmValue) { + if (element.type.isCollection) { + return -1; + } + + if (element.value is RealmObjectBase && !(element.value as RealmObjectBase).isManaged) { + return -1; + } + } + if (start < 0) start = 0; start += _skipOffset; final index = realmCore.resultsFind(this, element); @@ -304,7 +315,6 @@ class RealmResultsChanges extends RealmCollectionChanges { RealmResultsChanges._(super.handle, this.results); @override - @Deprecated("`isCleared` is deprecated. Use `isEmpty` of the results collection instead.") bool get isCleared => results.isEmpty; } diff --git a/packages/realm_dart/lib/src/session.dart b/packages/realm_dart/lib/src/session.dart index 07058181e..0664c1183 100644 --- a/packages/realm_dart/lib/src/session.dart +++ b/packages/realm_dart/lib/src/session.dart @@ -310,384 +310,3 @@ enum SyncErrorCode { final int code; const SyncErrorCode(this.code); } - -/// The category of a [SyncError]. -@Deprecated("Sync errors are not classified by SyncErrorCategory anymore.") -enum SyncErrorCategory { - /// The error originated from the client - client, - - /// The error originated from the connection - connection, - - /// The error originated from the session - session, - - /// Web socket error - webSocket, - - /// Another low-level system error occurred - system, - - /// The category is unknown - unknown, -} - -/// Protocol errors discovered by the client. -/// -/// These errors will terminate the network connection -/// (disconnect all sessions associated with the affected connection), -/// and the error will be reported via the connection state change listeners of the affected sessions. -@Deprecated("Use SyncError or its subclasses instead.") -enum SyncClientErrorCode { - /// Connection closed (no error) - connectionClosed(100), - - /// Unknown type of input message - unknownMessage(101), - - /// Bad syntax in input message head - badSyntax(102), - - /// Limits exceeded in input message - limitsExceeded(103), - - /// Bad session identifier in input message - badSessionIdent(104), - - /// Bad input message order - badMessageOrder(105), - - /// Bad client file identifier (IDENT) - badClientFileIdent(106), - - /// Bad progress information (DOWNLOAD) - badProgress(107), - - /// Bad syntax in changeset header (DOWNLOAD) - badChangesetHeaderSyntax(108), - - /// Bad changeset size in changeset header (DOWNLOAD) - badChangesetSize(109), - - /// Bad origin file identifier in changeset header (DOWNLOAD) - badOriginFileIdent(110), - - /// Bad server version in changeset header (DOWNLOAD) - badServerVersion(111), - - /// Bad changeset (DOWNLOAD) - badChangeset(112), - - /// Bad request identifier (MARK) - badRequestIdent(113), - - /// Bad error code (ERROR), - badErrorCode(114), - - /// Bad compression (DOWNLOAD) - badCompression(115), - - /// Bad last integrated client version in changeset header (DOWNLOAD) - badClientVersion(116), - - /// SSL server certificate rejected - sslServerCertRejected(117), - - /// Timeout on reception of PONG respone message - pongTimeout(118), - - /// Bad client file identifier salt (IDENT) - badClientFileIdentSalt(119), - - /// Bad file identifier (ALLOC) - badFileIdent(120), - - /// Sync connection was not fully established in time - connectTimeout(121), - - /// Bad timestamp (PONG) - badTimestamp(122), - - /// Bad or missing protocol version information from server - badProtocolFromServer(123), - - /// Protocol version negotiation failed: Client is too old for server - clientTooOldForServer(124), - - /// Protocol version negotiation failed: Client is too new for server - clientTooNewForServer(125), - - /// Protocol version negotiation failed: No version supported by both client and server - protocolMismatch(126), - - /// Bad values in state message (STATE) - badStateMessage(127), - - /// Requested feature missing in negotiated protocol version - missingProtocolFeature(128), - - /// Failed to establish HTTP tunnel with configured proxy - httpTunnelFailed(131), - - /// A fatal error was encountered which prevents completion of a client reset - autoClientResetFailure(132), - - /// Unknown Sync client error code - unknown(9999); - - static final Map _valuesMap = {for (var value in SyncClientErrorCode.values) value.code: value}; - - static SyncClientErrorCode fromInt(int code) { - return SyncClientErrorCode._valuesMap[code] ?? SyncClientErrorCode.unknown; - } - - final int code; - - const SyncClientErrorCode(this.code); -} - -/// Protocol connection errors discovered by the server, and reported to the client -/// -/// These errors will be reported via the error handlers of the affected sessions. -@Deprecated("Use SyncError or its subclasses instead of using error codes.") -enum SyncConnectionErrorCode { - // Connection level and protocol errors - /// Connection closed (no error) - connectionClosed(100), - - /// Other connection level error - otherError(101), - - /// Unknown type of input message - unknownMessage(102), - - /// Bad syntax in input message head - badSyntax(103), - - /// Limits exceeded in input message - limitsExceeded(104), - - /// Wrong protocol version (CLIENT) (obsolete) - wrongProtocolVersion(105), - - /// Bad session identifier in input message - badSessionIdent(106), - - /// Overlapping reuse of session identifier (BIND) - reuseOfSessionIdent(107), - - /// Client file bound in other session (IDENT) - boundInOtherSession(108), - - /// Bad input message order - badMessageOrder(109), - - /// Error in decompression (UPLOAD) - badDecompression(110), - - /// Bad syntax in a changeset header (UPLOAD) - badChangesetHeaderSyntax(111), - - /// Bad size specified in changeset header (UPLOAD) - badChangesetSize(112), - - /// Connected with wrong wire protocol - should switch to FLX sync - switchToFlxSync(113), - - /// Connected with wrong wire protocol - should switch to PBS - switchToPbs(114), - - /// Unknown Sync connection error code - unknown(9999); - - static final Map _valuesMap = {for (var value in SyncConnectionErrorCode.values) value.code: value}; - - static SyncConnectionErrorCode fromInt(int code) { - return SyncConnectionErrorCode._valuesMap[code] ?? SyncConnectionErrorCode.unknown; - } - - final int code; - const SyncConnectionErrorCode(this.code); -} - -/// Protocol session errors discovered by the server, and reported to the client -/// -/// These errors will be reported via the error handlers of the affected sessions. -@Deprecated("Use SyncError or its subclasses instead of using error codes.") -enum SyncSessionErrorCode { - /// Session closed (no error) - sessionClosed(200), - - /// Other session level error - otherSessionError(201), - - /// Access token expired - tokenExpired(202), - - /// Bad user authentication (BIND) - badAuthentication(203), - - /// Illegal Realm path (BIND) - illegalRealmPath(204), - - /// No such Realm (BIND) - noSuchRealm(205), - - /// Permission denied (BIND) - permissionDenied(206), - - /// Bad server file identifier (IDENT) (obsolete!) - badServerFileIdent(207), - - /// Bad client file identifier (IDENT) - badClientFileIdent(208), - - /// Bad server version (IDENT, UPLOAD, TRANSACT) - badServerVersion(209), - - /// Bad client version (IDENT, UPLOAD) - badClientVersion(210), - - /// Diverging histories (IDENT) - divergingHistories(211), - - /// Bad changeset (UPLOAD) - badChangeset(212), - - /// Partial sync disabled (BIND) - partialSyncDisabled(214), - - /// Unsupported session-level feature - unsupportedSessionFeature(215), - - /// Bad origin file identifier (UPLOAD) - badOriginFileIdent(216), - - /// Synchronization no longer possible for client-side file - badClientFile(217), - - /// Server file was deleted while session was bound to it - serverFileDeleted(218), - - /// Client file has been blacklisted (IDENT) - clientFileBlacklisted(219), - - /// User has been blacklisted (BIND) - userBlacklisted(220), - - /// Serialized transaction before upload completion - transactBeforeUpload(221), - - /// Client file has expired - clientFileExpired(222), - - /// User mismatch for client file identifier (IDENT) - userMismatch(223), - - /// Too many sessions in connection (BIND) - tooManySessions(224), - - /// Invalid schema change (UPLOAD) - invalidSchemaChange(225), - - /// Client query is invalid/malformed (IDENT, QUERY) - badQuery(226), - - /// Client tried to create an object that already exists outside their (()UPLOAD) - objectAlreadyExists(227), - - /// Server permissions for this file ident have changed since the last time it (used) (IDENT) - serverPermissionsChanged(228), - - /// Client tried to open a session before initial sync is complete (BIND) - initialSyncNotCompleted(229), - - /// Client attempted a write that is disallowed by permissions, or modifies an object - /// outside the current query - requires client reset (UPLOAD) - writeNotAllowed(230), - - /// Client attempted a write that is disallowed by permissions, or modifies an object - /// outside the current query, and the server undid the modification (UPLOAD) - compensatingWrite(231), - - /// Bad progress information (ERROR) - sessionBadProgress(233), - - /// Unknown Sync session error code - unknown(9999); - - static final Map _valuesMap = {for (var value in SyncSessionErrorCode.values) value.code: value}; - - static SyncSessionErrorCode fromInt(int code) { - return SyncSessionErrorCode._valuesMap[code] ?? SyncSessionErrorCode.unknown; - } - - final int code; - const SyncSessionErrorCode(this.code); -} - -/// Protocol network resolution errors. -/// -/// These errors will be reported via the error handlers of the affected sessions. -/// This enum is deprecated and it will be removed. -/// Use [SyncWebSocketErrorCode] instead. -@Deprecated("Use SyncWebSocketErrorCode instead") -enum SyncResolveErrorCode { - /// Host not found (authoritative). - hostNotFound(1), - - /// Host not found (non-authoritative). - hostNotFoundTryAgain(2), - - /// The query is valid but does not have associated address data. - noData(3), - - /// A non-recoverable error occurred. - noRecovery(4), - - /// The service is not supported for the given socket type. - serviceNotFound(5), - - /// The socket type is not supported. - socketTypeNotSupported(6), - - /// Unknown resolve errors - unknown(1000); - - static final Map _valuesMap = {for (var value in SyncResolveErrorCode.values) value.code: value}; - - static SyncResolveErrorCode fromInt(int code) { - return SyncResolveErrorCode._valuesMap[code] ?? SyncResolveErrorCode.unknown; - } - - final int code; - const SyncResolveErrorCode(this.code); -} - -/// Web socket errors. -/// -/// These errors will be reported via the error handlers of the affected sessions. -@Deprecated("Use SyncError or its subclasses instead of using error codes.") -enum SyncWebSocketErrorCode { - /// Web socket resolution failed - websocketResolveFailed(4400), - - /// Web socket connection closed by the client - websocketConnectionClosedClient(4401), - - /// Web socket connection closed by the server - websocketConnectionClosedServer(4402), - - /// Unknown resolve errors - unknown(9999); - - static final Map _valuesMap = {for (var value in SyncWebSocketErrorCode.values) value.code: value}; - - static SyncWebSocketErrorCode fromInt(int code) { - return SyncWebSocketErrorCode._valuesMap[code] ?? SyncWebSocketErrorCode.unknown; - } - - final int code; - const SyncWebSocketErrorCode(this.code); -} diff --git a/packages/realm_dart/lib/src/set.dart b/packages/realm_dart/lib/src/set.dart index 3e6edf62f..b1e9c0826 100644 --- a/packages/realm_dart/lib/src/set.dart +++ b/packages/realm_dart/lib/src/set.dart @@ -107,6 +107,13 @@ abstract class RealmSet extends SetBase with RealmEntity i class UnmanagedRealmSet extends collection.DelegatingSet with RealmEntity implements RealmSet { UnmanagedRealmSet([Set? items]) : super(items ?? {}); + @override + bool add(T value) { + _throwOnRealmValueCollection(value); + + return super.add(value); + } + @override // ignore: unused_element RealmObjectMetadata? get _metadata => throw RealmError("Unmanaged RealmSets don't have metadata associated with them."); @@ -143,6 +150,8 @@ class ManagedRealmSet with RealmEntity, SetMixin implement @override bool add(T value) { + _throwOnRealmValueCollection(value); + if (_isManagedRealmObject(value)) { //It is valid to call `add` with managed objects already in the set. _ensureManagedByThis(value, "add"); @@ -330,6 +339,9 @@ class RealmSetChanges extends RealmCollectionChanges { final RealmSet set; RealmSetChanges._(super.handle, this.set); + + /// `true` if the underlying set was deleted. + bool get isCollectionDeleted => changes.isDeleted; } /// @nodoc @@ -378,3 +390,12 @@ extension RealmSetOfObject on RealmSet { return RealmResultsInternal.create(handle, realm, _metadata); } } + +extension on RealmSet { + void _throwOnRealmValueCollection(Object? value) { + // There's no API in Core that would allow us to store a collection inside a Set. + if (value is RealmValue && value.type.isCollection) { + throw RealmStateError('Storing collections inside Set is not supported'); + } + } +} diff --git a/packages/realm_dart/lib/src/user.dart b/packages/realm_dart/lib/src/user.dart index 287e67049..c622410b6 100644 --- a/packages/realm_dart/lib/src/user.dart +++ b/packages/realm_dart/lib/src/user.dart @@ -99,12 +99,6 @@ class User { return realmCore.userGetDeviceId(this); } - /// Gets the [AuthProviderType] this [User] is currently logged in with. - @Deprecated("Get the auth provider from the user identity.") - AuthProviderType get provider { - return identities.first.provider; - } - /// Gets the profile information for this [User]. UserProfile get profile { return realmCore.userGetProfileData(this); diff --git a/packages/realm_dart/pubspec.yaml b/packages/realm_dart/pubspec.yaml index e6e876b4b..07f44ac95 100644 --- a/packages/realm_dart/pubspec.yaml +++ b/packages/realm_dart/pubspec.yaml @@ -1,6 +1,6 @@ name: realm_dart description: The official Realm SDK for Dart. Realm is a mobile database - an alternative to SQLite and key-value stores. -version: 1.9.0 +version: 2.0.0-alpha.2 homepage: https://www.realm.io repository: https://github.com/realm/realm-dart diff --git a/packages/realm_dart/src/realm-core b/packages/realm_dart/src/realm-core index d12c38de2..677c9c28e 160000 --- a/packages/realm_dart/src/realm-core +++ b/packages/realm_dart/src/realm-core @@ -1 +1 @@ -Subproject commit d12c38de2c8e4043ca1cd0934e1056c6eb214892 +Subproject commit 677c9c28e29b3a0899dc46b0054f2354cd47a0d1 diff --git a/packages/realm_dart/src/realm_dart.cpp b/packages/realm_dart/src/realm_dart.cpp index 7ac878815..0487b08ae 100644 --- a/packages/realm_dart/src/realm_dart.cpp +++ b/packages/realm_dart/src/realm_dart.cpp @@ -86,7 +86,7 @@ RLM_API void realm_dart_invoke_unlock_callback(realm_userdata_t error, void* unl // Stamped into the library by the build system (see prepare-release.yml) // Keep this method as it is written and do not format it. // We have a github workflow that looks for and replaces this string as it is written here. -RLM_API const char* realm_dart_library_version() { return "1.9.0"; } +RLM_API const char* realm_dart_library_version() { return "2.0.0-alpha.2"; } //for debugging only // RLM_API void realm_dart_gc() { diff --git a/packages/realm_dart/test/data/realm_files/old-format.realm b/packages/realm_dart/test/data/realm_files/old-format.realm index 758ed8625..57fb28c08 100644 Binary files a/packages/realm_dart/test/data/realm_files/old-format.realm and b/packages/realm_dart/test/data/realm_files/old-format.realm differ diff --git a/packages/realm_dart/test/dynamic_realm_test.dart b/packages/realm_dart/test/dynamic_realm_test.dart index e35bf8d17..119e7a199 100644 --- a/packages/realm_dart/test/dynamic_realm_test.dart +++ b/packages/realm_dart/test/dynamic_realm_test.dart @@ -100,13 +100,49 @@ void main() { AllTypes _getEmptyAllTypes() => AllTypes('', false, DateTime(0).toUtc(), 0, objectId, uuid, 0, Decimal128.zero); AllCollections _getPopulatedAllCollections() => AllCollections( - strings: ['abc', 'def'], - bools: [true, false], - dates: [date, DateTime(0).toUtc()], - doubles: [-123.456, 555.666], - objectIds: [objectId, objectId], - uuids: [uuid, uuid], - ints: [-987, 123]); + stringList: ['abc', 'def'], + boolList: [true, false], + dateList: [date, DateTime(0).toUtc()], + doubleList: [-123.456, 555.666], + objectIdList: [objectId, objectId], + uuidList: [uuid, uuid], + intList: [-987, 123], + nullableStringList: ['abc', null], + nullableBoolList: [true, null], + nullableDateList: [date, null], + nullableDoubleList: [555.666, null], + nullableObjectIdList: [objectId, null], + nullableUuidList: [uuid, null], + nullableIntList: [123, null], + stringSet: {'abc', 'def'}, + boolSet: {true, false}, + dateSet: {date, DateTime(0).toUtc()}, + doubleSet: {-123.456, 555.666}, + objectIdSet: {objectId, objectId}, + uuidSet: {uuid, uuid}, + intSet: {-987, 123}, + nullableStringSet: {'abc', null}, + nullableBoolSet: {true, null}, + nullableDateSet: {date, null}, + nullableDoubleSet: {555.666, null}, + nullableObjectIdSet: {objectId, null}, + nullableUuidSet: {uuid, null}, + nullableIntSet: {123, null}, + stringMap: {'a': 'abc', 'b': 'def'}, + boolMap: {'a': true, 'b': false}, + dateMap: {'a': date, 'b': DateTime(0).toUtc()}, + doubleMap: {'a': -123.456, 'b': 555.666}, + objectIdMap: {'a': objectId, 'b': objectId}, + uuidMap: {'a': uuid, 'b': uuid}, + intMap: {'a': -987, 'b': 123}, + nullableStringMap: {'a': 'abc', 'b': null}, + nullableBoolMap: {'a': true, 'b': null}, + nullableDateMap: {'a': date, 'b': null}, + nullableDoubleMap: {'a': 555.666, 'b': null}, + nullableObjectIdMap: {'a': objectId, 'b': null}, + nullableUuidMap: {'a': uuid, 'b': null}, + nullableIntMap: {'a': 123, 'b': null}, + ); void _validateDynamic(RealmObject actual, AllTypes expected) { expect(actual.dynamic.get('stringProp'), expected.stringProp); @@ -168,40 +204,212 @@ void main() { expect(actualDynamic.nullableDecimalProp, expected.nullableDecimalProp); } - void _validateDynamicLists(RealmObject actual, AllCollections expected) { - expect(actual.dynamic.getList('strings'), expected.strings); - expect(actual.dynamic.getList('strings'), expected.strings); + void _validateDynamicCollections(RealmObject actual, AllCollections expected) { + dynamic actualDynamic = actual; - expect(actual.dynamic.getList('bools'), expected.bools); - expect(actual.dynamic.getList('bools'), expected.bools); + // Lists + expect(actual.dynamic.getList('stringList'), expected.stringList); + expect(actual.dynamic.getList('stringList'), expected.stringList); - expect(actual.dynamic.getList('dates'), expected.dates); - expect(actual.dynamic.getList('dates'), expected.dates); + expect(actual.dynamic.getList('boolList'), expected.boolList); + expect(actual.dynamic.getList('boolList'), expected.boolList); - expect(actual.dynamic.getList('doubles'), expected.doubles); - expect(actual.dynamic.getList('doubles'), expected.doubles); + expect(actual.dynamic.getList('dateList'), expected.dateList); + expect(actual.dynamic.getList('dateList'), expected.dateList); - expect(actual.dynamic.getList('objectIds'), expected.objectIds); - expect(actual.dynamic.getList('objectIds'), expected.objectIds); + expect(actual.dynamic.getList('doubleList'), expected.doubleList); + expect(actual.dynamic.getList('doubleList'), expected.doubleList); - expect(actual.dynamic.getList('uuids'), expected.uuids); - expect(actual.dynamic.getList('uuids'), expected.uuids); + expect(actual.dynamic.getList('objectIdList'), expected.objectIdList); + expect(actual.dynamic.getList('objectIdList'), expected.objectIdList); - expect(actual.dynamic.getList('ints'), expected.ints); - expect(actual.dynamic.getList('ints'), expected.ints); + expect(actual.dynamic.getList('uuidList'), expected.uuidList); + expect(actual.dynamic.getList('uuidList'), expected.uuidList); - expect(actual.dynamic.getList('decimals'), expected.decimals); - expect(actual.dynamic.getList('decimals'), expected.decimals); + expect(actual.dynamic.getList('intList'), expected.intList); + expect(actual.dynamic.getList('intList'), expected.intList); - dynamic actualDynamic = actual; - expect(actualDynamic.strings, expected.strings); - expect(actualDynamic.bools, expected.bools); - expect(actualDynamic.dates, expected.dates); - expect(actualDynamic.doubles, expected.doubles); - expect(actualDynamic.objectIds, expected.objectIds); - expect(actualDynamic.uuids, expected.uuids); - expect(actualDynamic.ints, expected.ints); - expect(actualDynamic.decimals, expected.decimals); + expect(actual.dynamic.getList('decimalList'), expected.decimalList); + expect(actual.dynamic.getList('decimalList'), expected.decimalList); + + expect(actualDynamic.stringList, expected.stringList); + expect(actualDynamic.boolList, expected.boolList); + expect(actualDynamic.dateList, expected.dateList); + expect(actualDynamic.doubleList, expected.doubleList); + expect(actualDynamic.objectIdList, expected.objectIdList); + expect(actualDynamic.uuidList, expected.uuidList); + expect(actualDynamic.intList, expected.intList); + expect(actualDynamic.decimalList, expected.decimalList); + + // Nullable lists + expect(actual.dynamic.getList('nullableStringList'), expected.nullableStringList); + expect(actual.dynamic.getList('nullableStringList'), expected.nullableStringList); + + expect(actual.dynamic.getList('nullableBoolList'), expected.nullableBoolList); + expect(actual.dynamic.getList('nullableBoolList'), expected.nullableBoolList); + + expect(actual.dynamic.getList('nullableDateList'), expected.nullableDateList); + expect(actual.dynamic.getList('nullableDateList'), expected.nullableDateList); + + expect(actual.dynamic.getList('nullableDoubleList'), expected.nullableDoubleList); + expect(actual.dynamic.getList('nullableDoubleList'), expected.nullableDoubleList); + + expect(actual.dynamic.getList('nullableObjectIdList'), expected.nullableObjectIdList); + expect(actual.dynamic.getList('nullableObjectIdList'), expected.nullableObjectIdList); + + expect(actual.dynamic.getList('nullableUuidList'), expected.nullableUuidList); + expect(actual.dynamic.getList('nullableUuidList'), expected.nullableUuidList); + + expect(actual.dynamic.getList('nullableIntList'), expected.nullableIntList); + expect(actual.dynamic.getList('nullableIntList'), expected.nullableIntList); + + expect(actual.dynamic.getList('nullableDecimalList'), expected.nullableDecimalList); + expect(actual.dynamic.getList('nullableDecimalList'), expected.nullableDecimalList); + + expect(actualDynamic.nullableStringList, expected.nullableStringList); + expect(actualDynamic.nullableBoolList, expected.nullableBoolList); + expect(actualDynamic.nullableDateList, expected.nullableDateList); + expect(actualDynamic.nullableDoubleList, expected.nullableDoubleList); + expect(actualDynamic.nullableObjectIdList, expected.nullableObjectIdList); + expect(actualDynamic.nullableUuidList, expected.nullableUuidList); + expect(actualDynamic.nullableIntList, expected.nullableIntList); + expect(actualDynamic.nullableDecimalList, expected.nullableDecimalList); + + // Sets + expect(actual.dynamic.getSet('stringSet'), expected.stringSet); + expect(actual.dynamic.getSet('stringSet'), expected.stringSet); + + expect(actual.dynamic.getSet('boolSet'), expected.boolSet); + expect(actual.dynamic.getSet('boolSet'), expected.boolSet); + + expect(actual.dynamic.getSet('dateSet'), expected.dateSet); + expect(actual.dynamic.getSet('dateSet'), expected.dateSet); + + expect(actual.dynamic.getSet('doubleSet'), expected.doubleSet); + expect(actual.dynamic.getSet('doubleSet'), expected.doubleSet); + + expect(actual.dynamic.getSet('objectIdSet'), expected.objectIdSet); + expect(actual.dynamic.getSet('objectIdSet'), expected.objectIdSet); + + expect(actual.dynamic.getSet('uuidSet'), expected.uuidSet); + expect(actual.dynamic.getSet('uuidSet'), expected.uuidSet); + + expect(actual.dynamic.getSet('intSet'), expected.intSet); + expect(actual.dynamic.getSet('intSet'), expected.intSet); + + expect(actual.dynamic.getSet('decimalSet'), expected.decimalSet); + expect(actual.dynamic.getSet('decimalSet'), expected.decimalSet); + + expect(actualDynamic.stringSet, expected.stringSet); + expect(actualDynamic.boolSet, expected.boolSet); + expect(actualDynamic.dateSet, expected.dateSet); + expect(actualDynamic.doubleSet, expected.doubleSet); + expect(actualDynamic.objectIdSet, expected.objectIdSet); + expect(actualDynamic.uuidSet, expected.uuidSet); + expect(actualDynamic.intSet, expected.intSet); + expect(actualDynamic.decimalSet, expected.decimalSet); + + // Nullable sets + expect(actual.dynamic.getSet('nullableStringSet'), expected.nullableStringSet); + expect(actual.dynamic.getSet('nullableStringSet'), expected.nullableStringSet); + + expect(actual.dynamic.getSet('nullableBoolSet'), expected.nullableBoolSet); + expect(actual.dynamic.getSet('nullableBoolSet'), expected.nullableBoolSet); + + expect(actual.dynamic.getSet('nullableDateSet'), expected.nullableDateSet); + expect(actual.dynamic.getSet('nullableDateSet'), expected.nullableDateSet); + + expect(actual.dynamic.getSet('nullableDoubleSet'), expected.nullableDoubleSet); + expect(actual.dynamic.getSet('nullableDoubleSet'), expected.nullableDoubleSet); + + expect(actual.dynamic.getSet('nullableObjectIdSet'), expected.nullableObjectIdSet); + expect(actual.dynamic.getSet('nullableObjectIdSet'), expected.nullableObjectIdSet); + + expect(actual.dynamic.getSet('nullableUuidSet'), expected.nullableUuidSet); + expect(actual.dynamic.getSet('nullableUuidSet'), expected.nullableUuidSet); + + expect(actual.dynamic.getSet('nullableIntSet'), expected.nullableIntSet); + expect(actual.dynamic.getSet('nullableIntSet'), expected.nullableIntSet); + + expect(actual.dynamic.getSet('nullableDecimalSet'), expected.nullableDecimalSet); + expect(actual.dynamic.getSet('nullableDecimalSet'), expected.nullableDecimalSet); + + expect(actualDynamic.nullableStringSet, expected.nullableStringSet); + expect(actualDynamic.nullableBoolSet, expected.nullableBoolSet); + expect(actualDynamic.nullableDateSet, expected.nullableDateSet); + expect(actualDynamic.nullableDoubleSet, expected.nullableDoubleSet); + expect(actualDynamic.nullableObjectIdSet, expected.nullableObjectIdSet); + expect(actualDynamic.nullableUuidSet, expected.nullableUuidSet); + expect(actualDynamic.nullableIntSet, expected.nullableIntSet); + expect(actualDynamic.nullableDecimalSet, expected.nullableDecimalSet); + + // Maps + expect(actual.dynamic.getMap('stringMap'), expected.stringMap); + expect(actual.dynamic.getMap('stringMap'), expected.stringMap); + + expect(actual.dynamic.getMap('boolMap'), expected.boolMap); + expect(actual.dynamic.getMap('boolMap'), expected.boolMap); + + expect(actual.dynamic.getMap('dateMap'), expected.dateMap); + expect(actual.dynamic.getMap('dateMap'), expected.dateMap); + + expect(actual.dynamic.getMap('doubleMap'), expected.doubleMap); + expect(actual.dynamic.getMap('doubleMap'), expected.doubleMap); + + expect(actual.dynamic.getMap('objectIdMap'), expected.objectIdMap); + expect(actual.dynamic.getMap('objectIdMap'), expected.objectIdMap); + + expect(actual.dynamic.getMap('uuidMap'), expected.uuidMap); + expect(actual.dynamic.getMap('uuidMap'), expected.uuidMap); + + expect(actual.dynamic.getMap('intMap'), expected.intMap); + expect(actual.dynamic.getMap('intMap'), expected.intMap); + + expect(actual.dynamic.getMap('decimalMap'), expected.decimalMap); + expect(actual.dynamic.getMap('decimalMap'), expected.decimalMap); + + expect(actualDynamic.stringMap, expected.stringMap); + expect(actualDynamic.boolMap, expected.boolMap); + expect(actualDynamic.dateMap, expected.dateMap); + expect(actualDynamic.doubleMap, expected.doubleMap); + expect(actualDynamic.objectIdMap, expected.objectIdMap); + expect(actualDynamic.uuidMap, expected.uuidMap); + expect(actualDynamic.intMap, expected.intMap); + expect(actualDynamic.decimalMap, expected.decimalMap); + + // Nullable Maps + expect(actual.dynamic.getMap('nullableStringMap'), expected.nullableStringMap); + expect(actual.dynamic.getMap('nullableStringMap'), expected.nullableStringMap); + + expect(actual.dynamic.getMap('nullableBoolMap'), expected.nullableBoolMap); + expect(actual.dynamic.getMap('nullableBoolMap'), expected.nullableBoolMap); + + expect(actual.dynamic.getMap('nullableDateMap'), expected.nullableDateMap); + expect(actual.dynamic.getMap('nullableDateMap'), expected.nullableDateMap); + + expect(actual.dynamic.getMap('nullableDoubleMap'), expected.nullableDoubleMap); + expect(actual.dynamic.getMap('nullableDoubleMap'), expected.nullableDoubleMap); + + expect(actual.dynamic.getMap('nullableObjectIdMap'), expected.nullableObjectIdMap); + expect(actual.dynamic.getMap('nullableObjectIdMap'), expected.nullableObjectIdMap); + + expect(actual.dynamic.getMap('nullableUuidMap'), expected.nullableUuidMap); + expect(actual.dynamic.getMap('nullableUuidMap'), expected.nullableUuidMap); + + expect(actual.dynamic.getMap('nullableIntMap'), expected.nullableIntMap); + expect(actual.dynamic.getMap('nullableIntMap'), expected.nullableIntMap); + + expect(actual.dynamic.getMap('nullableDecimalMap'), expected.nullableDecimalMap); + expect(actual.dynamic.getMap('nullableDecimalMap'), expected.nullableDecimalMap); + + expect(actualDynamic.nullableStringMap, expected.nullableStringMap); + expect(actualDynamic.nullableBoolMap, expected.nullableBoolMap); + expect(actualDynamic.nullableDateMap, expected.nullableDateMap); + expect(actualDynamic.nullableDoubleMap, expected.nullableDoubleMap); + expect(actualDynamic.nullableObjectIdMap, expected.nullableObjectIdMap); + expect(actualDynamic.nullableUuidMap, expected.nullableUuidMap); + expect(actualDynamic.nullableIntMap, expected.nullableIntMap); + expect(actualDynamic.nullableDecimalMap, expected.nullableDecimalMap); } for (var isDynamic in [true, false]) { @@ -438,24 +646,24 @@ void main() { final obj = dynamicRealm.dynamic.all(AllCollections.schema.name).single; expect( - () => obj.dynamic.get('strings'), + () => obj.dynamic.get('stringList'), throws( - "Property 'strings' on class 'AllCollections' is 'RealmCollectionType.list' but the method used to access it expected 'RealmCollectionType.none'.")); + "Property 'stringList' on class 'AllCollections' is 'RealmCollectionType.list' but the method used to access it expected 'RealmCollectionType.none'.")); expect( - () => obj.dynamic.get('strings'), + () => obj.dynamic.get('stringList'), throws( - "Property 'strings' on class 'AllCollections' is 'RealmCollectionType.list' but the method used to access it expected 'RealmCollectionType.none'.")); + "Property 'stringList' on class 'AllCollections' is 'RealmCollectionType.list' but the method used to access it expected 'RealmCollectionType.none'.")); expect( - () => obj.dynamic.get('strings'), + () => obj.dynamic.get('stringList'), throws( - "Property 'strings' on class 'AllCollections' is 'RealmCollectionType.list' but the method used to access it expected 'RealmCollectionType.none'.")); + "Property 'stringList' on class 'AllCollections' is 'RealmCollectionType.list' but the method used to access it expected 'RealmCollectionType.none'.")); }); }); - group('RealmObject.dynamic.getList', () { - test('gets all list types', () { + group('RealmObject.dynamic.getCollection when isDynamic=$isDynamic', () { + test('gets collection of primitive types', () { final config = Configuration.local([AllCollections.schema]); final staticRealm = getRealm(config); staticRealm.write(() { @@ -465,14 +673,14 @@ void main() { final dynamicRealm = _getDynamicRealm(staticRealm); final objects = dynamicRealm.dynamic.all(AllCollections.schema.name); - final obj1 = objects.singleWhere((element) => element.dynamic.getList('strings').isNotEmpty); - final obj2 = objects.singleWhere((element) => element.dynamic.getList('strings').isEmpty); + final obj1 = objects.singleWhere((element) => element.dynamic.getList('stringList').isNotEmpty); + final obj2 = objects.singleWhere((element) => element.dynamic.getList('stringList').isEmpty); - _validateDynamicLists(obj1, _getPopulatedAllCollections()); - _validateDynamicLists(obj2, AllCollections()); + _validateDynamicCollections(obj1, _getPopulatedAllCollections()); + _validateDynamicCollections(obj2, AllCollections()); }); - test('gets collections of objects', () { + test('gets collection of objects', () { final config = Configuration.local([LinksClass.schema]); final staticRealm = getRealm(config); @@ -481,7 +689,7 @@ void main() { staticRealm.write(() { final obj1 = staticRealm.add(LinksClass(uuid1)); - staticRealm.add(LinksClass(uuid2, list: [obj1, obj1])); + staticRealm.add(LinksClass(uuid2, list: [obj1, obj1], linksSet: {obj1}, map: {'a': obj1, 'b': obj1})); }); final dynamicRealm = _getDynamicRealm(staticRealm); @@ -491,62 +699,100 @@ void main() { expect(obj1.dynamic.getList('list'), isEmpty); expect(obj1.dynamic.getList('list'), isEmpty); + expect(obj1.dynamic.getSet('linksSet'), isEmpty); + expect(obj1.dynamic.getSet('linksSet'), isEmpty); + expect(obj1.dynamic.getMap('map'), isEmpty); + expect(obj1.dynamic.getMap('map'), isEmpty); expect(obj2.dynamic.getList('list'), [obj1, obj1]); expect(obj2.dynamic.getList('list'), [obj1, obj1]); expect(obj2.dynamic.getList('list')[0].dynamic.get('id'), uuid1); + expect(obj2.dynamic.getSet('linksSet'), [obj1]); + expect(obj2.dynamic.getSet('linksSet'), [obj1]); + expect(obj2.dynamic.getSet('linksSet').first.dynamic.get('id'), uuid1); + + expect(obj2.dynamic.getMap('map'), {'a': obj1, 'b': obj1}); + expect(obj2.dynamic.getMap('map'), {'a': obj1, 'b': obj1}); + expect(obj2.dynamic.getMap('map')['a']!.dynamic.get('id'), uuid1); + expect(obj2.dynamic.getMap('map')['non-existent'], null); + dynamic dynamicObj1 = obj1; dynamic dynamicObj2 = obj2; expect(dynamicObj1.list, isEmpty); + expect(dynamicObj1.linksSet, isEmpty); + expect(dynamicObj1.map, isEmpty); expect(dynamicObj2.list, [obj1, obj1]); expect(dynamicObj2.list[0].id, uuid1); + expect(dynamicObj2.linksSet, [obj1]); + expect(dynamicObj2.linksSet.first.id, uuid1); + expect(dynamicObj2.map, {'a': obj1, 'b': obj1}); + expect(dynamicObj2.map['a']!.id, uuid1); + expect(dynamicObj2.map['non-existent'], null); }); - test('fails with non-existent property', () { - final config = Configuration.local([AllCollections.schema]); - final staticRealm = getRealm(config); - staticRealm.write(() { - staticRealm.add(AllCollections()); + for (final collectionType in [RealmCollectionType.list, RealmCollectionType.set, RealmCollectionType.map]) { + dynamic getter(RealmObjectBase object, String property) { + return switch (collectionType) { + RealmCollectionType.list => object.dynamic.getList(property), + RealmCollectionType.set => object.dynamic.getSet(property), + RealmCollectionType.map => object.dynamic.getMap(property), + _ => throw RealmError('Unexpected collectionType: $collectionType'), + }; + } + + final propertySuffix = switch (collectionType) { + RealmCollectionType.list => 'List', + RealmCollectionType.set => 'Set', + RealmCollectionType.map => 'Map', + _ => throw RealmError('Unexpected collectionType: $collectionType'), + }; + + test('get$propertySuffix fails with non-existent property', () { + final config = Configuration.local([AllCollections.schema]); + final staticRealm = getRealm(config); + staticRealm.write(() { + staticRealm.add(AllCollections()); + }); + final dynamicRealm = _getDynamicRealm(staticRealm); + + final obj = dynamicRealm.dynamic.all(AllCollections.schema.name).single; + expect(() => getter(obj, 'i-dont-exist'), throws("Property 'i-dont-exist' does not exist on class 'AllCollections'")); }); - final dynamicRealm = _getDynamicRealm(staticRealm); - final obj = dynamicRealm.dynamic.all(AllCollections.schema.name).single; - expect(() => obj.dynamic.getList('i-dont-exist'), throws("Property 'i-dont-exist' does not exist on class 'AllCollections'")); - }); + test('get$propertySuffix fails with wrong type', () { + final config = Configuration.local([AllCollections.schema]); + final staticRealm = getRealm(config); + staticRealm.write(() { + staticRealm.add(AllCollections()); + }); + final dynamicRealm = _getDynamicRealm(staticRealm); - test('fails with wrong type', () { - final config = Configuration.local([AllCollections.schema]); - final staticRealm = getRealm(config); - staticRealm.write(() { - staticRealm.add(AllCollections()); - }); - final dynamicRealm = _getDynamicRealm(staticRealm); - - final obj = dynamicRealm.dynamic.all(AllCollections.schema.name).single; + final obj = dynamicRealm.dynamic.all(AllCollections.schema.name).single; - expect( - () => obj.dynamic.getList('strings'), - throws( - "Property 'strings' on class 'AllCollections' is not the correct type. Expected 'RealmPropertyType.int', got 'RealmPropertyType.string'")); - }); - - test('fails on non-collection properties', () { - final config = Configuration.local([AllTypes.schema]); - final staticRealm = getRealm(config); - staticRealm.write(() { - staticRealm.add(_getEmptyAllTypes()); + expect( + () => getter(obj, 'string$propertySuffix'), + throws( + "Property 'string$propertySuffix' on class 'AllCollections' is not the correct type. Expected 'RealmPropertyType.int', got 'RealmPropertyType.string'")); }); - final dynamicRealm = _getDynamicRealm(staticRealm); - final obj = dynamicRealm.dynamic.all(AllTypes.schema.name).single; - expect( - () => obj.dynamic.getList('intProp'), - throws( - "Property 'intProp' on class 'AllTypes' is 'RealmCollectionType.none' but the method used to access it expected 'RealmCollectionType.list'.")); - }); + test('get$propertySuffix fails on non-collection properties', () { + final config = Configuration.local([AllTypes.schema]); + final staticRealm = getRealm(config); + staticRealm.write(() { + staticRealm.add(_getEmptyAllTypes()); + }); + final dynamicRealm = _getDynamicRealm(staticRealm); + + final obj = dynamicRealm.dynamic.all(AllTypes.schema.name).single; + expect( + () => getter(obj, 'intProp'), + throws( + "Property 'intProp' on class 'AllTypes' is 'RealmCollectionType.none' but the method used to access it expected '$collectionType'.")); + }); + } }); } @@ -575,7 +821,7 @@ void main() { }); for (final obj in realm.all()) { - _validateDynamicLists(obj, obj); + _validateDynamicCollections(obj, obj); } }); diff --git a/packages/realm_dart/test/list_test.dart b/packages/realm_dart/test/list_test.dart index 4f082b086..f074dc3b3 100644 --- a/packages/realm_dart/test/list_test.dart +++ b/packages/realm_dart/test/list_test.dart @@ -375,20 +375,20 @@ void main() { testListNotificationsHelper('insert', getList, (ch) => ch.inserted, op, inserts); } - testListInsertNotifications((c) => c.nullableBools, (c, i) => c.insert(i, null)); - testListInsertNotifications((c) => c.bools, (c, i) => c.insert(i, i % 2 == 0)); - testListInsertNotifications((c) => c.nullableDates, (c, i) => c.insert(i, null)); - testListInsertNotifications((c) => c.dates, (c, i) => c.insert(i, DateTime(i))); - testListInsertNotifications((c) => c.nullableDoubles, (c, i) => c.insert(i, null)); - testListInsertNotifications((c) => c.doubles, (c, i) => c.insert(i, i.toDouble())); - testListInsertNotifications((c) => c.nullableInts, (c, i) => c.insert(i, null)); - testListInsertNotifications((c) => c.ints, (c, i) => c.insert(i, i)); - testListInsertNotifications((c) => c.nullableObjectIds, (c, i) => c.insert(i, null)); - testListInsertNotifications((c) => c.objectIds, (c, i) => c.insert(i, ObjectId())); - testListInsertNotifications((c) => c.nullableStrings, (c, i) => c.insert(i, null)); - testListInsertNotifications((c) => c.strings, (c, i) => c.insert(i, '$i')); - testListInsertNotifications((c) => c.nullableUuids, (c, i) => c.insert(i, null)); - testListInsertNotifications((c) => c.uuids, (c, i) => c.insert(i, Uuid.v4())); + testListInsertNotifications((c) => c.nullableBoolList, (c, i) => c.insert(i, null)); + testListInsertNotifications((c) => c.boolList, (c, i) => c.insert(i, i % 2 == 0)); + testListInsertNotifications((c) => c.nullableDateList, (c, i) => c.insert(i, null)); + testListInsertNotifications((c) => c.dateList, (c, i) => c.insert(i, DateTime(i))); + testListInsertNotifications((c) => c.nullableDoubleList, (c, i) => c.insert(i, null)); + testListInsertNotifications((c) => c.doubleList, (c, i) => c.insert(i, i.toDouble())); + testListInsertNotifications((c) => c.nullableIntList, (c, i) => c.insert(i, null)); + testListInsertNotifications((c) => c.intList, (c, i) => c.insert(i, i)); + testListInsertNotifications((c) => c.nullableObjectIdList, (c, i) => c.insert(i, null)); + testListInsertNotifications((c) => c.objectIdList, (c, i) => c.insert(i, ObjectId())); + testListInsertNotifications((c) => c.nullableStringList, (c, i) => c.insert(i, null)); + testListInsertNotifications((c) => c.stringList, (c, i) => c.insert(i, '$i')); + testListInsertNotifications((c) => c.nullableUuidList, (c, i) => c.insert(i, null)); + testListInsertNotifications((c) => c.uuidList, (c, i) => c.insert(i, Uuid.v4())); final deletes = [ [0], @@ -414,20 +414,20 @@ void main() { )); } - testListDeleteNotifications((c) => c.nullableBools, (i) => null); - testListDeleteNotifications((c) => c.bools, (i) => i % 2 == 0); - testListDeleteNotifications((c) => c.nullableDates, (i) => null); - testListDeleteNotifications((c) => c.dates, (i) => DateTime(i)); - testListDeleteNotifications((c) => c.nullableDoubles, (i) => null); - testListDeleteNotifications((c) => c.doubles, (i) => i.toDouble()); - testListDeleteNotifications((c) => c.nullableInts, (i) => null); - testListDeleteNotifications((c) => c.ints, (i) => i); - testListDeleteNotifications((c) => c.nullableObjectIds, (i) => null); - testListDeleteNotifications((c) => c.objectIds, (i) => ObjectId()); - testListDeleteNotifications((c) => c.nullableStrings, (i) => null); - testListDeleteNotifications((c) => c.strings, (i) => '$i'); - testListDeleteNotifications((c) => c.nullableUuids, (i) => null); - testListDeleteNotifications((c) => c.uuids, (i) => Uuid.v4()); + testListDeleteNotifications((c) => c.nullableBoolList, (i) => null); + testListDeleteNotifications((c) => c.boolList, (i) => i % 2 == 0); + testListDeleteNotifications((c) => c.nullableDateList, (i) => null); + testListDeleteNotifications((c) => c.dateList, (i) => DateTime(i)); + testListDeleteNotifications((c) => c.nullableDoubleList, (i) => null); + testListDeleteNotifications((c) => c.doubleList, (i) => i.toDouble()); + testListDeleteNotifications((c) => c.nullableIntList, (i) => null); + testListDeleteNotifications((c) => c.intList, (i) => i); + testListDeleteNotifications((c) => c.nullableObjectIdList, (i) => null); + testListDeleteNotifications((c) => c.objectIdList, (i) => ObjectId()); + testListDeleteNotifications((c) => c.nullableStringList, (i) => null); + testListDeleteNotifications((c) => c.stringList, (i) => '$i'); + testListDeleteNotifications((c) => c.nullableUuidList, (i) => null); + testListDeleteNotifications((c) => c.uuidList, (i) => Uuid.v4()); final modifications = [ [0], @@ -453,20 +453,20 @@ void main() { )); } - testListModificationNotifications((c) => c.nullableBools, (i) => null); - testListModificationNotifications((c) => c.bools, (i) => i % 2 == 0); - testListModificationNotifications((c) => c.nullableDates, (i) => null); - testListModificationNotifications((c) => c.dates, (i) => DateTime(i)); - testListModificationNotifications((c) => c.nullableDoubles, (i) => null); - testListModificationNotifications((c) => c.doubles, (i) => i.toDouble()); - testListModificationNotifications((c) => c.nullableInts, (i) => null); - testListModificationNotifications((c) => c.ints, (i) => i); - testListModificationNotifications((c) => c.nullableObjectIds, (i) => null); - testListModificationNotifications((c) => c.objectIds, (i) => ObjectId()); - testListModificationNotifications((c) => c.nullableStrings, (i) => null); - testListDeleteNotifications((c) => c.strings, (i) => '$i'); - testListDeleteNotifications((c) => c.nullableUuids, (i) => null); - testListDeleteNotifications((c) => c.uuids, (i) => Uuid.v4()); + testListModificationNotifications((c) => c.nullableBoolList, (i) => null); + testListModificationNotifications((c) => c.boolList, (i) => i % 2 == 0); + testListModificationNotifications((c) => c.nullableDateList, (i) => null); + testListModificationNotifications((c) => c.dateList, (i) => DateTime(i)); + testListModificationNotifications((c) => c.nullableDoubleList, (i) => null); + testListModificationNotifications((c) => c.doubleList, (i) => i.toDouble()); + testListModificationNotifications((c) => c.nullableIntList, (i) => null); + testListModificationNotifications((c) => c.intList, (i) => i); + testListModificationNotifications((c) => c.nullableObjectIdList, (i) => null); + testListModificationNotifications((c) => c.objectIdList, (i) => ObjectId()); + testListModificationNotifications((c) => c.nullableStringList, (i) => null); + testListDeleteNotifications((c) => c.stringList, (i) => '$i'); + testListDeleteNotifications((c) => c.nullableUuidList, (i) => null); + testListDeleteNotifications((c) => c.uuidList, (i) => Uuid.v4()); test('List query', () { final config = Configuration.local([Team.schema, Person.schema]); @@ -1189,10 +1189,20 @@ void main() { expectLater( team.players.changes, emitsInOrder([ - isA>().having((changes) => changes.inserted, 'inserted', []), // always an empty event on subscription - isA>().having((changes) => changes.isCleared, 'isCleared', true), + isA>() + .having((changes) => changes.inserted, 'inserted', []) + .having((changes) => changes.isCleared, 'isCleared', false) + .having((changes) => changes.isCollectionDeleted, 'isCollectionDeleted', false), // always an empty event on subscription + isA>() + .having((changes) => changes.isCleared, 'isCleared', true) + .having((changes) => changes.isCollectionDeleted, 'isCollectionDeleted', false), + isA>() + .having((changes) => changes.isCleared, 'isCleared', false) + .having((changes) => changes.isCollectionDeleted, 'isCollectionDeleted', true), ])); realm.write(() => team.players.clear()); + realm.refresh(); + realm.write(() => realm.delete(team)); }); test('RealmList.changes - await for with yield', () async { @@ -1238,6 +1248,7 @@ void main() { ])); realm.write(() => team.players.clear()); expect(playersAsResults.length, 0); + realm.refresh(); }); test('Query on RealmList with IN-operator', () { diff --git a/packages/realm_dart/test/realm_map_test.dart b/packages/realm_dart/test/realm_map_test.dart index ed9dd3c22..32aeac8e5 100644 --- a/packages/realm_dart/test/realm_map_test.dart +++ b/packages/realm_dart/test/realm_map_test.dart @@ -535,7 +535,7 @@ List> realmValueTestValues() => [ ('intKey', RealmValue.int(10)), ('boolKey', RealmValue.bool(true)), ('stringKey', RealmValue.string('abc')), - ('dataKey', RealmValue.uint8List(Uint8List.fromList([0, 1, 2]))), + ('dataKey', RealmValue.binary(Uint8List.fromList([0, 1, 2]))), ('dateKey', RealmValue.dateTime(DateTime.fromMillisecondsSinceEpoch(1616137641000).toUtc())), ('doubleKey', RealmValue.double(2.5)), ('decimalKey', RealmValue.decimal128(Decimal128.fromDouble(5.0))), @@ -631,12 +631,12 @@ void testNotifications(RealmMap Function(TestRealmMaps) accessor, TestCase realm.add(testObject); }); - // final managedMap = accessor(testObject); - // await runManagedNotificationTests(testData, managedMap); + final managedMap = accessor(testObject); + await runManagedNotificationTests(testData, managedMap, testObject); }); test('$T key notifications', () async { - // TODO: for some reason, we don't appear to be getting key notifications + // TODO: for some reason, we don't appear to be getting key notifications: https://github.com/realm/realm-core/issues/7219 final config = Configuration.local([TestRealmMaps.schema, Car.schema, EmbeddedValue.schema]); final realm = getRealm(config); @@ -668,7 +668,7 @@ void testNotifications(RealmMap Function(TestRealmMaps) accessor, TestCase realm.write(() { map.remove('a'); }); - }, skip: 'Key notifications are not working'); + }, skip: 'Key notifications are not working: https://github.com/realm/realm-core/issues/7219'); test('$T value notifications', () async { final config = Configuration.local([TestRealmMaps.schema, Car.schema, EmbeddedValue.schema]); @@ -719,7 +719,7 @@ Future runTestsCore(TestCaseData testData, RealmMap map, {require testData.assertRemove(map); } -Future runManagedNotificationTests(TestCaseData testData, RealmMap map) async { +Future runManagedNotificationTests(TestCaseData testData, RealmMap map, TestRealmMaps parent) async { final insertedKey = Uuid.v4().toString(); final (keyToUpdate, _) = testData._getDifferentValue(map, testData.sampleValue); @@ -730,16 +730,20 @@ Future runManagedNotificationTests(TestCaseData testData, RealmMap> waitForChanges(({List inserted, List modified, List deleted}) expected) async { + Future> waitForChanges(({List inserted, List modified, List deleted})? expected) async { expectedCallbacks++; map.realm.refresh(); await waitForCondition(() => changes.length == expectedCallbacks); final result = changes[expectedCallbacks - 1]; - expect(result.inserted, expected.inserted); - expect(result.modified, expected.modified); - expect(result.deleted, expected.deleted); + if (expected != null) { + expect(result.inserted, expected.inserted); + expect(result.modified, expected.modified); + expect(result.deleted, expected.deleted); + expect(result.isCleared, false); + expect(result.isCollectionDeleted, false); + } return result; } @@ -781,6 +785,35 @@ Future runManagedNotificationTests(TestCaseData testData, RealmMap[ - isA>().having((changes) => changes.inserted, 'inserted', []), // always an empty event on subscription - isA>().having((changes) => changes.isCleared, 'isCleared', true), + isA>() + .having((changes) => changes.inserted, 'inserted', []) + .having((changes) => changes.isCleared, 'isCleared', false) + .having((changes) => changes.isCollectionDeleted, 'isCollectionDeleted', false), // always an empty event on subscription + isA>() + .having((changes) => changes.isCleared, 'isCleared', true) + .having((changes) => changes.isCollectionDeleted, 'isCollectionDeleted', false), + isA>() + .having((changes) => changes.isCollectionDeleted, 'isCollectionDeleted', true) + .having((changes) => changes.isCleared, 'isCleared', false), ])); realm.write(() => set.clear()); + realm.refresh(); + realm.write(() => realm.delete(testSet)); }); test('RealmSet<$type> basic operations on unmanaged sets', () { diff --git a/packages/realm_dart/test/realm_value_test.dart b/packages/realm_dart/test/realm_value_test.dart index 1e7180ec9..c7a24f9e7 100644 --- a/packages/realm_dart/test/realm_value_test.dart +++ b/packages/realm_dart/test/realm_value_test.dart @@ -35,6 +35,8 @@ class _AnythingGoes { @Indexed() late RealmValue oneAny; late List manyAny; + late Map dictOfAny; + late Set setOfAny; } @RealmModel() @@ -45,126 +47,162 @@ class _Stuff { void main() { setupTests(); + Realm getMixedRealm() { + final config = Configuration.local([AnythingGoes.schema, Stuff.schema, TuckedIn.schema]); + return getRealm(config); + } + group('RealmValue', () { - final now = DateTime.now().toUtc(); - final values = [ + final primitiveValues = [ null, true, 'text', 42, 3.14, - AnythingGoes(), - Stuff(), - now, - ObjectId.fromTimestamp(now), - Uuid.v4(), + DateTime.utc(2024, 5, 3, 23, 11, 54), + ObjectId.fromHexString('64c13ab08edf48a008793cac'), + Uuid.fromString('7a459a5e-5eb6-45f6-9b72-8f794e324105'), Decimal128.fromDouble(128.128), Uint8List.fromList([1, 2, 0]) ]; - for (final x in values) { + for (final x in primitiveValues) { test('Roundtrip ${x.runtimeType} $x', () { - final config = Configuration.local([AnythingGoes.schema, Stuff.schema, TuckedIn.schema]); - final realm = getRealm(config); + final realm = getMixedRealm(); final something = realm.write(() => realm.add(AnythingGoes(oneAny: RealmValue.from(x)))); - expect(something.oneAny.type, x.runtimeType); + expect(something.oneAny.value.runtimeType, x.runtimeType); expect(something.oneAny.value, x); expect(something.oneAny, RealmValue.from(x)); }); + + final queryArg = RealmValue.from(x); + test('Query @type == ${queryArg.type} $x', () { + final realm = getMixedRealm(); + realm.write(() { + // Add all values, we're going to query for just one of them. + for (final v in primitiveValues) { + realm.add(AnythingGoes(oneAny: RealmValue.from(v))); + } + realm.add(AnythingGoes(oneAny: RealmValue.from(Stuff()))); + }); + + final matches = realm.query(r'oneAny.@type == $0', [queryArg.type]); + expect(matches.length, 1); + expect(matches.single.oneAny.value, x); + expect(matches.single.oneAny.type, queryArg.type); + expect(matches.single.oneAny, queryArg); + }); } + test('Roundtrip object', () { + final stuff = Stuff(i: 123); + final realm = getMixedRealm(); + final something = realm.write(() => realm.add(AnythingGoes(oneAny: RealmValue.from(stuff)))); + expect(something.oneAny.value.runtimeType, Stuff); + expect(something.oneAny.as().i, 123); + }); + + test('Query @type == object', () { + final realm = getMixedRealm(); + realm.write(() { + for (final v in primitiveValues) { + realm.add(AnythingGoes(oneAny: RealmValue.from(v))); + } + + realm.add(AnythingGoes(oneAny: RealmValue.from(Stuff(i: 123)))); + }); + + final matches = realm.query(r'oneAny.@type == $0', [RealmValueType.object]); + expect(matches.length, 1); + expect(matches.single.oneAny.as().i, 123); + expect(matches.single.oneAny.type, RealmValueType.object); + }); + test('Illegal value', () { - final config = Configuration.local([AnythingGoes.schema, Stuff.schema, TuckedIn.schema]); - final realm = getRealm(config); - expect(() => realm.write(() => realm.add(AnythingGoes(oneAny: RealmValue.from([1, 2])))), throwsArgumentError); + final realm = getMixedRealm(); + expect(() => realm.write(() => realm.add(AnythingGoes(oneAny: RealmValue.from(realm)))), throwsArgumentError); }); test('Embedded object not allowed in RealmValue', () { - final config = Configuration.local([AnythingGoes.schema, Stuff.schema, TuckedIn.schema]); - final realm = getRealm(config); + final realm = getMixedRealm(); expect(() => realm.write(() => realm.add(AnythingGoes(oneAny: RealmValue.from(TuckedIn())))), throwsArgumentError); }); - for (final x in values) { + for (final x in primitiveValues) { test('Switch $x', () { final something = AnythingGoes(oneAny: RealmValue.from(x)); final value = something.oneAny.value; - // Uint8List can not be in the switch - if (something.oneAny.type == Uint8List(0).runtimeType) { - expect(value, isA()); - return; - } - switch (something.oneAny.type) { - case Null: + case RealmValueType.nullValue: expect(value, isA()); break; - case bool: + case RealmValueType.boolean: expect(value, isA()); break; - case String: + case RealmValueType.string: expect(value, isA()); break; - case int: + case RealmValueType.int: expect(value, isA()); break; - case double: + case RealmValueType.double: expect(value, isA()); break; - case AnythingGoes: // RealmObject won't work with switch - expect(value, isA()); - break; - case Stuff: // RealmObject won't work with switch - expect(value, isA()); + case RealmValueType.object: + expect(value is AnythingGoes || value is Stuff, true); break; - case DateTime: + case RealmValueType.dateTime: expect(value, isA()); break; - case ObjectId: + case RealmValueType.objectId: expect(value, isA()); break; - case Uuid: + case RealmValueType.uuid: expect(value, isA()); break; - case Decimal128: + case RealmValueType.decimal: expect(value, isA()); break; - default: - fail('${something.oneAny} not handled correctly in switch'); + case RealmValueType.binary: + expect(value, isA()); + break; + case RealmValueType.list: + case RealmValueType.map: + fail('List and map should not be tested here.'); } }); } - for (final x in values) { + for (final x in primitiveValues) { test('If-is $x', () { final something = AnythingGoes(oneAny: RealmValue.from(x)); final value = something.oneAny.value; final type = something.oneAny.type; if (value == null) { - expect(type, Null); + expect(type, RealmValueType.nullValue); } else if (value is int) { - expect(type, int); + expect(type, RealmValueType.int); } else if (value is String) { - expect(type, String); + expect(type, RealmValueType.string); } else if (value is bool) { - expect(type, bool); + expect(type, RealmValueType.boolean); } else if (value is double) { - expect(type, double); + expect(type, RealmValueType.double); } else if (value is DateTime) { - expect(type, DateTime); + expect(type, RealmValueType.dateTime); } else if (value is Uuid) { - expect(type, Uuid); + expect(type, RealmValueType.uuid); } else if (value is ObjectId) { - expect(type, ObjectId); + expect(type, RealmValueType.objectId); } else if (value is Decimal128) { - expect(type, Decimal128); + expect(type, RealmValueType.decimal); } else if (value is Uint8List) { - expect(type, Uint8List(0).runtimeType); + expect(type, RealmValueType.binary); } else if (value is AnythingGoes) { - expect(type, AnythingGoes); + expect(type, RealmValueType.object); } else if (value is Stuff) { - expect(type, Stuff); + expect(type, RealmValueType.object); } else { fail('$value not handled correctly in if-is'); } @@ -220,8 +258,7 @@ void main() { ]; test('Roundtrip', () { - final config = Configuration.local([AnythingGoes.schema, Stuff.schema, TuckedIn.schema]); - final realm = getRealm(config); + final realm = getMixedRealm(); final something = realm.write(() => realm.add(AnythingGoes(manyAny: values.map(RealmValue.from)))); expect(something.manyAny.map((e) => e.value), values); expect(something.manyAny, values.map(RealmValue.from)); @@ -243,8 +280,7 @@ void main() { Uuid.v4(), Decimal128.fromInt(128), ]; - final config = Configuration.local([AnythingGoes.schema, Stuff.schema]); - final realm = getRealm(config); + final realm = getMixedRealm(); final realmValues = values.map(RealmValue.from); realm.write(() => realm.add(AnythingGoes(manyAny: realmValues, oneAny: realmValues.last))); @@ -254,4 +290,749 @@ void main() { results = realm.query("oneAny IN \$0", [values]); expect(results.first.oneAny, realmValues.last); }); + + test('Set with numeric values', () { + final realm = getMixedRealm(); + final values = [RealmValue.int(0), RealmValue.double(0.0), RealmValue.bool(false), RealmValue.decimal128(Decimal128.zero), RealmValue.nullValue()]; + final obj = realm.write(() => realm.add(AnythingGoes())..setOfAny.addAll(values)); + + expect(obj.setOfAny, unorderedMatches([RealmValue.int(0), RealmValue.bool(false), RealmValue.nullValue()])); + }); + + test('Set removes duplicates', () { + final realm = getMixedRealm(); + final values = [ + RealmValue.int(1), + RealmValue.nullValue(), + RealmValue.double(2.0), + RealmValue.string('abc'), + RealmValue.nullValue(), + RealmValue.string('abc') + ]; + final obj = realm.write(() => realm.add(AnythingGoes())..setOfAny.addAll(values)); + + expect(obj.setOfAny, unorderedMatches([RealmValue.int(1), RealmValue.double(2.0), RealmValue.nullValue(), RealmValue.string('abc')])); + }); + + group('Collections in RealmValue', () { + void expectMatches(RealmValue actual, Object? expected) { + switch (actual.collectionType) { + case RealmCollectionType.list: + expect(expected, isList); + final actualList = actual.asList(); + final expectedList = expected as List; + expect(actualList, hasLength(expectedList.length)); + for (var i = 0; i < expectedList.length; i++) { + expectMatches(actualList[i], expectedList[i]); + } + break; + case RealmCollectionType.map: + expect(expected, isMap); + final actualMap = actual.asMap(); + final expectedMap = expected as Map; + expect(actualMap, hasLength(expectedMap.length)); + for (String key in expectedMap.keys) { + expect(actualMap.containsKey(key), true, reason: "Didn't find $key in the actual map"); + expectMatches(actualMap[key]!, expectedMap[key]); + } + break; + default: + expect(actual, RealmValue.from(expected)); + break; + } + } + + test('Set throws', () { + final realm = getMixedRealm(); + final list = RealmValue.list([RealmValue.from(5)]); + final map = RealmValue.map({'a': RealmValue.from('abc')}); + + final obj = AnythingGoes(); + expect(() => obj.setOfAny.add(list), throws()); + expect(() => obj.setOfAny.add(map), throws()); + + realm.write(() => realm.add(obj)); + + realm.write(() { + expect(() => obj.setOfAny.add(list), throws()); + expect(() => obj.setOfAny.add(map), throws()); + }); + }); + + test('List get and set', () { + final realm = getMixedRealm(); + final list = RealmValue.from([5]); + + final obj = AnythingGoes(oneAny: list, manyAny: [list], dictOfAny: {'value': list}); + expect(obj.oneAny.value, isA>()); + expect(obj.oneAny.asList().length, 1); + expect(obj.oneAny.asList().single.value, 5); + + expect(obj.manyAny[0].value, isA>()); + expect(obj.manyAny[0].asList().length, 1); + expect(obj.manyAny[0].asList().single.value, 5); + + expect(obj.dictOfAny['value']!.value, isA>()); + expect(obj.dictOfAny['value']!.asList().length, 1); + expect(obj.dictOfAny['value']!.asList().single.value, 5); + + realm.write(() { + realm.add(obj); + }); + + final foundObj = realm.all().single; + expect(foundObj.oneAny.value, isA>()); + expect(foundObj.oneAny.asList().length, 1); + expect(foundObj.oneAny.asList()[0].value, 5); + + expect(foundObj.manyAny[0].value, isA>()); + expect(foundObj.manyAny[0].asList().length, 1); + expect(foundObj.manyAny[0].asList()[0].value, 5); + + expect(foundObj.dictOfAny['value']!.value, isA>()); + expect(foundObj.dictOfAny['value']!.asList().length, 1); + expect(foundObj.dictOfAny['value']!.asList()[0].value, 5); + + realm.write(() { + foundObj.oneAny.asList().add(RealmValue.from('abc')); + foundObj.manyAny[0].asList().add(RealmValue.from('abc')); + foundObj.dictOfAny['value']!.asList().add(RealmValue.from('abc')); + }); + + expect(obj.oneAny.asList()[1].value, 'abc'); + expect(obj.manyAny[0].asList()[1].value, 'abc'); + expect(obj.dictOfAny['value']!.asList()[1].value, 'abc'); + }); + + test('Map get and set', () { + final realm = getMixedRealm(); + final map = RealmValue.from({'foo': 5}); + + final obj = AnythingGoes(oneAny: map, manyAny: [map], dictOfAny: {'value': map}); + expect(obj.oneAny.value, isA>()); + expect(obj.oneAny.asMap().length, 1); + expect(obj.oneAny.asMap()['foo']!.value, 5); + + expect(obj.manyAny[0].value, isA>()); + expect(obj.manyAny[0].asMap().length, 1); + expect(obj.manyAny[0].asMap()['foo']!.value, 5); + + expect(obj.dictOfAny['value']!.value, isA>()); + expect(obj.dictOfAny['value']!.asMap().length, 1); + expect(obj.dictOfAny['value']!.asMap()['foo']!.value, 5); + + realm.write(() { + realm.add(obj); + }); + + final foundObj = realm.all().single; + expect(foundObj.oneAny.value, isA>()); + expect(foundObj.oneAny.asMap().length, 1); + expect(foundObj.oneAny.asMap()['foo']!.value, 5); + + expect(foundObj.manyAny[0].value, isA>()); + expect(foundObj.manyAny[0].asMap().length, 1); + expect(foundObj.manyAny[0].asMap()['foo']!.value, 5); + + expect(foundObj.dictOfAny['value']!.value, isA>()); + expect(foundObj.dictOfAny['value']!.asMap().length, 1); + expect(foundObj.dictOfAny['value']!.asMap()['foo']!.value, 5); + + realm.write(() { + foundObj.oneAny.asMap()['bar'] = RealmValue.from('abc'); + foundObj.manyAny[0].asMap()['bar'] = RealmValue.from('abc'); + foundObj.dictOfAny['value']!.asMap()['bar'] = RealmValue.from('abc'); + }); + + expect(obj.oneAny.asMap()['bar']!.value, 'abc'); + expect(obj.manyAny[0].asMap()['bar']!.value, 'abc'); + expect(obj.dictOfAny['value']!.asMap()['bar']!.value, 'abc'); + }); + + for (var isManaged in [true, false]) { + final managedString = isManaged ? 'managed' : 'unmanaged'; + RealmValue persistIfNecessary(RealmValue rv, Realm realm) { + if (isManaged) { + realm.write(() { + realm.add(AnythingGoes(oneAny: rv)); + }); + + return realm.all().first.oneAny; + } + + return rv; + } + + void writeIfNecessary(Realm realm, void Function() func) { + if (isManaged) { + realm.write(() => func()); + } else { + func(); + } + } + + test('List when $managedString works with all types', () { + final realm = getMixedRealm(); + final originalList = [ + null, + 1, + true, + 'string', + DateTime(1999, 3, 4, 5, 30, 23).toUtc(), + 2.3, + Decimal128.parse('1.23456789'), + ObjectId.fromHexString('5f63e882536de46d71877979'), + Uuid.fromString('3809d6d9-7618-4b3d-8044-2aa35fd02f31'), + Uint8List.fromList([1, 2, 0]), + Stuff(i: 123), + [5, 'abc'], + {'int': -10, 'string': 'abc'} + ]; + final foundValue = persistIfNecessary(RealmValue.from(originalList), realm); + expect(foundValue.value, isA>()); + expect(foundValue.type, RealmValueType.list); + + final foundList = foundValue.asList(); + expect(foundList.length, originalList.length); + + // Last 3 elements are objects/collections, so they are treated specially + final primitiveCount = originalList.length - 3; + for (var i = 0; i < primitiveCount; i++) { + expect(foundList[i].value, originalList[i]); + } + + final storedObj = foundList[primitiveCount]; + expect(storedObj.value, isA()); + expect(storedObj.as().isManaged, isManaged); + expect(storedObj.as().i, 123); + + final storedList = foundList[primitiveCount + 1]; + expectMatches(storedList, [5, 'abc']); + + final storedDict = foundList[primitiveCount + 2]; + expectMatches(storedDict, {'int': -10, 'string': 'abc'}); + expect(storedDict.asMap()['non-existent'], null); + }); + + test('List when $managedString can be reassigned', () { + final realm = getMixedRealm(); + final obj = AnythingGoes(oneAny: RealmValue.from([true, 5.3])); + if (isManaged) { + realm.write(() => realm.add(obj)); + } + + expect(obj.oneAny.type, RealmValueType.list); + expectMatches(obj.oneAny, [true, 5.3]); + + writeIfNecessary(realm, () => obj.oneAny = RealmValue.from(999)); + expectMatches(obj.oneAny, 999); + + writeIfNecessary(realm, () => obj.oneAny = RealmValue.from({'int': -100})); + expectMatches(obj.oneAny, {'int': -100}); + }); + + test('Map when $managedString works with all types', () { + final realm = getMixedRealm(); + final originalMap = { + 'primitive_null': null, + 'primitive_int': 1, + 'primitive_bool': true, + 'primitive_string': 'string', + 'primitive_date': DateTime(1999, 3, 4, 5, 30, 23).toUtc(), + 'primitive_double': 2.3, + 'primitive_decimal': Decimal128.parse('1.23456789'), + 'primitive_objectId': ObjectId.fromHexString('5f63e882536de46d71877979'), + 'primitive_uuid': Uuid.fromString('3809d6d9-7618-4b3d-8044-2aa35fd02f31'), + 'primitive_binary': Uint8List.fromList([1, 2, 0]), + 'object': Stuff(i: 123), + 'list': [5, 'abc'], + 'map': {'int': -10, 'string': 'abc'} + }; + final foundValue = persistIfNecessary(RealmValue.from(originalMap), realm); + expect(foundValue.value, isA>()); + expect(foundValue.type, RealmValueType.map); + + final foundMap = foundValue.asMap(); + expect(foundMap.length, foundMap.length); + + for (var key in originalMap.keys.where((k) => k.startsWith('primitive_'))) { + expect(foundMap[key]!.value, originalMap[key]); + } + + final storedObj = foundMap['object']!; + expect(storedObj.value, isA()); + expect(storedObj.as().isManaged, isManaged); + expect(storedObj.as().i, 123); + + final storedList = foundMap['list']!; + expectMatches(storedList, [5, 'abc']); + + final storedDict = foundMap['map']!; + expectMatches(storedDict, {'int': -10, 'string': 'abc'}); + }); + + test('Map when $managedString can be reassigned', () { + final realm = getMixedRealm(); + final obj = AnythingGoes(oneAny: RealmValue.from({'bool': true, 'double': 5.3})); + if (isManaged) { + realm.write(() => realm.add(obj)); + } + + expect(obj.oneAny.type, RealmValueType.map); + expectMatches(obj.oneAny, {'bool': true, 'double': 5.3}); + + writeIfNecessary(realm, () => obj.oneAny = RealmValue.from(999)); + expectMatches(obj.oneAny, 999); + + writeIfNecessary(realm, () => obj.oneAny = RealmValue.from([1.23456789])); + expectMatches(obj.oneAny, [1.23456789]); + }); + + test('RealmValue when $managedString can store complex struct', () { + final realm = getMixedRealm(); + final rv = persistIfNecessary( + RealmValue.from([ + {'0_bool': true, '0_double': 5.3}, + { + '1_int': 5, + '1_map': { + '2_decimal': Decimal128.fromDouble(0.1), + '2_list': [ + 'bla bla', + { + '3_dict': {'4_string': 'abc'} + } + ] + } + } + ]), + realm); + + expectMatches(rv, [ + {'0_bool': true, '0_double': 5.3}, + { + '1_int': 5, + '1_map': { + '2_decimal': Decimal128.fromDouble(0.1), + '2_list': [ + 'bla bla', + { + '3_dict': {'4_string': 'abc'} + } + ] + } + } + ]); + + writeIfNecessary(realm, () { + rv.asList().removeAt(0); + }); + + expectMatches(rv, [ + { + '1_int': 5, + '1_map': { + '2_decimal': Decimal128.fromDouble(0.1), + '2_list': [ + 'bla bla', + { + '3_dict': {'4_string': 'abc'} + } + ] + } + } + ]); + + writeIfNecessary(realm, () { + rv.asList()[0].asMap()['1_double'] = RealmValue.double(5.5); + rv.asList()[0].asMap().remove('1_map'); + rv.asList().add(RealmValue.bool(true)); + }); + + expectMatches(rv, [ + {'1_int': 5, '1_double': 5.5}, + true + ]); + }); + } + + test('List inside RealmValue equality', () { + final realm = getMixedRealm(); + final originalList = [1]; + final managedValue = realm.write(() { + return realm.add(AnythingGoes(oneAny: RealmValue.from(originalList))).oneAny; + }); + + final unmanagedValue = RealmValue.from(originalList); + + expect(managedValue.type, RealmValueType.list); + expect(unmanagedValue.type, RealmValueType.list); + + expect(managedValue.asList().isManaged, true); + expect(unmanagedValue.asList().isManaged, false); + + expect(managedValue == unmanagedValue, false); + expect(unmanagedValue == managedValue, false); + expect(managedValue == managedValue, false); + expect(unmanagedValue == unmanagedValue, false); + + // ignore: unrelated_type_equality_checks + expect(managedValue == originalList, false); + + // ignore: unrelated_type_equality_checks + expect(unmanagedValue == originalList, false); + }); + + test('List.indexOf for list', () { + final realm = getMixedRealm(); + final originalList = [1]; + final managedList = realm.write(() { + return realm.add(AnythingGoes(manyAny: [RealmValue.from(originalList)])).manyAny; + }); + + final unmanagedList = [RealmValue.from(originalList)]; + + expect(managedList.isManaged, true); + + expect(managedList.indexOf(RealmValue.from(originalList)), -1); + expect(managedList.indexOf(managedList.first), -1); + expect(managedList.contains(RealmValue.from(originalList)), false); + expect(managedList.contains(managedList.first), false); + + expect(managedList.asResults().indexOf(RealmValue.from(originalList)), -1); + expect(managedList.asResults().indexOf(managedList.first), -1); + expect(managedList.asResults().contains(RealmValue.from(originalList)), false); + expect(managedList.asResults().contains(managedList.first), false); + + expect(unmanagedList.indexOf(RealmValue.from(originalList)), -1); + expect(unmanagedList.indexOf(unmanagedList.first), -1); + expect(unmanagedList.contains(RealmValue.from(originalList)), false); + expect(unmanagedList.contains(unmanagedList.first), false); + }); + + test('List.indexOf for map', () { + final realm = getMixedRealm(); + final originalMap = {'foo': 1}; + final managedList = realm.write(() { + return realm.add(AnythingGoes(manyAny: [RealmValue.from(originalMap)])).manyAny; + }); + + final unmanagedList = [RealmValue.from(originalMap)]; + + expect(managedList.isManaged, true); + + expect(managedList.indexOf(RealmValue.from(originalMap)), -1); + expect(managedList.indexOf(managedList.first), -1); + expect(managedList.contains(RealmValue.from(originalMap)), false); + expect(managedList.contains(managedList.first), false); + + expect(managedList.asResults().indexOf(RealmValue.from(originalMap)), -1); + expect(managedList.asResults().indexOf(managedList.first), -1); + expect(managedList.asResults().contains(RealmValue.from(originalMap)), false); + expect(managedList.asResults().contains(managedList.first), false); + + expect(unmanagedList.indexOf(RealmValue.from(originalMap)), -1); + expect(unmanagedList.indexOf(unmanagedList.first), -1); + expect(unmanagedList.contains(RealmValue.from(originalMap)), false); + expect(unmanagedList.contains(unmanagedList.first), false); + }); + + test('Map inside RealmValue equality', () { + final realm = getMixedRealm(); + final originalMap = {'foo': 'bar'}; + final managedValue = realm.write(() { + return realm.add(AnythingGoes(oneAny: RealmValue.from(originalMap))).oneAny; + }); + + final unmanagedValue = RealmValue.from(originalMap); + + expect(managedValue.type, RealmValueType.map); + expect(unmanagedValue.type, RealmValueType.map); + + expect(managedValue.asMap().isManaged, true); + expect(unmanagedValue.asMap().isManaged, false); + + expect(managedValue == unmanagedValue, false); + expect(unmanagedValue == managedValue, false); + expect(managedValue == managedValue, false); + expect(unmanagedValue == unmanagedValue, false); + + // ignore: unrelated_type_equality_checks + expect(managedValue == originalMap, false); + + // ignore: unrelated_type_equality_checks + expect(unmanagedValue == originalMap, false); + }); + + test('Map.contains for list', () { + final realm = getMixedRealm(); + final originalList = [1]; + final managedMap = realm.write(() { + return realm.add(AnythingGoes(dictOfAny: {'foo': RealmValue.from(originalList)})).dictOfAny; + }); + + final unmanagedMap = {'foo': RealmValue.from(originalList)}; + + expect(managedMap.isManaged, true); + + expect(managedMap.containsValue(RealmValue.from(originalList)), false); + expect(managedMap.containsValue(managedMap.values.first), false); + + expect(managedMap.values.contains(RealmValue.from(originalList)), false); + expect(managedMap.values.contains(managedMap.values.first), false); + + expect(unmanagedMap.containsValue(RealmValue.from(originalList)), false); + expect(unmanagedMap.containsValue(unmanagedMap.values.first), false); + + expect(unmanagedMap.values.contains(RealmValue.from(originalList)), false); + expect(unmanagedMap.values.contains(managedMap.values.first), false); + }); + + test('Map.contains for map', () { + final realm = getMixedRealm(); + final originalMap = {'bar': 1}; + final managedMap = realm.write(() { + return realm.add(AnythingGoes(dictOfAny: {'foo': RealmValue.from(originalMap)})).dictOfAny; + }); + + final unmanagedMap = {'foo': RealmValue.from(originalMap)}; + + expect(managedMap.isManaged, true); + + expect(managedMap.containsValue(RealmValue.from(originalMap)), false); + expect(managedMap.containsValue(managedMap.values.first), false); + + expect(managedMap.values.contains(RealmValue.from(originalMap)), false); + expect(managedMap.values.contains(managedMap.values.first), false); + + expect(unmanagedMap.containsValue(RealmValue.from(originalMap)), false); + expect(unmanagedMap.containsValue(unmanagedMap.values.first), false); + + expect(unmanagedMap.values.contains(RealmValue.from(originalMap)), false); + expect(unmanagedMap.values.contains(managedMap.values.first), false); + }); + + test('Map.indexOf for map', () { + final realm = getMixedRealm(); + final originalMap = {'foo': 1}; + final managedList = realm.write(() { + return realm.add(AnythingGoes(manyAny: [RealmValue.from(originalMap)])).manyAny; + }); + + final unmanagedList = [RealmValue.from(originalMap)]; + + expect(managedList.isManaged, true); + + expect(managedList.indexOf(RealmValue.from(originalMap)), -1); + expect(managedList.indexOf(managedList.first), -1); + expect(managedList.contains(RealmValue.from(originalMap)), false); + expect(managedList.contains(managedList.first), false); + + expect(managedList.asResults().indexOf(RealmValue.from(originalMap)), -1); + expect(managedList.asResults().indexOf(managedList.first), -1); + expect(managedList.asResults().contains(RealmValue.from(originalMap)), false); + expect(managedList.asResults().contains(managedList.first), false); + + expect(unmanagedList.indexOf(RealmValue.from(originalMap)), -1); + expect(unmanagedList.indexOf(unmanagedList.first), -1); + expect(unmanagedList.contains(RealmValue.from(originalMap)), false); + expect(unmanagedList.contains(unmanagedList.first), false); + }); + + test('List in RealmValue when unmanaged is equal to original list', () { + final list = [RealmValue.bool(true), RealmValue.string('abc')]; + final rv = RealmValue.list(list); + expect(rv.asList() == list, true); + }); + + test('List in RealmValue when managed is different instance', () { + final list = [RealmValue.bool(true), RealmValue.string('abc')]; + final rv = RealmValue.list(list); + final realm = getMixedRealm(); + final obj = realm.write(() => realm.add(AnythingGoes(oneAny: rv))); + expect(identical(obj.oneAny.asList(), list), false); + }); + + test('Map in RealmValue when unmanaged is equal to original map', () { + final map = {'bool': RealmValue.bool(true), 'str': RealmValue.string('abc')}; + final rv = RealmValue.map(map); + expect(rv.asMap() == map, true); + }); + + test('Map in RealmValue when managed is different instance', () { + final map = {'bool': RealmValue.bool(true), 'str': RealmValue.string('abc')}; + final rv = RealmValue.map(map); + final realm = getMixedRealm(); + final obj = realm.write(() => realm.add(AnythingGoes(oneAny: rv))); + expect(identical(obj.oneAny.asMap(), map), false); + }); + + test('Notifications', () async { + final realm = getMixedRealm(); + final obj = AnythingGoes( + oneAny: RealmValue.from([ + 5, + { + 'foo': 'bar', + 'list': [10] + } + ])); + + realm.write(() { + realm.add(obj); + }); + + final List> parentChanges = []; + final subscription = obj.changes.listen((event) { + parentChanges.add(event); + }); + + final List> listChanges = []; + final listSubscription = obj.oneAny.asList().changes.listen((event) { + listChanges.add(event); + }); + + final List> mapChanges = []; + final mapSubscription = obj.oneAny.asList()[1].asMap().changes.listen((event) { + mapChanges.add(event); + }); + + await Future.delayed(Duration(milliseconds: 20)); + + parentChanges.clear(); + listChanges.clear(); + mapChanges.clear(); + + realm.write(() { + obj.oneAny.asList().add(RealmValue.bool(true)); + }); + + await Future.delayed(Duration(milliseconds: 20)); + + expect(parentChanges, hasLength(1)); + expect(parentChanges[0].properties, ['oneAny']); + + expect(listChanges, hasLength(1)); + expect(listChanges[0].inserted, [2]); + expect(listChanges[0].deleted, isEmpty); + expect(listChanges[0].modified, isEmpty); + expect(listChanges[0].isCleared, false); + expect(listChanges[0].isCollectionDeleted, false); + + expect(mapChanges, hasLength(0)); + + realm.write(() { + obj.oneAny.asList()[1].asMap()['list'] = RealmValue.from([10]); + obj.oneAny.asList()[1].asMap()['new-value'] = RealmValue.from({'foo': 'bar'}); + }); + + await Future.delayed(Duration(milliseconds: 20)); + + expect(parentChanges, hasLength(2)); + expect(parentChanges[1].properties, ['oneAny']); + + expect(listChanges, hasLength(2)); + expect(listChanges[1].inserted, isEmpty); + expect(listChanges[1].deleted, isEmpty); + expect(listChanges[1].modified, [1]); + expect(listChanges[1].isCleared, false); + expect(listChanges[1].isCollectionDeleted, false); + + expect(mapChanges, hasLength(1)); + expect(mapChanges[0].modified, ['list']); + expect(mapChanges[0].inserted, ['new-value']); + expect(mapChanges[0].deleted, isEmpty); + expect(mapChanges[0].isCleared, false); + expect(mapChanges[0].isCollectionDeleted, false); + + realm.write(() { + obj.oneAny.asList().removeAt(1); + }); + + await Future.delayed(Duration(milliseconds: 20)); + + expect(parentChanges, hasLength(3)); + expect(parentChanges[2].properties, ['oneAny']); + + expect(listChanges, hasLength(3)); + expect(listChanges[2].inserted, isEmpty); + expect(listChanges[2].deleted, [1]); + expect(listChanges[2].modified, isEmpty); + expect(listChanges[2].isCleared, false); + expect(listChanges[2].isCollectionDeleted, false); + + expect(mapChanges, hasLength(2)); + expect(mapChanges[1].isCollectionDeleted, true); + + subscription.cancel(); + listSubscription.cancel(); + mapSubscription.cancel(); + + realm.write(() { + obj.oneAny = RealmValue.bool(false); + }); + + await Future.delayed(Duration(milliseconds: 20)); + + // Subscriptions have been canceled - shouldn't get more notifications + expect(parentChanges, hasLength(3)); + expect(listChanges, hasLength(3)); + expect(mapChanges, hasLength(2)); + }); + + test('Queries', () { + final realm = getMixedRealm(); + + late AnythingGoes first; + late AnythingGoes second; + late AnythingGoes third; + + realm.write(() { + first = realm.add(AnythingGoes( + oneAny: RealmValue.from([ + 1, + 'a', + {'foo': 'bar'} + ]))); + + second = realm.add(AnythingGoes( + oneAny: RealmValue.from([ + 2, + {'foo': 'baz'} + ]))); + + third = realm.add(AnythingGoes( + oneAny: RealmValue.from([ + 3, + 'c', + { + 'foo': {'child': 5}, + 'bar': 10 + }, + 3.4 + ]))); + }); + + final listElementQuery = realm.query('oneAny[0] < 3'); + expect(listElementQuery, unorderedMatches([first, second])); + + final listLengthQuery = realm.query('oneAny.@size > 3'); + expect(listLengthQuery, unorderedMatches([third])); + + final listStarQuery = realm.query('oneAny[*] == 3.4'); + expect(listStarQuery, unorderedMatches([third])); + + final typeQuery = realm.query("oneAny[2].@type == 'dictionary'"); + expect(typeQuery, unorderedMatches([first, third])); + + final dictionaryInListQuery = realm.query("oneAny[*].foo BEGINSWITH 'ba'"); + expect(dictionaryInListQuery, unorderedMatches([first, second])); + + final dictionaryKeysQuery = realm.query("oneAny[*].foo.@keys == 'child'"); + expect(dictionaryKeysQuery, unorderedMatches([third])); + + final noMatchesQuery = realm.query("oneAny[*].bar == 9"); + expect(noMatchesQuery, isEmpty); + }); + }); } diff --git a/packages/realm_dart/test/realm_value_test.realm.dart b/packages/realm_dart/test/realm_value_test.realm.dart index f5596faae..53c9c96de 100644 --- a/packages/realm_dart/test/realm_value_test.realm.dart +++ b/packages/realm_dart/test/realm_value_test.realm.dart @@ -51,10 +51,16 @@ class AnythingGoes extends _AnythingGoes AnythingGoes({ RealmValue oneAny = const RealmValue.nullValue(), Iterable manyAny = const [], + Set setOfAny = const {}, + Map dictOfAny = const {}, }) { RealmObjectBase.set(this, 'oneAny', oneAny); RealmObjectBase.set>( this, 'manyAny', RealmList(manyAny)); + RealmObjectBase.set>( + this, 'setOfAny', RealmSet(setOfAny)); + RealmObjectBase.set>( + this, 'dictOfAny', RealmMap(dictOfAny)); } AnythingGoes._(); @@ -72,6 +78,21 @@ class AnythingGoes extends _AnythingGoes set manyAny(covariant RealmList value) => throw RealmUnsupportedSetError(); + @override + RealmMap get dictOfAny => + RealmObjectBase.get(this, 'dictOfAny') + as RealmMap; + @override + set dictOfAny(covariant RealmMap value) => + throw RealmUnsupportedSetError(); + + @override + RealmSet get setOfAny => + RealmObjectBase.get(this, 'setOfAny') as RealmSet; + @override + set setOfAny(covariant RealmSet value) => + throw RealmUnsupportedSetError(); + @override Stream> get changes => RealmObjectBase.getChanges(this); @@ -89,6 +110,10 @@ class AnythingGoes extends _AnythingGoes optional: true, indexType: RealmIndexType.regular), SchemaProperty('manyAny', RealmPropertyType.mixed, optional: true, collectionType: RealmCollectionType.list), + SchemaProperty('dictOfAny', RealmPropertyType.mixed, + optional: true, collectionType: RealmCollectionType.map), + SchemaProperty('setOfAny', RealmPropertyType.mixed, + optional: true, collectionType: RealmCollectionType.set), ]); } } diff --git a/packages/realm_dart/test/results_test.dart b/packages/realm_dart/test/results_test.dart index 377d7360b..8c0cd0873 100644 --- a/packages/realm_dart/test/results_test.dart +++ b/packages/realm_dart/test/results_test.dart @@ -250,7 +250,7 @@ void main() { var config = Configuration.local([Car.schema]); var realm = getRealm(config); realm.write(() => realm.add(Car("Audi"))); - expect(() => realm.all().query(r'make == $0', [1]), throws("Unsupported comparison between type")); + expect(() => realm.all().query(r'make == $0', [1]), throws("Cannot compare argument")); }); test('Results query with wrong argument types (bool for int) throws ', () { diff --git a/packages/realm_dart/test/test.dart b/packages/realm_dart/test/test.dart index a1e0da61a..134ea76fe 100644 --- a/packages/realm_dart/test/test.dart +++ b/packages/realm_dart/test/test.dart @@ -158,27 +158,65 @@ class _LinksClass { late _LinksClass? link; late List<_LinksClass> list; + late Set<_LinksClass> linksSet; + late Map map; } @RealmModel() class _AllCollections { - late List strings; - late List bools; - late List dates; - late List doubles; - late List objectIds; - late List uuids; - late List ints; - late List decimals; - - late List nullableStrings; - late List nullableBools; - late List nullableDates; - late List nullableDoubles; - late List nullableObjectIds; - late List nullableUuids; - late List nullableInts; - late List nullableDecimals; + late List stringList; + late List boolList; + late List dateList; + late List doubleList; + late List objectIdList; + late List uuidList; + late List intList; + late List decimalList; + + late List nullableStringList; + late List nullableBoolList; + late List nullableDateList; + late List nullableDoubleList; + late List nullableObjectIdList; + late List nullableUuidList; + late List nullableIntList; + late List nullableDecimalList; + + late Set stringSet; + late Set boolSet; + late Set dateSet; + late Set doubleSet; + late Set objectIdSet; + late Set uuidSet; + late Set intSet; + late Set decimalSet; + + late Set nullableStringSet; + late Set nullableBoolSet; + late Set nullableDateSet; + late Set nullableDoubleSet; + late Set nullableObjectIdSet; + late Set nullableUuidSet; + late Set nullableIntSet; + late Set nullableDecimalSet; + + late Map stringMap; + late Map boolMap; + late Map dateMap; + late Map doubleMap; + late Map objectIdMap; + late Map uuidMap; + late Map intMap; + late Map decimalMap; + + late Map nullableStringMap; + late Map nullableBoolMap; + late Map nullableDateMap; + late Map nullableDoubleMap; + late Map nullableObjectIdMap; + late Map nullableUuidMap; + late Map nullableIntMap; + late Map nullableDecimalMap; } @RealmModel() diff --git a/packages/realm_dart/test/test.realm.dart b/packages/realm_dart/test/test.realm.dart index 616cc5492..cb62a1106 100644 --- a/packages/realm_dart/test/test.realm.dart +++ b/packages/realm_dart/test/test.realm.dart @@ -742,11 +742,17 @@ class LinksClass extends _LinksClass Uuid id, { LinksClass? link, Iterable list = const [], + Set linksSet = const {}, + Map map = const {}, }) { RealmObjectBase.set(this, 'id', id); RealmObjectBase.set(this, 'link', link); RealmObjectBase.set>( this, 'list', RealmList(list)); + RealmObjectBase.set>( + this, 'linksSet', RealmSet(linksSet)); + RealmObjectBase.set>( + this, 'map', RealmMap(map)); } LinksClass._(); @@ -770,6 +776,20 @@ class LinksClass extends _LinksClass set list(covariant RealmList value) => throw RealmUnsupportedSetError(); + @override + RealmSet get linksSet => + RealmObjectBase.get(this, 'linksSet') as RealmSet; + @override + set linksSet(covariant RealmSet value) => + throw RealmUnsupportedSetError(); + + @override + RealmMap get map => + RealmObjectBase.get(this, 'map') as RealmMap; + @override + set map(covariant RealmMap value) => + throw RealmUnsupportedSetError(); + @override Stream> get changes => RealmObjectBase.getChanges(this); @@ -788,6 +808,12 @@ class LinksClass extends _LinksClass optional: true, linkTarget: 'LinksClass'), SchemaProperty('list', RealmPropertyType.object, linkTarget: 'LinksClass', collectionType: RealmCollectionType.list), + SchemaProperty('linksSet', RealmPropertyType.object, + linkTarget: 'LinksClass', collectionType: RealmCollectionType.set), + SchemaProperty('map', RealmPropertyType.object, + optional: true, + linkTarget: 'LinksClass', + collectionType: RealmCollectionType.map), ]); } } @@ -795,171 +821,504 @@ class LinksClass extends _LinksClass class AllCollections extends _AllCollections with RealmEntity, RealmObjectBase, RealmObject { AllCollections({ - Iterable strings = const [], - Iterable bools = const [], - Iterable dates = const [], - Iterable doubles = const [], - Iterable objectIds = const [], - Iterable uuids = const [], - Iterable ints = const [], - Iterable decimals = const [], - Iterable nullableStrings = const [], - Iterable nullableBools = const [], - Iterable nullableDates = const [], - Iterable nullableDoubles = const [], - Iterable nullableObjectIds = const [], - Iterable nullableUuids = const [], - Iterable nullableInts = const [], - Iterable nullableDecimals = const [], + Iterable stringList = const [], + Iterable boolList = const [], + Iterable dateList = const [], + Iterable doubleList = const [], + Iterable objectIdList = const [], + Iterable uuidList = const [], + Iterable intList = const [], + Iterable decimalList = const [], + Iterable nullableStringList = const [], + Iterable nullableBoolList = const [], + Iterable nullableDateList = const [], + Iterable nullableDoubleList = const [], + Iterable nullableObjectIdList = const [], + Iterable nullableUuidList = const [], + Iterable nullableIntList = const [], + Iterable nullableDecimalList = const [], + Set stringSet = const {}, + Set boolSet = const {}, + Set dateSet = const {}, + Set doubleSet = const {}, + Set objectIdSet = const {}, + Set uuidSet = const {}, + Set intSet = const {}, + Set decimalSet = const {}, + Set nullableStringSet = const {}, + Set nullableBoolSet = const {}, + Set nullableDateSet = const {}, + Set nullableDoubleSet = const {}, + Set nullableObjectIdSet = const {}, + Set nullableUuidSet = const {}, + Set nullableIntSet = const {}, + Set nullableDecimalSet = const {}, + Map stringMap = const {}, + Map boolMap = const {}, + Map dateMap = const {}, + Map doubleMap = const {}, + Map objectIdMap = const {}, + Map uuidMap = const {}, + Map intMap = const {}, + Map decimalMap = const {}, + Map nullableStringMap = const {}, + Map nullableBoolMap = const {}, + Map nullableDateMap = const {}, + Map nullableDoubleMap = const {}, + Map nullableObjectIdMap = const {}, + Map nullableUuidMap = const {}, + Map nullableIntMap = const {}, + Map nullableDecimalMap = const {}, }) { RealmObjectBase.set>( - this, 'strings', RealmList(strings)); - RealmObjectBase.set>(this, 'bools', RealmList(bools)); + this, 'stringList', RealmList(stringList)); + RealmObjectBase.set>( + this, 'boolList', RealmList(boolList)); RealmObjectBase.set>( - this, 'dates', RealmList(dates)); + this, 'dateList', RealmList(dateList)); RealmObjectBase.set>( - this, 'doubles', RealmList(doubles)); + this, 'doubleList', RealmList(doubleList)); RealmObjectBase.set>( - this, 'objectIds', RealmList(objectIds)); - RealmObjectBase.set>(this, 'uuids', RealmList(uuids)); - RealmObjectBase.set>(this, 'ints', RealmList(ints)); + this, 'objectIdList', RealmList(objectIdList)); + RealmObjectBase.set>( + this, 'uuidList', RealmList(uuidList)); + RealmObjectBase.set>( + this, 'intList', RealmList(intList)); RealmObjectBase.set>( - this, 'decimals', RealmList(decimals)); + this, 'decimalList', RealmList(decimalList)); RealmObjectBase.set>( - this, 'nullableStrings', RealmList(nullableStrings)); + this, 'nullableStringList', RealmList(nullableStringList)); RealmObjectBase.set>( - this, 'nullableBools', RealmList(nullableBools)); + this, 'nullableBoolList', RealmList(nullableBoolList)); RealmObjectBase.set>( - this, 'nullableDates', RealmList(nullableDates)); + this, 'nullableDateList', RealmList(nullableDateList)); RealmObjectBase.set>( - this, 'nullableDoubles', RealmList(nullableDoubles)); - RealmObjectBase.set>( - this, 'nullableObjectIds', RealmList(nullableObjectIds)); + this, 'nullableDoubleList', RealmList(nullableDoubleList)); + RealmObjectBase.set>(this, 'nullableObjectIdList', + RealmList(nullableObjectIdList)); RealmObjectBase.set>( - this, 'nullableUuids', RealmList(nullableUuids)); + this, 'nullableUuidList', RealmList(nullableUuidList)); RealmObjectBase.set>( - this, 'nullableInts', RealmList(nullableInts)); - RealmObjectBase.set>( - this, 'nullableDecimals', RealmList(nullableDecimals)); + this, 'nullableIntList', RealmList(nullableIntList)); + RealmObjectBase.set>(this, 'nullableDecimalList', + RealmList(nullableDecimalList)); + RealmObjectBase.set>( + this, 'stringSet', RealmSet(stringSet)); + RealmObjectBase.set>( + this, 'boolSet', RealmSet(boolSet)); + RealmObjectBase.set>( + this, 'dateSet', RealmSet(dateSet)); + RealmObjectBase.set>( + this, 'doubleSet', RealmSet(doubleSet)); + RealmObjectBase.set>( + this, 'objectIdSet', RealmSet(objectIdSet)); + RealmObjectBase.set>( + this, 'uuidSet', RealmSet(uuidSet)); + RealmObjectBase.set>(this, 'intSet', RealmSet(intSet)); + RealmObjectBase.set>( + this, 'decimalSet', RealmSet(decimalSet)); + RealmObjectBase.set>( + this, 'nullableStringSet', RealmSet(nullableStringSet)); + RealmObjectBase.set>( + this, 'nullableBoolSet', RealmSet(nullableBoolSet)); + RealmObjectBase.set>( + this, 'nullableDateSet', RealmSet(nullableDateSet)); + RealmObjectBase.set>( + this, 'nullableDoubleSet', RealmSet(nullableDoubleSet)); + RealmObjectBase.set>( + this, 'nullableObjectIdSet', RealmSet(nullableObjectIdSet)); + RealmObjectBase.set>( + this, 'nullableUuidSet', RealmSet(nullableUuidSet)); + RealmObjectBase.set>( + this, 'nullableIntSet', RealmSet(nullableIntSet)); + RealmObjectBase.set>( + this, 'nullableDecimalSet', RealmSet(nullableDecimalSet)); + RealmObjectBase.set>( + this, 'stringMap', RealmMap(stringMap)); + RealmObjectBase.set>( + this, 'boolMap', RealmMap(boolMap)); + RealmObjectBase.set>( + this, 'dateMap', RealmMap(dateMap)); + RealmObjectBase.set>( + this, 'doubleMap', RealmMap(doubleMap)); + RealmObjectBase.set>( + this, 'objectIdMap', RealmMap(objectIdMap)); + RealmObjectBase.set>( + this, 'uuidMap', RealmMap(uuidMap)); + RealmObjectBase.set>(this, 'intMap', RealmMap(intMap)); + RealmObjectBase.set>( + this, 'decimalMap', RealmMap(decimalMap)); + RealmObjectBase.set>( + this, 'nullableStringMap', RealmMap(nullableStringMap)); + RealmObjectBase.set>( + this, 'nullableBoolMap', RealmMap(nullableBoolMap)); + RealmObjectBase.set>( + this, 'nullableDateMap', RealmMap(nullableDateMap)); + RealmObjectBase.set>( + this, 'nullableDoubleMap', RealmMap(nullableDoubleMap)); + RealmObjectBase.set>( + this, 'nullableObjectIdMap', RealmMap(nullableObjectIdMap)); + RealmObjectBase.set>( + this, 'nullableUuidMap', RealmMap(nullableUuidMap)); + RealmObjectBase.set>( + this, 'nullableIntMap', RealmMap(nullableIntMap)); + RealmObjectBase.set>( + this, 'nullableDecimalMap', RealmMap(nullableDecimalMap)); } AllCollections._(); @override - RealmList get strings => - RealmObjectBase.get(this, 'strings') as RealmList; + RealmList get stringList => + RealmObjectBase.get(this, 'stringList') as RealmList; @override - set strings(covariant RealmList value) => + set stringList(covariant RealmList value) => throw RealmUnsupportedSetError(); @override - RealmList get bools => - RealmObjectBase.get(this, 'bools') as RealmList; + RealmList get boolList => + RealmObjectBase.get(this, 'boolList') as RealmList; @override - set bools(covariant RealmList value) => + set boolList(covariant RealmList value) => throw RealmUnsupportedSetError(); @override - RealmList get dates => - RealmObjectBase.get(this, 'dates') as RealmList; + RealmList get dateList => + RealmObjectBase.get(this, 'dateList') as RealmList; @override - set dates(covariant RealmList value) => + set dateList(covariant RealmList value) => throw RealmUnsupportedSetError(); @override - RealmList get doubles => - RealmObjectBase.get(this, 'doubles') as RealmList; + RealmList get doubleList => + RealmObjectBase.get(this, 'doubleList') as RealmList; @override - set doubles(covariant RealmList value) => + set doubleList(covariant RealmList value) => throw RealmUnsupportedSetError(); @override - RealmList get objectIds => - RealmObjectBase.get(this, 'objectIds') as RealmList; + RealmList get objectIdList => + RealmObjectBase.get(this, 'objectIdList') + as RealmList; @override - set objectIds(covariant RealmList value) => + set objectIdList(covariant RealmList value) => throw RealmUnsupportedSetError(); @override - RealmList get uuids => - RealmObjectBase.get(this, 'uuids') as RealmList; + RealmList get uuidList => + RealmObjectBase.get(this, 'uuidList') as RealmList; @override - set uuids(covariant RealmList value) => + set uuidList(covariant RealmList value) => throw RealmUnsupportedSetError(); @override - RealmList get ints => - RealmObjectBase.get(this, 'ints') as RealmList; + RealmList get intList => + RealmObjectBase.get(this, 'intList') as RealmList; @override - set ints(covariant RealmList value) => throw RealmUnsupportedSetError(); + set intList(covariant RealmList value) => + throw RealmUnsupportedSetError(); @override - RealmList get decimals => - RealmObjectBase.get(this, 'decimals') + RealmList get decimalList => + RealmObjectBase.get(this, 'decimalList') as RealmList; @override - set decimals(covariant RealmList value) => + set decimalList(covariant RealmList value) => throw RealmUnsupportedSetError(); @override - RealmList get nullableStrings => - RealmObjectBase.get(this, 'nullableStrings') + RealmList get nullableStringList => + RealmObjectBase.get(this, 'nullableStringList') as RealmList; @override - set nullableStrings(covariant RealmList value) => + set nullableStringList(covariant RealmList value) => throw RealmUnsupportedSetError(); @override - RealmList get nullableBools => - RealmObjectBase.get(this, 'nullableBools') as RealmList; + RealmList get nullableBoolList => + RealmObjectBase.get(this, 'nullableBoolList') as RealmList; @override - set nullableBools(covariant RealmList value) => + set nullableBoolList(covariant RealmList value) => throw RealmUnsupportedSetError(); @override - RealmList get nullableDates => - RealmObjectBase.get(this, 'nullableDates') + RealmList get nullableDateList => + RealmObjectBase.get(this, 'nullableDateList') as RealmList; @override - set nullableDates(covariant RealmList value) => + set nullableDateList(covariant RealmList value) => throw RealmUnsupportedSetError(); @override - RealmList get nullableDoubles => - RealmObjectBase.get(this, 'nullableDoubles') + RealmList get nullableDoubleList => + RealmObjectBase.get(this, 'nullableDoubleList') as RealmList; @override - set nullableDoubles(covariant RealmList value) => + set nullableDoubleList(covariant RealmList value) => throw RealmUnsupportedSetError(); @override - RealmList get nullableObjectIds => - RealmObjectBase.get(this, 'nullableObjectIds') + RealmList get nullableObjectIdList => + RealmObjectBase.get(this, 'nullableObjectIdList') as RealmList; @override - set nullableObjectIds(covariant RealmList value) => + set nullableObjectIdList(covariant RealmList value) => throw RealmUnsupportedSetError(); @override - RealmList get nullableUuids => - RealmObjectBase.get(this, 'nullableUuids') as RealmList; + RealmList get nullableUuidList => + RealmObjectBase.get(this, 'nullableUuidList') as RealmList; @override - set nullableUuids(covariant RealmList value) => + set nullableUuidList(covariant RealmList value) => throw RealmUnsupportedSetError(); @override - RealmList get nullableInts => - RealmObjectBase.get(this, 'nullableInts') as RealmList; + RealmList get nullableIntList => + RealmObjectBase.get(this, 'nullableIntList') as RealmList; @override - set nullableInts(covariant RealmList value) => + set nullableIntList(covariant RealmList value) => throw RealmUnsupportedSetError(); @override - RealmList get nullableDecimals => - RealmObjectBase.get(this, 'nullableDecimals') + RealmList get nullableDecimalList => + RealmObjectBase.get(this, 'nullableDecimalList') as RealmList; @override - set nullableDecimals(covariant RealmList value) => + set nullableDecimalList(covariant RealmList value) => + throw RealmUnsupportedSetError(); + + @override + RealmSet get stringSet => + RealmObjectBase.get(this, 'stringSet') as RealmSet; + @override + set stringSet(covariant RealmSet value) => + throw RealmUnsupportedSetError(); + + @override + RealmSet get boolSet => + RealmObjectBase.get(this, 'boolSet') as RealmSet; + @override + set boolSet(covariant RealmSet value) => + throw RealmUnsupportedSetError(); + + @override + RealmSet get dateSet => + RealmObjectBase.get(this, 'dateSet') as RealmSet; + @override + set dateSet(covariant RealmSet value) => + throw RealmUnsupportedSetError(); + + @override + RealmSet get doubleSet => + RealmObjectBase.get(this, 'doubleSet') as RealmSet; + @override + set doubleSet(covariant RealmSet value) => + throw RealmUnsupportedSetError(); + + @override + RealmSet get objectIdSet => + RealmObjectBase.get(this, 'objectIdSet') as RealmSet; + @override + set objectIdSet(covariant RealmSet value) => + throw RealmUnsupportedSetError(); + + @override + RealmSet get uuidSet => + RealmObjectBase.get(this, 'uuidSet') as RealmSet; + @override + set uuidSet(covariant RealmSet value) => + throw RealmUnsupportedSetError(); + + @override + RealmSet get intSet => + RealmObjectBase.get(this, 'intSet') as RealmSet; + @override + set intSet(covariant RealmSet value) => throw RealmUnsupportedSetError(); + + @override + RealmSet get decimalSet => + RealmObjectBase.get(this, 'decimalSet') + as RealmSet; + @override + set decimalSet(covariant RealmSet value) => + throw RealmUnsupportedSetError(); + + @override + RealmSet get nullableStringSet => + RealmObjectBase.get(this, 'nullableStringSet') + as RealmSet; + @override + set nullableStringSet(covariant RealmSet value) => + throw RealmUnsupportedSetError(); + + @override + RealmSet get nullableBoolSet => + RealmObjectBase.get(this, 'nullableBoolSet') as RealmSet; + @override + set nullableBoolSet(covariant RealmSet value) => + throw RealmUnsupportedSetError(); + + @override + RealmSet get nullableDateSet => + RealmObjectBase.get(this, 'nullableDateSet') + as RealmSet; + @override + set nullableDateSet(covariant RealmSet value) => + throw RealmUnsupportedSetError(); + + @override + RealmSet get nullableDoubleSet => + RealmObjectBase.get(this, 'nullableDoubleSet') + as RealmSet; + @override + set nullableDoubleSet(covariant RealmSet value) => + throw RealmUnsupportedSetError(); + + @override + RealmSet get nullableObjectIdSet => + RealmObjectBase.get(this, 'nullableObjectIdSet') + as RealmSet; + @override + set nullableObjectIdSet(covariant RealmSet value) => + throw RealmUnsupportedSetError(); + + @override + RealmSet get nullableUuidSet => + RealmObjectBase.get(this, 'nullableUuidSet') as RealmSet; + @override + set nullableUuidSet(covariant RealmSet value) => + throw RealmUnsupportedSetError(); + + @override + RealmSet get nullableIntSet => + RealmObjectBase.get(this, 'nullableIntSet') as RealmSet; + @override + set nullableIntSet(covariant RealmSet value) => + throw RealmUnsupportedSetError(); + + @override + RealmSet get nullableDecimalSet => + RealmObjectBase.get(this, 'nullableDecimalSet') + as RealmSet; + @override + set nullableDecimalSet(covariant RealmSet value) => + throw RealmUnsupportedSetError(); + + @override + RealmMap get stringMap => + RealmObjectBase.get(this, 'stringMap') as RealmMap; + @override + set stringMap(covariant RealmMap value) => + throw RealmUnsupportedSetError(); + + @override + RealmMap get boolMap => + RealmObjectBase.get(this, 'boolMap') as RealmMap; + @override + set boolMap(covariant RealmMap value) => + throw RealmUnsupportedSetError(); + + @override + RealmMap get dateMap => + RealmObjectBase.get(this, 'dateMap') as RealmMap; + @override + set dateMap(covariant RealmMap value) => + throw RealmUnsupportedSetError(); + + @override + RealmMap get doubleMap => + RealmObjectBase.get(this, 'doubleMap') as RealmMap; + @override + set doubleMap(covariant RealmMap value) => + throw RealmUnsupportedSetError(); + + @override + RealmMap get objectIdMap => + RealmObjectBase.get(this, 'objectIdMap') as RealmMap; + @override + set objectIdMap(covariant RealmMap value) => + throw RealmUnsupportedSetError(); + + @override + RealmMap get uuidMap => + RealmObjectBase.get(this, 'uuidMap') as RealmMap; + @override + set uuidMap(covariant RealmMap value) => + throw RealmUnsupportedSetError(); + + @override + RealmMap get intMap => + RealmObjectBase.get(this, 'intMap') as RealmMap; + @override + set intMap(covariant RealmMap value) => throw RealmUnsupportedSetError(); + + @override + RealmMap get decimalMap => + RealmObjectBase.get(this, 'decimalMap') + as RealmMap; + @override + set decimalMap(covariant RealmMap value) => + throw RealmUnsupportedSetError(); + + @override + RealmMap get nullableStringMap => + RealmObjectBase.get(this, 'nullableStringMap') + as RealmMap; + @override + set nullableStringMap(covariant RealmMap value) => + throw RealmUnsupportedSetError(); + + @override + RealmMap get nullableBoolMap => + RealmObjectBase.get(this, 'nullableBoolMap') as RealmMap; + @override + set nullableBoolMap(covariant RealmMap value) => + throw RealmUnsupportedSetError(); + + @override + RealmMap get nullableDateMap => + RealmObjectBase.get(this, 'nullableDateMap') + as RealmMap; + @override + set nullableDateMap(covariant RealmMap value) => + throw RealmUnsupportedSetError(); + + @override + RealmMap get nullableDoubleMap => + RealmObjectBase.get(this, 'nullableDoubleMap') + as RealmMap; + @override + set nullableDoubleMap(covariant RealmMap value) => + throw RealmUnsupportedSetError(); + + @override + RealmMap get nullableObjectIdMap => + RealmObjectBase.get(this, 'nullableObjectIdMap') + as RealmMap; + @override + set nullableObjectIdMap(covariant RealmMap value) => + throw RealmUnsupportedSetError(); + + @override + RealmMap get nullableUuidMap => + RealmObjectBase.get(this, 'nullableUuidMap') as RealmMap; + @override + set nullableUuidMap(covariant RealmMap value) => + throw RealmUnsupportedSetError(); + + @override + RealmMap get nullableIntMap => + RealmObjectBase.get(this, 'nullableIntMap') as RealmMap; + @override + set nullableIntMap(covariant RealmMap value) => + throw RealmUnsupportedSetError(); + + @override + RealmMap get nullableDecimalMap => + RealmObjectBase.get(this, 'nullableDecimalMap') + as RealmMap; + @override + set nullableDecimalMap(covariant RealmMap value) => throw RealmUnsupportedSetError(); @override @@ -975,38 +1334,102 @@ class AllCollections extends _AllCollections RealmObjectBase.registerFactory(AllCollections._); return const SchemaObject( ObjectType.realmObject, AllCollections, 'AllCollections', [ - SchemaProperty('strings', RealmPropertyType.string, + SchemaProperty('stringList', RealmPropertyType.string, collectionType: RealmCollectionType.list), - SchemaProperty('bools', RealmPropertyType.bool, + SchemaProperty('boolList', RealmPropertyType.bool, collectionType: RealmCollectionType.list), - SchemaProperty('dates', RealmPropertyType.timestamp, + SchemaProperty('dateList', RealmPropertyType.timestamp, collectionType: RealmCollectionType.list), - SchemaProperty('doubles', RealmPropertyType.double, + SchemaProperty('doubleList', RealmPropertyType.double, collectionType: RealmCollectionType.list), - SchemaProperty('objectIds', RealmPropertyType.objectid, + SchemaProperty('objectIdList', RealmPropertyType.objectid, collectionType: RealmCollectionType.list), - SchemaProperty('uuids', RealmPropertyType.uuid, + SchemaProperty('uuidList', RealmPropertyType.uuid, collectionType: RealmCollectionType.list), - SchemaProperty('ints', RealmPropertyType.int, + SchemaProperty('intList', RealmPropertyType.int, collectionType: RealmCollectionType.list), - SchemaProperty('decimals', RealmPropertyType.decimal128, + SchemaProperty('decimalList', RealmPropertyType.decimal128, collectionType: RealmCollectionType.list), - SchemaProperty('nullableStrings', RealmPropertyType.string, + SchemaProperty('nullableStringList', RealmPropertyType.string, optional: true, collectionType: RealmCollectionType.list), - SchemaProperty('nullableBools', RealmPropertyType.bool, + SchemaProperty('nullableBoolList', RealmPropertyType.bool, optional: true, collectionType: RealmCollectionType.list), - SchemaProperty('nullableDates', RealmPropertyType.timestamp, + SchemaProperty('nullableDateList', RealmPropertyType.timestamp, optional: true, collectionType: RealmCollectionType.list), - SchemaProperty('nullableDoubles', RealmPropertyType.double, + SchemaProperty('nullableDoubleList', RealmPropertyType.double, optional: true, collectionType: RealmCollectionType.list), - SchemaProperty('nullableObjectIds', RealmPropertyType.objectid, + SchemaProperty('nullableObjectIdList', RealmPropertyType.objectid, optional: true, collectionType: RealmCollectionType.list), - SchemaProperty('nullableUuids', RealmPropertyType.uuid, + SchemaProperty('nullableUuidList', RealmPropertyType.uuid, optional: true, collectionType: RealmCollectionType.list), - SchemaProperty('nullableInts', RealmPropertyType.int, + SchemaProperty('nullableIntList', RealmPropertyType.int, optional: true, collectionType: RealmCollectionType.list), - SchemaProperty('nullableDecimals', RealmPropertyType.decimal128, + SchemaProperty('nullableDecimalList', RealmPropertyType.decimal128, optional: true, collectionType: RealmCollectionType.list), + SchemaProperty('stringSet', RealmPropertyType.string, + collectionType: RealmCollectionType.set), + SchemaProperty('boolSet', RealmPropertyType.bool, + collectionType: RealmCollectionType.set), + SchemaProperty('dateSet', RealmPropertyType.timestamp, + collectionType: RealmCollectionType.set), + SchemaProperty('doubleSet', RealmPropertyType.double, + collectionType: RealmCollectionType.set), + SchemaProperty('objectIdSet', RealmPropertyType.objectid, + collectionType: RealmCollectionType.set), + SchemaProperty('uuidSet', RealmPropertyType.uuid, + collectionType: RealmCollectionType.set), + SchemaProperty('intSet', RealmPropertyType.int, + collectionType: RealmCollectionType.set), + SchemaProperty('decimalSet', RealmPropertyType.decimal128, + collectionType: RealmCollectionType.set), + SchemaProperty('nullableStringSet', RealmPropertyType.string, + optional: true, collectionType: RealmCollectionType.set), + SchemaProperty('nullableBoolSet', RealmPropertyType.bool, + optional: true, collectionType: RealmCollectionType.set), + SchemaProperty('nullableDateSet', RealmPropertyType.timestamp, + optional: true, collectionType: RealmCollectionType.set), + SchemaProperty('nullableDoubleSet', RealmPropertyType.double, + optional: true, collectionType: RealmCollectionType.set), + SchemaProperty('nullableObjectIdSet', RealmPropertyType.objectid, + optional: true, collectionType: RealmCollectionType.set), + SchemaProperty('nullableUuidSet', RealmPropertyType.uuid, + optional: true, collectionType: RealmCollectionType.set), + SchemaProperty('nullableIntSet', RealmPropertyType.int, + optional: true, collectionType: RealmCollectionType.set), + SchemaProperty('nullableDecimalSet', RealmPropertyType.decimal128, + optional: true, collectionType: RealmCollectionType.set), + SchemaProperty('stringMap', RealmPropertyType.string, + collectionType: RealmCollectionType.map), + SchemaProperty('boolMap', RealmPropertyType.bool, + collectionType: RealmCollectionType.map), + SchemaProperty('dateMap', RealmPropertyType.timestamp, + collectionType: RealmCollectionType.map), + SchemaProperty('doubleMap', RealmPropertyType.double, + collectionType: RealmCollectionType.map), + SchemaProperty('objectIdMap', RealmPropertyType.objectid, + collectionType: RealmCollectionType.map), + SchemaProperty('uuidMap', RealmPropertyType.uuid, + collectionType: RealmCollectionType.map), + SchemaProperty('intMap', RealmPropertyType.int, + collectionType: RealmCollectionType.map), + SchemaProperty('decimalMap', RealmPropertyType.decimal128, + collectionType: RealmCollectionType.map), + SchemaProperty('nullableStringMap', RealmPropertyType.string, + optional: true, collectionType: RealmCollectionType.map), + SchemaProperty('nullableBoolMap', RealmPropertyType.bool, + optional: true, collectionType: RealmCollectionType.map), + SchemaProperty('nullableDateMap', RealmPropertyType.timestamp, + optional: true, collectionType: RealmCollectionType.map), + SchemaProperty('nullableDoubleMap', RealmPropertyType.double, + optional: true, collectionType: RealmCollectionType.map), + SchemaProperty('nullableObjectIdMap', RealmPropertyType.objectid, + optional: true, collectionType: RealmCollectionType.map), + SchemaProperty('nullableUuidMap', RealmPropertyType.uuid, + optional: true, collectionType: RealmCollectionType.map), + SchemaProperty('nullableIntMap', RealmPropertyType.int, + optional: true, collectionType: RealmCollectionType.map), + SchemaProperty('nullableDecimalMap', RealmPropertyType.decimal128, + optional: true, collectionType: RealmCollectionType.map), ]); } } diff --git a/packages/realm_generator/pubspec.yaml b/packages/realm_generator/pubspec.yaml index 8c1440cb6..57f0f578f 100644 --- a/packages/realm_generator/pubspec.yaml +++ b/packages/realm_generator/pubspec.yaml @@ -3,7 +3,7 @@ description: >- Generates RealmObject classes from Realm data model classes. This package is part of the official Realm Flutter and Realm Dart SDKs. -version: 1.9.0 +version: 2.0.0-alpha.2 homepage: https://www.realm.io repository: https://github.com/realm/realm-dart