realm/jazzy 7262
Soulful docs for Swift & Objective-C
DEPRECATED - Realm Browser for Mac OS X has been replaced by realm-studio which is cross platform.
realm/realm-android-adapters 414
Adapters for combining Realm Java with Android UI components and framework classes
Generate Gantt Charts From Github Issues!
realm/RChat 72
A template conference app, featuring real-time schedule and data changes & running on Realm 🚀
Components that simplifies using Realm with React
ToDo demo app using Realm and Realm Object Server to synchronize tasks.
realm/node-template-project 19
A template for your Node and TypeScript Project with Visual Studio Code Debugging!
PR opened realm/realm-dart
This will allow other builders to consume output of the realm generator.
Generated files are now suffixed with .realm.dart
instead of .g.dart
.
pr created time in 16 minutes
push eventrealm/realm-js
commit sha 0e4303b779e6e180e4b5325f116e1e681c76aa9f
Corrected changelog
push time in 33 minutes
push eventrealm/realm-js
commit sha eba0df9187e8b9754037013ccf1ca96318ffd574
Updated changelog
push time in 35 minutes
push eventrealm/realm-js
commit sha 525071c726f6a26ba0b5c59381e6dbc464389de5
Enable cleartext traffic in android test app to make tests work in release builds
commit sha 7a6a769265d515a00363ed1710e8cb8cec442102
Update package-unit-tests.yml to add ccache and ninja (#5837)
commit sha e3d63cdcccbc28a1d0ddb0fba7e9dc6df3fa565f
Update install-test-react-native.yml (#5848)
commit sha 24e875d3391fb7f1d91d54d15103abfee27539d7
Fix warning for deprecated namespace setting method in Android (#5862)
commit sha 3a00265cff2edbd810ff97f8bdfd0a6603002617
Add Flexible Sync subscribe/unsubscribe APIs (#5772) * Implement 'subscribe()' w/o 'timeout' option. * Add initial 'subscribe()' tests. * Implement 'unsubscribe()'. * Add initial 'unsubscribe()' tests. * Refactor 'TimeoutPromise' to optionally not reject on timeout. * Update 'subscribe()' to handle 'timeout' option. * Update tests. * Update 'unsubscribe()' and let 'mutableSubs.remove()' handle the not-found case. * Replace 'Results' instance field with call to Core. * Add 'unnamedOnly' param to 'MutableSubscriptionSet.removeAll()'. * Add test for removing unnamed subscriptions. * Add CHANGELOG entry. * Update formatting. * Update TSDocs. * Add test for 'subscribe()'. * Update use of 'SubscriptionsState' to 'SubscriptionSetState'. * Remove 'unnamedOnly' param and create 'removeUnnamed()' method. * Treat a subscription with an empty name as named. (Same behavior as v11) * Mark 'Results.unsubscribe()' as experimental. * Remove boolean return type from 'Results.unsubscribe()'. * Update minor formatting in CHANGELOG. * Store subscription name on 'Results' to unsubscribe correctly. * Add more tests to 'unsubscribe()'. * Update minor formatting in test. * Add comment regarding 'this.timeout()' in test. * Mark 'subscribe()' as experimental. * Add clarification to test. * Change ordering of condition checks. * Update CHANGELOG.md Co-authored-by: Kræn Hansen <kraen.hansen@mongodb.com> --------- Co-authored-by: Kræn Hansen <kraen.hansen@mongodb.com>
commit sha ca790bedfb5d6d1ecd1f557ee951c0fa5175b142
Fix User.callFunction JSDoc to match the v11+ API (#5768)
commit sha a755aeb70d681c375019685203294cdb0c9e381d
Merge branch 'main' into fp/update-core-13.13 * main: Fix User.callFunction JSDoc to match the v11+ API (#5768) Add Flexible Sync subscribe/unsubscribe APIs (#5772) Fix warning for deprecated namespace setting method in Android (#5862) Update install-test-react-native.yml (#5848) Update package-unit-tests.yml to add ccache and ninja (#5837) Enable cleartext traffic in android test app to make tests work in release builds # Conflicts: # CHANGELOG.md
push time in 38 minutes
push eventrealm/realm-core
commit sha fa33cc9d4d56e5a27fc0e9483a4aa6f281d0a76e
Fixed name
push time in 42 minutes
push eventrealm/realm-core
commit sha 6a7ce4c0dcd56cef7cfb93a1ee7ee68db4c30d71
Update core 13.13 in bindgen branch (#6683) Co-authored-by: Christian Melchior <christian.melchior@mongodb.com> Co-authored-by: James Stone <james.stone@mongodb.com> Co-authored-by: realm-ci <ci@realm.io> Co-authored-by: Kirill Burtsev <kirill.burtsev@mongodb.com> Co-authored-by: Daniel Tabacaru <96778637+danieltabacaru@users.noreply.github.com> Co-authored-by: Thomas Goyne <tg@realm.io> Co-authored-by: Thomas Goyne <thomas.goyne@mongodb.com> Co-authored-by: Jørgen Edelbo <jorgen.edelbo@mongodb.com> Co-authored-by: Michael Wilkerson-Barker <michael.wilkersonbarker@mongodb.com> Co-authored-by: Nicola Cabiddu <nicola.cabiddu@mongodb.com> fix entries that went to the wrong change version (#6632) fix a race in a test (#6651) Fix a lock order inversion in tests (#6666) Fix an assertion failure if an async write callback ran during a write transaction (#6661)
commit sha 6d0d6e92e51d558b31392148ca1506d3004a306b
Merge branch 'bindgen' into fp/geospatial * bindgen: Update core 13.13 in bindgen branch (#6683)
push time in an hour
push eventrealm/realm-js
commit sha e3be0ce15a0c010714e5b123feb2ac956f27668e
Imrpoved queries
push time in an hour
PR opened realm/realm-kotlin
Fixes part of #1401 (still investigating the persisted name issue)
pr created time in an hour
push eventrealm/realm-kotlin
commit sha 55a2ce85a623fbe3773b63f607ed4a4dc528b346
Cleanup
push time in an hour
push eventrealm/realm-js
commit sha bc600da7caa016ea4005f8883b4b4e7a1d7abdc5
Share the auth operation state between both useAuth and useEmailPasswordAuth
push time in an hour
push eventrealm/realm-core
commit sha 6a7ce4c0dcd56cef7cfb93a1ee7ee68db4c30d71
Update core 13.13 in bindgen branch (#6683) Co-authored-by: Christian Melchior <christian.melchior@mongodb.com> Co-authored-by: James Stone <james.stone@mongodb.com> Co-authored-by: realm-ci <ci@realm.io> Co-authored-by: Kirill Burtsev <kirill.burtsev@mongodb.com> Co-authored-by: Daniel Tabacaru <96778637+danieltabacaru@users.noreply.github.com> Co-authored-by: Thomas Goyne <tg@realm.io> Co-authored-by: Thomas Goyne <thomas.goyne@mongodb.com> Co-authored-by: Jørgen Edelbo <jorgen.edelbo@mongodb.com> Co-authored-by: Michael Wilkerson-Barker <michael.wilkersonbarker@mongodb.com> Co-authored-by: Nicola Cabiddu <nicola.cabiddu@mongodb.com> fix entries that went to the wrong change version (#6632) fix a race in a test (#6651) Fix a lock order inversion in tests (#6666) Fix an assertion failure if an async write callback ran during a write transaction (#6661)
push time in an hour
push eventrealm/realm-kotlin
commit sha 0fda36d00ad4a27a8ac0eebfa67104dbe72a9336
Update changelog
push time in an hour
push eventrealm/realm-js
commit sha b6430da17e69b548e29f9103b2a0773f28e714da
Share the auth operation state between both useAuth and useEmailPasswordAuth
push time in an hour
create barnchrealm/realm-kotlin
branch : cm/fix-async-server-error-crash
created branch time in an hour
Pull request review commentrealm/realm-kotlin
+package io.realm.kotlin.test.mongodb.shared++import io.realm.kotlin.Realm+import io.realm.kotlin.entities.sync.flx.FlexChildObject+import io.realm.kotlin.entities.sync.flx.FlexEmbeddedObject+import io.realm.kotlin.entities.sync.flx.FlexParentObject+import io.realm.kotlin.ext.query+import io.realm.kotlin.internal.platform.runBlocking+import io.realm.kotlin.mongodb.ext.subscribe+import io.realm.kotlin.mongodb.subscriptions+import io.realm.kotlin.mongodb.sync.Subscription+import io.realm.kotlin.mongodb.sync.SubscriptionSetState+import io.realm.kotlin.mongodb.sync.SyncConfiguration+import io.realm.kotlin.mongodb.sync.WaitForSync+import io.realm.kotlin.mongodb.syncSession+import io.realm.kotlin.query.RealmResults+import io.realm.kotlin.test.mongodb.TEST_APP_FLEX+import io.realm.kotlin.test.mongodb.TestApp+import io.realm.kotlin.test.mongodb.createUserAndLogIn+import io.realm.kotlin.test.util.TestHelper+import io.realm.kotlin.test.util.use+import kotlinx.coroutines.TimeoutCancellationException+import kotlin.random.Random+import kotlin.test.AfterTest+import kotlin.test.BeforeTest+import kotlin.test.Test+import kotlin.test.assertEquals+import kotlin.test.assertFailsWith+import kotlin.test.assertNotEquals+import kotlin.test.assertNull+import kotlin.time.Duration.Companion.nanoseconds+import kotlin.time.Duration.Companion.seconds++/**+ * Class for testing the various extension methods we have for bridging the gap between Subscriptions+ * and RealmQuery/RealmResults.+ */+class SubscriptionExtensionsTests {++ private lateinit var app: TestApp+ private lateinit var realm: Realm++ @BeforeTest+ fun setup() {+ app = TestApp(appName = TEST_APP_FLEX)+ val (email, password) = TestHelper.randomEmail() to "password1234"+ val user = runBlocking {+ app.createUserAndLogIn(email, password)+ }+ val config = SyncConfiguration.Builder(+ user,+ schema = setOf(FlexParentObject::class, FlexChildObject::class, FlexEmbeddedObject::class)+ )+ .build()+ realm = Realm.open(config)+ }++ @AfterTest+ fun tearDown() {+ if (this::realm.isInitialized && !realm.isClosed()) {+ realm.close()+ }+ if (this::app.isInitialized) {+ app.close()+ }+ }++ @Test+ fun realmQuery_subscribe_anonymous() = runBlocking {+ val subs = realm.subscriptions+ assertEquals(0, subs.size)+ val results: RealmResults<FlexParentObject> = realm.query<FlexParentObject>().subscribe()+ assertEquals(SubscriptionSetState.COMPLETE, subs.state)+ assertEquals(1, subs.size)+ val sub: Subscription = subs.first()+ assertNull(sub.name)+ assertEquals("TRUEPREDICATE ", sub.queryDescription)+ assertEquals(FlexParentObject::class.simpleName, sub.objectType)+ }++ // Check that subscribing twice to a query will result in the same subscription+ @Test+ fun realmQuery_subscribe_anonymousTwice() = runBlocking {+ val subs = realm.subscriptions+ assertEquals(0, subs.size)+ realm.query<FlexParentObject>().subscribe()+ realm.query<FlexParentObject>().subscribe()+ assertEquals(SubscriptionSetState.COMPLETE, subs.state)+ assertEquals(1, subs.size)+ val sub: Subscription = subs.first()+ assertNull(sub.name)+ assertEquals("TRUEPREDICATE ", sub.queryDescription)+ assertEquals(FlexParentObject::class.simpleName, sub.objectType)+ }++ // Check that anonymous RealmQuery and RealmResults .subscribe calls result in the same sub.+ @Test+ fun anonymousSubscriptionsOverlap() = runBlocking {+ val subs = realm.subscriptions+ assertEquals(0, subs.size)+ realm.query<FlexParentObject>().subscribe()+ realm.query<FlexParentObject>().find().subscribe()+ assertEquals(SubscriptionSetState.COMPLETE, subs.state)+ assertEquals(1, subs.size)+ val sub: Subscription = subs.first()+ assertNull(sub.name)+ assertEquals("TRUEPREDICATE ", sub.queryDescription)+ assertEquals(FlexParentObject::class.simpleName, sub.objectType)+ }++ // Verify that the realm query doesn't run against a frozen version previous to the Realm
// Verify that the realm query doesn't run against a frozen version prior to the Realm
comment created time in an hour
Pull request review commentrealm/realm-kotlin
+@file:Suppress("invisible_reference", "invisible_member")+package io.realm.kotlin.mongodb.ext++import io.realm.kotlin.mongodb.annotations.ExperimentalFlexibleSyncApi+import io.realm.kotlin.mongodb.sync.Subscription+import io.realm.kotlin.mongodb.sync.SubscriptionSet+import io.realm.kotlin.mongodb.sync.WaitForSync+import io.realm.kotlin.query.RealmQuery+import io.realm.kotlin.query.RealmResults+import io.realm.kotlin.types.RealmObject+import kotlin.time.Duration++/**+ * Automatically create an anonymous [Subscription] from a local query result in the background and+ * return the result of re-running the same query against the Realm file.+ *+ * This is a more streamlined alternative to doing something like this:+ *+ * ```+ * fun suspend getData(realm: Realm): RealmResults<Person> {+ * val results = realm.query<Person>().find()+ * realm.subscriptions.update { bgRealm ->+ * add("myquery", results.query(""))+ * }+ * realm.subscriptions.waitForSynchronization()+ * return realm.query<Person>().find()+ * }+ * ```+ *+ * It is possible to define whether or not to wait for the server to send all data before+ * running the local query. This is relevant as there might be delay from creating a subscription+ * to the data being available on the device due to either latency or because a large dataset needs+ * be downloaded.+ *+ * The default behaviour is that the first time `subscribe` is called, the query result will not+ * be returned until data has been downloaded from the server. On subsequent calls to `subscribe`+ * for the same query, the query will run immediately on the local database while any updates+ * are downloaded in the background.+ *+ * @param name name of the subscription. This can be used to identify it later in the [SubscriptionSet].+ * @param mode type of mode used to resolve the subscription. See [WaitForSync] for more details.+ * @param timeout How long to wait for the server to return the objects defined by the subscription.+ * This is only relevant for [WaitForSync.ALWAYS] and [WaitForSync.FIRST_TIME].+ * @return The result of running the query against the local Realm file. The results returned will+ * depend on which [mode] was used.+ * @throws kotlinx.coroutines.TimeoutCancellationException if the specified timeout was hit before+ * a query result could be returned.+ * @Throws IllegalStateException if this method is called on a Realm that isn't using Flexible Sync.+ */@ExperimentalFlexibleSyncApi+public suspend fun <T : RealmObject> RealmResults<T>.subscribe(+ mode: WaitForSync = WaitForSync.FIRST_TIME,+ timeout: Duration = Duration.INFINITE+): RealmResults<T> {+ val query: RealmQuery<T> = this.query("")+ return query.subscribe(mode, timeout)+}++/**+ * Automatically create a named [Subscription] from a local query result in the background and+ * return the result of re-running the same query against the Realm file.+ *+ * This is a more streamlined alternative to doing something like this:+ *+ * ```+ * fun suspend getData(realm: Realm): RealmResults<Person> {+ * val results = realm.query<Person>().find()+ * realm.subscriptions.update { bgRealm ->+ * add("myquery", results.query(""))+ * }+ * realm.subscriptions.waitForSynchronization()+ * return realm.query<Person>().find()+ * }+ * ```+ *+ * It is possible to define whether or not to wait for the server to send all data before+ * running the local query. This is relevant as there might be delay from creating a subscription+ * to the data being available on the device due to either latency or because a large dataset needs+ * be downloaded.+ *+ * The default behaviour is that the first time `subscribe` is called, the query result will not+ * be returned until data has been downloaded from the server. On subsequent calls to `subscribe`+ * for the same query, the query will run immediately on the local database while any updates+ * are downloaded in the background.+ *+ * @param name name of the subscription. This can be used to identify it later in the [SubscriptionSet].+ * @param mode type of mode used to resolve the subscription. See [WaitForSync] for more details.+ * @param timeout How long to wait for the server to return the objects defined by the subscription.+ * This is only relevant for [WaitForSync.ALWAYS] and [WaitForSync.FIRST_TIME].+ * @return The result of running the query against the local Realm file. The results returned will+ * depend on which [mode] was used.+ * @throws kotlinx.coroutines.TimeoutCancellationException if the specified timeout was hit before+ * a query result could be returned.+ * @Throws IllegalStateException if this method is called on a Realm that isn't using Flexible Sync.+ */+@ExperimentalFlexibleSyncApi+public suspend fun <T : RealmObject> RealmResults<T>.subscribe(+ name: String,
Would it make sense to collapse the anonymous and named variant by allowing name: String? = null
to reduce the API surface 🤔
comment created time in an hour
Pull request review commentrealm/realm-kotlin
+@file:Suppress("invisible_reference", "invisible_member")+package io.realm.kotlin.mongodb.ext++import io.realm.kotlin.mongodb.annotations.ExperimentalFlexibleSyncApi+import io.realm.kotlin.mongodb.sync.Subscription+import io.realm.kotlin.mongodb.sync.SubscriptionSet+import io.realm.kotlin.mongodb.sync.WaitForSync+import io.realm.kotlin.query.RealmQuery+import io.realm.kotlin.query.RealmResults+import io.realm.kotlin.types.RealmObject+import kotlin.time.Duration++/**+ * Automatically create an anonymous [Subscription] from a local query result in the background and+ * return the result of re-running the same query against the Realm file.+ *+ * This is a more streamlined alternative to doing something like this:+ *+ * ```+ * fun suspend getData(realm: Realm): RealmResults<Person> {+ * val results = realm.query<Person>().find()+ * realm.subscriptions.update { bgRealm ->+ * add("myquery", results.query(""))+ * }+ * realm.subscriptions.waitForSynchronization()+ * return realm.query<Person>().find()+ * }+ * ```+ *+ * It is possible to define whether or not to wait for the server to send all data before+ * running the local query. This is relevant as there might be delay from creating a subscription+ * to the data being available on the device due to either latency or because a large dataset needs+ * be downloaded.+ *+ * The default behaviour is that the first time `subscribe` is called, the query result will not+ * be returned until data has been downloaded from the server. On subsequent calls to `subscribe`+ * for the same query, the query will run immediately on the local database while any updates+ * are downloaded in the background.+ *+ * @param name name of the subscription. This can be used to identify it later in the [SubscriptionSet].+ * @param mode type of mode used to resolve the subscription. See [WaitForSync] for more details.
* @param mode mode used to resolve the subscription. See [WaitForSync] for more details.
comment created time in an hour
Pull request review commentrealm/realm-kotlin
+package io.realm.kotlin.test.mongodb.shared++import io.realm.kotlin.Realm+import io.realm.kotlin.entities.sync.flx.FlexChildObject+import io.realm.kotlin.entities.sync.flx.FlexEmbeddedObject+import io.realm.kotlin.entities.sync.flx.FlexParentObject+import io.realm.kotlin.ext.query+import io.realm.kotlin.internal.platform.runBlocking+import io.realm.kotlin.mongodb.ext.subscribe+import io.realm.kotlin.mongodb.subscriptions+import io.realm.kotlin.mongodb.sync.Subscription+import io.realm.kotlin.mongodb.sync.SubscriptionSetState+import io.realm.kotlin.mongodb.sync.SyncConfiguration+import io.realm.kotlin.mongodb.sync.WaitForSync+import io.realm.kotlin.mongodb.syncSession+import io.realm.kotlin.query.RealmResults+import io.realm.kotlin.test.mongodb.TEST_APP_FLEX+import io.realm.kotlin.test.mongodb.TestApp+import io.realm.kotlin.test.mongodb.createUserAndLogIn+import io.realm.kotlin.test.util.TestHelper+import io.realm.kotlin.test.util.use+import kotlinx.coroutines.TimeoutCancellationException+import kotlin.random.Random+import kotlin.test.AfterTest+import kotlin.test.BeforeTest+import kotlin.test.Test+import kotlin.test.assertEquals+import kotlin.test.assertFailsWith+import kotlin.test.assertNotEquals+import kotlin.test.assertNull+import kotlin.time.Duration.Companion.nanoseconds+import kotlin.time.Duration.Companion.seconds++/**+ * Class for testing the various extension methods we have for bridging the gap between Subscriptions+ * and RealmQuery/RealmResults.+ */+class SubscriptionExtensionsTests {++ private lateinit var app: TestApp+ private lateinit var realm: Realm++ @BeforeTest+ fun setup() {+ app = TestApp(appName = TEST_APP_FLEX)+ val (email, password) = TestHelper.randomEmail() to "password1234"+ val user = runBlocking {+ app.createUserAndLogIn(email, password)+ }+ val config = SyncConfiguration.Builder(+ user,+ schema = setOf(FlexParentObject::class, FlexChildObject::class, FlexEmbeddedObject::class)+ )+ .build()+ realm = Realm.open(config)+ }++ @AfterTest+ fun tearDown() {+ if (this::realm.isInitialized && !realm.isClosed()) {+ realm.close()+ }+ if (this::app.isInitialized) {+ app.close()+ }+ }++ @Test+ fun realmQuery_subscribe_anonymous() = runBlocking {+ val subs = realm.subscriptions+ assertEquals(0, subs.size)+ val results: RealmResults<FlexParentObject> = realm.query<FlexParentObject>().subscribe()+ assertEquals(SubscriptionSetState.COMPLETE, subs.state)+ assertEquals(1, subs.size)+ val sub: Subscription = subs.first()+ assertNull(sub.name)+ assertEquals("TRUEPREDICATE ", sub.queryDescription)+ assertEquals(FlexParentObject::class.simpleName, sub.objectType)+ }++ // Check that subscribing twice to a query will result in the same subscription+ @Test+ fun realmQuery_subscribe_anonymousTwice() = runBlocking {+ val subs = realm.subscriptions+ assertEquals(0, subs.size)+ realm.query<FlexParentObject>().subscribe()+ realm.query<FlexParentObject>().subscribe()+ assertEquals(SubscriptionSetState.COMPLETE, subs.state)+ assertEquals(1, subs.size)+ val sub: Subscription = subs.first()+ assertNull(sub.name)+ assertEquals("TRUEPREDICATE ", sub.queryDescription)+ assertEquals(FlexParentObject::class.simpleName, sub.objectType)+ }++ // Check that anonymous RealmQuery and RealmResults .subscribe calls result in the same sub.
// Check that anonymous RealmQuery and RealmResults `subscribe` calls result in the same sub.
comment created time in an hour
Pull request review commentrealm/realm-kotlin
+package io.realm.kotlin.test.mongodb.shared++import io.realm.kotlin.Realm+import io.realm.kotlin.entities.sync.flx.FlexChildObject+import io.realm.kotlin.entities.sync.flx.FlexEmbeddedObject+import io.realm.kotlin.entities.sync.flx.FlexParentObject+import io.realm.kotlin.ext.query+import io.realm.kotlin.internal.platform.runBlocking+import io.realm.kotlin.mongodb.ext.subscribe+import io.realm.kotlin.mongodb.subscriptions+import io.realm.kotlin.mongodb.sync.Subscription+import io.realm.kotlin.mongodb.sync.SubscriptionSetState+import io.realm.kotlin.mongodb.sync.SyncConfiguration+import io.realm.kotlin.mongodb.sync.WaitForSync+import io.realm.kotlin.mongodb.syncSession+import io.realm.kotlin.query.RealmResults+import io.realm.kotlin.test.mongodb.TEST_APP_FLEX+import io.realm.kotlin.test.mongodb.TestApp+import io.realm.kotlin.test.mongodb.createUserAndLogIn+import io.realm.kotlin.test.util.TestHelper+import io.realm.kotlin.test.util.use+import kotlinx.coroutines.TimeoutCancellationException+import kotlin.random.Random+import kotlin.test.AfterTest+import kotlin.test.BeforeTest+import kotlin.test.Test+import kotlin.test.assertEquals+import kotlin.test.assertFailsWith+import kotlin.test.assertNotEquals+import kotlin.test.assertNull+import kotlin.time.Duration.Companion.nanoseconds+import kotlin.time.Duration.Companion.seconds++/**+ * Class for testing the various extension methods we have for bridging the gap between Subscriptions+ * and RealmQuery/RealmResults.+ */+class SubscriptionExtensionsTests {++ private lateinit var app: TestApp+ private lateinit var realm: Realm++ @BeforeTest+ fun setup() {+ app = TestApp(appName = TEST_APP_FLEX)+ val (email, password) = TestHelper.randomEmail() to "password1234"+ val user = runBlocking {+ app.createUserAndLogIn(email, password)+ }+ val config = SyncConfiguration.Builder(+ user,+ schema = setOf(FlexParentObject::class, FlexChildObject::class, FlexEmbeddedObject::class)+ )+ .build()+ realm = Realm.open(config)+ }++ @AfterTest+ fun tearDown() {+ if (this::realm.isInitialized && !realm.isClosed()) {+ realm.close()+ }+ if (this::app.isInitialized) {+ app.close()+ }+ }++ @Test+ fun realmQuery_subscribe_anonymous() = runBlocking {+ val subs = realm.subscriptions+ assertEquals(0, subs.size)+ val results: RealmResults<FlexParentObject> = realm.query<FlexParentObject>().subscribe()+ assertEquals(SubscriptionSetState.COMPLETE, subs.state)+ assertEquals(1, subs.size)+ val sub: Subscription = subs.first()+ assertNull(sub.name)+ assertEquals("TRUEPREDICATE ", sub.queryDescription)+ assertEquals(FlexParentObject::class.simpleName, sub.objectType)+ }++ // Check that subscribing twice to a query will result in the same subscription+ @Test+ fun realmQuery_subscribe_anonymousTwice() = runBlocking {+ val subs = realm.subscriptions+ assertEquals(0, subs.size)+ realm.query<FlexParentObject>().subscribe()+ realm.query<FlexParentObject>().subscribe()+ assertEquals(SubscriptionSetState.COMPLETE, subs.state)+ assertEquals(1, subs.size)+ val sub: Subscription = subs.first()+ assertNull(sub.name)+ assertEquals("TRUEPREDICATE ", sub.queryDescription)+ assertEquals(FlexParentObject::class.simpleName, sub.objectType)+ }++ // Check that anonymous RealmQuery and RealmResults .subscribe calls result in the same sub.+ @Test+ fun anonymousSubscriptionsOverlap() = runBlocking {+ val subs = realm.subscriptions+ assertEquals(0, subs.size)+ realm.query<FlexParentObject>().subscribe()+ realm.query<FlexParentObject>().find().subscribe()+ assertEquals(SubscriptionSetState.COMPLETE, subs.state)+ assertEquals(1, subs.size)+ val sub: Subscription = subs.first()+ assertNull(sub.name)+ assertEquals("TRUEPREDICATE ", sub.queryDescription)+ assertEquals(FlexParentObject::class.simpleName, sub.objectType)+ }++ // Verify that the realm query doesn't run against a frozen version previous to the Realm+ // being updated from `waitForSynchronization`.+ @Test+ fun realmQuery_subscribe_queryResultIsLatestVersion() = runBlocking {+ // Write data to a server Realm+ val section = Random.nextInt()+ val (email, password) = TestHelper.randomEmail() to "password1234"+ val user1 = app.createUserAndLogIn(email, password)+ val config = SyncConfiguration.Builder(+ user1,+ schema = setOf(FlexParentObject::class, FlexChildObject::class, FlexEmbeddedObject::class)+ ).initialSubscriptions { realm: Realm ->+ realm.query<FlexParentObject>("section = $0", section).subscribe()+ }.build()++ Realm.open(config).use { realmFromAnotherDevice ->+ realmFromAnotherDevice.writeBlocking {+ copyToRealm(FlexParentObject(section))+ }+ realmFromAnotherDevice.syncSession.uploadAllLocalChanges(30.seconds)+ }++ // Data still hasn't reached this device+ assertEquals(0, realm.query<FlexParentObject>().count().find())+ // Check that subscribing to a query, will run the query on the data downloaded from+ // the server and not just local data, due to WaitForSync.FIRST_TIME being the default.+ val result = realm.query<FlexParentObject>("section = $0", section).subscribe()+ assertEquals(1, result.size)+ assertEquals(section, result.first().section)+ }++ @Test+ fun realmQuery_subscribe_waitFirstTime() = runBlocking<Unit> {+ // Unnamed+ realm.query<FlexParentObject>().subscribe() // Default value is WaitForSync.FIRST_TIME+ var updatedSubs = realm.subscriptions+ assertEquals(1, updatedSubs.size)+ assertEquals(SubscriptionSetState.COMPLETE, updatedSubs.state)+ var sub: Subscription = updatedSubs.first()+ assertNull(sub.name)+ assertEquals("TRUEPREDICATE ", sub.queryDescription)+ assertEquals("FlexParentObject", sub.objectType)++ // Named+ realm.query<FlexParentObject>().subscribe("my-name") // Default value is WaitForSync.FIRST_TIME+ updatedSubs = realm.subscriptions+ assertEquals(2, updatedSubs.size)+ assertEquals(SubscriptionSetState.COMPLETE, updatedSubs.state)+ sub = updatedSubs.last()+ assertEquals("my-name", sub.name)+ assertEquals("TRUEPREDICATE ", sub.queryDescription)+ assertEquals("FlexParentObject", sub.objectType)+ }++ @Test+ fun realmQuery_subscribe_waitNever() = runBlocking {+ // Un-named+ realm.query<FlexParentObject>().subscribe(mode = WaitForSync.NEVER)+ var updatedSubs = realm.subscriptions+ assertEquals(1, updatedSubs.size)+ // Updating the subscription will happen in the background, but+ // hopefully hasn't reached COMPLETE yet.+ assertNotEquals(SubscriptionSetState.COMPLETE, updatedSubs.state)++ // Named+ realm.query<FlexParentObject>().subscribe(name = "my-name", mode = WaitForSync.NEVER)+ updatedSubs = realm.subscriptions+ assertEquals(2, updatedSubs.size)+ // Updating the subscription will happen in the background, but+ // hopefully hasn't reached COMPLETE yet.+ assertNotEquals(SubscriptionSetState.COMPLETE, updatedSubs.state)+ }++ @Test+ fun realmQuery_subscribe_waitAlways() = runBlocking {
Wouldn't it be safer to use the pattern from realmQuery_subscribe_queryResultIsLatestVersion
to verify that you always get a result reflecting writes from another "device"?
comment created time in an hour
Pull request review commentrealm/realm-kotlin
+@file:Suppress("invisible_reference", "invisible_member")+package io.realm.kotlin.mongodb.ext++import io.realm.kotlin.Realm+import io.realm.kotlin.internal.RealmImpl+import io.realm.kotlin.internal.getRealm+import io.realm.kotlin.mongodb.annotations.ExperimentalFlexibleSyncApi+import io.realm.kotlin.mongodb.internal.AppImpl+import io.realm.kotlin.mongodb.subscriptions+import io.realm.kotlin.mongodb.sync.Subscription+import io.realm.kotlin.mongodb.sync.SubscriptionSet+import io.realm.kotlin.mongodb.sync.SyncConfiguration+import io.realm.kotlin.mongodb.sync.WaitForSync+import io.realm.kotlin.mongodb.syncSession+import io.realm.kotlin.query.RealmQuery+import io.realm.kotlin.query.RealmResults+import io.realm.kotlin.types.RealmObject+import kotlinx.coroutines.CoroutineDispatcher+import kotlinx.coroutines.withContext+import kotlinx.coroutines.withTimeout+import kotlin.time.Duration++/**+ * Automatically create an anonymous [Subscription] from a query in the background and return the+ * result of running the same query against the local Realm file.+ *+ * This is a more streamlined alternative to doing something like this:+ *+ * ```+ * fun suspend getData(realm: Realm): RealmResults<Person> {+ * realm.subscriptions.update { bgRealm ->+ * add(bgRealm.query<Person>())+ * }+ * realm.subscriptions.waitForSynchronization()+ * return realm.query<Person>().find()+ * }+ * ```+ *+ * It is possible to define whether or not to wait for the server to send all data before+ * running the local query. This is relevant as there might be delay from creating a subscription+ * to the data being available on the device due to either latency or because a large dataset needs+ * be downloaded.+ *+ * The default behaviour is that the first time `subscribe` is called, the query result will not+ * be returned until data has been downloaded from the server. On subsequent calls to `subscribe`+ * for the same query, the query will run immediately on the local database while any updates+ * are downloaded in the background.+ *+ * @param mode type of mode used to resolve the subscription. See [WaitForSync] for more details.+ * @param timeout How long to wait for the server to return the objects defined by the subscription.+ * This is only relevant for [WaitForSync.ALWAYS] and [WaitForSync.FIRST_TIME].+ * @return The result of running the query against the local Realm file. The results returned will+ * depend on which [mode] was used.+ * @throws kotlinx.coroutines.TimeoutCancellationException if the specified timeout was hit before+ * a query result could be returned.+ * @Throws IllegalStateException if this method is called on a Realm that isn't using Flexible Sync.+ */+@ExperimentalFlexibleSyncApi+public suspend fun <T : RealmObject> RealmQuery<T>.subscribe(+ mode: WaitForSync = WaitForSync.FIRST_TIME,+ timeout: Duration = Duration.INFINITE+): RealmResults<T> {+ return createSubscriptionFromQuery(this, null, false, mode, timeout)+}++/**+ * Automatically create a named [Subscription] from a query in the background and return the+ * result of running the same query against the local Realm file.+ *+ * This is a more streamlined alternative to doing something like this:+ *+ * ```+ * fun suspend getData(realm: Realm): RealmResults<Person> {+ * realm.subscriptions.update { bgRealm ->+ * add("myquery", bgRealm.query<Person>())+ * }+ * realm.subscriptions.waitForSynchronization()+ * return realm.query<Person>().find()+ * }+ * ```+ *+ * It is possible to define whether or not to wait for the server to send all data before+ * running the local query. This is relevant as there might be delay from creating a subscription+ * to the data being available on the device due to either latency or because a large dataset needs+ * be downloaded.+ *+ * The default behaviour is that the first time `subscribe` is called, the query result will not+ * be returned until data has been downloaded from the server. On subsequent calls to `subscribe`+ * for the same query, the query will run immediately on the local database while any updates+ * are downloaded in the background.+ *+ * @param name name of the subscription. This can be used to identify it later in the [SubscriptionSet].+ * @param mode type of mode used to resolve the subscription. See [WaitForSync] for more details.+ * @param timeout How long to wait for the server to return the objects defined by the subscription.+ * This is only relevant for [WaitForSync.ALWAYS] and [WaitForSync.FIRST_TIME].+ * @return The result of running the query against the local Realm file. The results returned will+ * depend on which [mode] was used.+ * @throws kotlinx.coroutines.TimeoutCancellationException if the specified timeout was hit before+ * a query result could be returned.+ * @Throws IllegalStateException if this method is called on a Realm that isn't using Flexible Sync.+ */+@ExperimentalFlexibleSyncApi+public suspend fun <T : RealmObject> RealmQuery<T>.subscribe(+ name: String,+ updateExisting: Boolean = false,+ mode: WaitForSync = WaitForSync.FIRST_TIME,+ timeout: Duration = Duration.INFINITE+): RealmResults<T> {+ return createSubscriptionFromQuery(this, name, updateExisting, mode, timeout)+}++private suspend fun <T : RealmObject> createSubscriptionFromQuery(+ query: RealmQuery<T>,+ name: String?,+ updateExisting: Boolean = false,+ mode: WaitForSync,+ timeout: Duration+): RealmResults<T> {++ if (query !is io.realm.kotlin.internal.query.ObjectQuery<T>) {+ throw IllegalStateException("Only queries on objects are supported. This was: ${query::class}")+ }+ if (query.realmReference.owner !is RealmImpl) {+ throw IllegalStateException("Calling `subscribe()` inside a write transaction is not allowed.")+ }+ val realm: Realm = query.getRealm()+ val subscriptions = realm.subscriptions+ val appDispatcher: CoroutineDispatcher = ((realm.configuration as SyncConfiguration).user.app as AppImpl).appNetworkDispatcher.dispatcher
I guess we should like the above if called on a non-SyncConfiguration
?
comment created time in 2 hours
Pull request review commentrealm/realm-kotlin
+package io.realm.kotlin.test.mongodb.shared++import io.realm.kotlin.Realm+import io.realm.kotlin.entities.sync.flx.FlexChildObject+import io.realm.kotlin.entities.sync.flx.FlexEmbeddedObject+import io.realm.kotlin.entities.sync.flx.FlexParentObject+import io.realm.kotlin.ext.query+import io.realm.kotlin.internal.platform.runBlocking+import io.realm.kotlin.mongodb.ext.subscribe+import io.realm.kotlin.mongodb.subscriptions+import io.realm.kotlin.mongodb.sync.Subscription+import io.realm.kotlin.mongodb.sync.SubscriptionSetState+import io.realm.kotlin.mongodb.sync.SyncConfiguration+import io.realm.kotlin.mongodb.sync.WaitForSync+import io.realm.kotlin.mongodb.syncSession+import io.realm.kotlin.query.RealmResults+import io.realm.kotlin.test.mongodb.TEST_APP_FLEX+import io.realm.kotlin.test.mongodb.TestApp+import io.realm.kotlin.test.mongodb.createUserAndLogIn+import io.realm.kotlin.test.util.TestHelper+import io.realm.kotlin.test.util.use+import kotlinx.coroutines.TimeoutCancellationException+import kotlin.random.Random+import kotlin.test.AfterTest+import kotlin.test.BeforeTest+import kotlin.test.Test+import kotlin.test.assertEquals+import kotlin.test.assertFailsWith+import kotlin.test.assertNotEquals+import kotlin.test.assertNull+import kotlin.time.Duration.Companion.nanoseconds+import kotlin.time.Duration.Companion.seconds++/**+ * Class for testing the various extension methods we have for bridging the gap between Subscriptions+ * and RealmQuery/RealmResults.+ */+class SubscriptionExtensionsTests {++ private lateinit var app: TestApp+ private lateinit var realm: Realm++ @BeforeTest+ fun setup() {+ app = TestApp(appName = TEST_APP_FLEX)+ val (email, password) = TestHelper.randomEmail() to "password1234"+ val user = runBlocking {+ app.createUserAndLogIn(email, password)+ }+ val config = SyncConfiguration.Builder(+ user,+ schema = setOf(FlexParentObject::class, FlexChildObject::class, FlexEmbeddedObject::class)+ )+ .build()+ realm = Realm.open(config)+ }++ @AfterTest+ fun tearDown() {+ if (this::realm.isInitialized && !realm.isClosed()) {+ realm.close()+ }+ if (this::app.isInitialized) {+ app.close()+ }+ }++ @Test+ fun realmQuery_subscribe_anonymous() = runBlocking {+ val subs = realm.subscriptions+ assertEquals(0, subs.size)+ val results: RealmResults<FlexParentObject> = realm.query<FlexParentObject>().subscribe()+ assertEquals(SubscriptionSetState.COMPLETE, subs.state)+ assertEquals(1, subs.size)+ val sub: Subscription = subs.first()+ assertNull(sub.name)+ assertEquals("TRUEPREDICATE ", sub.queryDescription)+ assertEquals(FlexParentObject::class.simpleName, sub.objectType)+ }++ // Check that subscribing twice to a query will result in the same subscription+ @Test+ fun realmQuery_subscribe_anonymousTwice() = runBlocking {+ val subs = realm.subscriptions+ assertEquals(0, subs.size)+ realm.query<FlexParentObject>().subscribe()+ realm.query<FlexParentObject>().subscribe()+ assertEquals(SubscriptionSetState.COMPLETE, subs.state)+ assertEquals(1, subs.size)+ val sub: Subscription = subs.first()+ assertNull(sub.name)+ assertEquals("TRUEPREDICATE ", sub.queryDescription)+ assertEquals(FlexParentObject::class.simpleName, sub.objectType)+ }++ // Check that anonymous RealmQuery and RealmResults .subscribe calls result in the same sub.+ @Test+ fun anonymousSubscriptionsOverlap() = runBlocking {+ val subs = realm.subscriptions+ assertEquals(0, subs.size)+ realm.query<FlexParentObject>().subscribe()+ realm.query<FlexParentObject>().find().subscribe()+ assertEquals(SubscriptionSetState.COMPLETE, subs.state)+ assertEquals(1, subs.size)+ val sub: Subscription = subs.first()+ assertNull(sub.name)+ assertEquals("TRUEPREDICATE ", sub.queryDescription)+ assertEquals(FlexParentObject::class.simpleName, sub.objectType)+ }++ // Verify that the realm query doesn't run against a frozen version previous to the Realm+ // being updated from `waitForSynchronization`.+ @Test+ fun realmQuery_subscribe_queryResultIsLatestVersion() = runBlocking {+ // Write data to a server Realm+ val section = Random.nextInt()+ val (email, password) = TestHelper.randomEmail() to "password1234"+ val user1 = app.createUserAndLogIn(email, password)+ val config = SyncConfiguration.Builder(+ user1,+ schema = setOf(FlexParentObject::class, FlexChildObject::class, FlexEmbeddedObject::class)+ ).initialSubscriptions { realm: Realm ->+ realm.query<FlexParentObject>("section = $0", section).subscribe()+ }.build()++ Realm.open(config).use { realmFromAnotherDevice ->+ realmFromAnotherDevice.writeBlocking {+ copyToRealm(FlexParentObject(section))+ }+ realmFromAnotherDevice.syncSession.uploadAllLocalChanges(30.seconds)+ }++ // Data still hasn't reached this device+ assertEquals(0, realm.query<FlexParentObject>().count().find())+ // Check that subscribing to a query, will run the query on the data downloaded from+ // the server and not just local data, due to WaitForSync.FIRST_TIME being the default.+ val result = realm.query<FlexParentObject>("section = $0", section).subscribe()+ assertEquals(1, result.size)+ assertEquals(section, result.first().section)+ }++ @Test+ fun realmQuery_subscribe_waitFirstTime() = runBlocking<Unit> {+ // Unnamed+ realm.query<FlexParentObject>().subscribe() // Default value is WaitForSync.FIRST_TIME+ var updatedSubs = realm.subscriptions+ assertEquals(1, updatedSubs.size)+ assertEquals(SubscriptionSetState.COMPLETE, updatedSubs.state)+ var sub: Subscription = updatedSubs.first()+ assertNull(sub.name)+ assertEquals("TRUEPREDICATE ", sub.queryDescription)+ assertEquals("FlexParentObject", sub.objectType)++ // Named+ realm.query<FlexParentObject>().subscribe("my-name") // Default value is WaitForSync.FIRST_TIME+ updatedSubs = realm.subscriptions+ assertEquals(2, updatedSubs.size)+ assertEquals(SubscriptionSetState.COMPLETE, updatedSubs.state)+ sub = updatedSubs.last()+ assertEquals("my-name", sub.name)+ assertEquals("TRUEPREDICATE ", sub.queryDescription)+ assertEquals("FlexParentObject", sub.objectType)+ }++ @Test+ fun realmQuery_subscribe_waitNever() = runBlocking {+ // Un-named+ realm.query<FlexParentObject>().subscribe(mode = WaitForSync.NEVER)+ var updatedSubs = realm.subscriptions+ assertEquals(1, updatedSubs.size)+ // Updating the subscription will happen in the background, but+ // hopefully hasn't reached COMPLETE yet.+ assertNotEquals(SubscriptionSetState.COMPLETE, updatedSubs.state)++ // Named+ realm.query<FlexParentObject>().subscribe(name = "my-name", mode = WaitForSync.NEVER)+ updatedSubs = realm.subscriptions+ assertEquals(2, updatedSubs.size)+ // Updating the subscription will happen in the background, but+ // hopefully hasn't reached COMPLETE yet.+ assertNotEquals(SubscriptionSetState.COMPLETE, updatedSubs.state)+ }++ @Test+ fun realmQuery_subscribe_waitAlways() = runBlocking {+ val sectionId = Random.nextInt()+ val results1 = realm.query<FlexParentObject>("section = $0", sectionId).subscribe() // Default value is WaitForSync.FIRST_TIME+ assertEquals(0, results1.size)+ uploadServerData(sectionId, 5)+ // Since the subscription is already present, we cannot control if the data is downloaded+ // before creating the next subscription. Instead we pause the syncSession and verify+ // that WaitForSync.ALWAYS timeout during network failures and resuming the session should+ // then work+ realm.syncSession.pause()+ assertFailsWith<TimeoutCancellationException> {+ realm.query<FlexParentObject>("section = $0", sectionId).subscribe(timeout = 3.seconds, mode = WaitForSync.ALWAYS)+ }+ realm.syncSession.resume()+ val results2 = realm.query<FlexParentObject>("section = $0", sectionId).subscribe(mode = WaitForSync.ALWAYS)+ assertEquals(5, results2.size)+ }++ @Test+ fun realmQuery_subscribe_timeOut_fails() = runBlocking<Unit> {+ assertFailsWith<TimeoutCancellationException> {+ realm.query<FlexParentObject>().subscribe(timeout = 1.nanoseconds)
Should we also test the named-case?
comment created time in an hour
Pull request review commentrealm/realm-kotlin
+package io.realm.kotlin.test.mongodb.shared++import io.realm.kotlin.Realm+import io.realm.kotlin.entities.sync.flx.FlexChildObject+import io.realm.kotlin.entities.sync.flx.FlexEmbeddedObject+import io.realm.kotlin.entities.sync.flx.FlexParentObject+import io.realm.kotlin.ext.query+import io.realm.kotlin.internal.platform.runBlocking+import io.realm.kotlin.mongodb.ext.subscribe+import io.realm.kotlin.mongodb.subscriptions+import io.realm.kotlin.mongodb.sync.Subscription+import io.realm.kotlin.mongodb.sync.SubscriptionSetState+import io.realm.kotlin.mongodb.sync.SyncConfiguration+import io.realm.kotlin.mongodb.sync.WaitForSync+import io.realm.kotlin.mongodb.syncSession+import io.realm.kotlin.query.RealmResults+import io.realm.kotlin.test.mongodb.TEST_APP_FLEX+import io.realm.kotlin.test.mongodb.TestApp+import io.realm.kotlin.test.mongodb.createUserAndLogIn+import io.realm.kotlin.test.util.TestHelper+import io.realm.kotlin.test.util.use+import kotlinx.coroutines.TimeoutCancellationException+import kotlin.random.Random+import kotlin.test.AfterTest+import kotlin.test.BeforeTest+import kotlin.test.Test+import kotlin.test.assertEquals+import kotlin.test.assertFailsWith+import kotlin.test.assertNotEquals+import kotlin.test.assertNull+import kotlin.time.Duration.Companion.nanoseconds+import kotlin.time.Duration.Companion.seconds++/**+ * Class for testing the various extension methods we have for bridging the gap between Subscriptions+ * and RealmQuery/RealmResults.+ */+class SubscriptionExtensionsTests {++ private lateinit var app: TestApp+ private lateinit var realm: Realm++ @BeforeTest+ fun setup() {+ app = TestApp(appName = TEST_APP_FLEX)+ val (email, password) = TestHelper.randomEmail() to "password1234"+ val user = runBlocking {+ app.createUserAndLogIn(email, password)+ }+ val config = SyncConfiguration.Builder(+ user,+ schema = setOf(FlexParentObject::class, FlexChildObject::class, FlexEmbeddedObject::class)+ )+ .build()+ realm = Realm.open(config)+ }++ @AfterTest+ fun tearDown() {+ if (this::realm.isInitialized && !realm.isClosed()) {+ realm.close()+ }+ if (this::app.isInitialized) {+ app.close()+ }+ }++ @Test+ fun realmQuery_subscribe_anonymous() = runBlocking {+ val subs = realm.subscriptions+ assertEquals(0, subs.size)+ val results: RealmResults<FlexParentObject> = realm.query<FlexParentObject>().subscribe()+ assertEquals(SubscriptionSetState.COMPLETE, subs.state)+ assertEquals(1, subs.size)+ val sub: Subscription = subs.first()+ assertNull(sub.name)+ assertEquals("TRUEPREDICATE ", sub.queryDescription)+ assertEquals(FlexParentObject::class.simpleName, sub.objectType)+ }++ // Check that subscribing twice to a query will result in the same subscription+ @Test+ fun realmQuery_subscribe_anonymousTwice() = runBlocking {+ val subs = realm.subscriptions+ assertEquals(0, subs.size)+ realm.query<FlexParentObject>().subscribe()+ realm.query<FlexParentObject>().subscribe()+ assertEquals(SubscriptionSetState.COMPLETE, subs.state)+ assertEquals(1, subs.size)+ val sub: Subscription = subs.first()+ assertNull(sub.name)+ assertEquals("TRUEPREDICATE ", sub.queryDescription)+ assertEquals(FlexParentObject::class.simpleName, sub.objectType)+ }++ // Check that anonymous RealmQuery and RealmResults .subscribe calls result in the same sub.+ @Test+ fun anonymousSubscriptionsOverlap() = runBlocking {+ val subs = realm.subscriptions+ assertEquals(0, subs.size)+ realm.query<FlexParentObject>().subscribe()+ realm.query<FlexParentObject>().find().subscribe()+ assertEquals(SubscriptionSetState.COMPLETE, subs.state)+ assertEquals(1, subs.size)+ val sub: Subscription = subs.first()+ assertNull(sub.name)+ assertEquals("TRUEPREDICATE ", sub.queryDescription)+ assertEquals(FlexParentObject::class.simpleName, sub.objectType)+ }++ // Verify that the realm query doesn't run against a frozen version previous to the Realm+ // being updated from `waitForSynchronization`.+ @Test+ fun realmQuery_subscribe_queryResultIsLatestVersion() = runBlocking {+ // Write data to a server Realm+ val section = Random.nextInt()+ val (email, password) = TestHelper.randomEmail() to "password1234"+ val user1 = app.createUserAndLogIn(email, password)+ val config = SyncConfiguration.Builder(+ user1,+ schema = setOf(FlexParentObject::class, FlexChildObject::class, FlexEmbeddedObject::class)+ ).initialSubscriptions { realm: Realm ->+ realm.query<FlexParentObject>("section = $0", section).subscribe()+ }.build()++ Realm.open(config).use { realmFromAnotherDevice ->+ realmFromAnotherDevice.writeBlocking {+ copyToRealm(FlexParentObject(section))+ }+ realmFromAnotherDevice.syncSession.uploadAllLocalChanges(30.seconds)+ }++ // Data still hasn't reached this device+ assertEquals(0, realm.query<FlexParentObject>().count().find())+ // Check that subscribing to a query, will run the query on the data downloaded from+ // the server and not just local data, due to WaitForSync.FIRST_TIME being the default.+ val result = realm.query<FlexParentObject>("section = $0", section).subscribe()+ assertEquals(1, result.size)+ assertEquals(section, result.first().section)+ }++ @Test+ fun realmQuery_subscribe_waitFirstTime() = runBlocking<Unit> {
There aren't really any logic testing that it is actually waitFirstTime
'ing here are there 🤔 It looks like that it is the above test that actually tests that, at least data wise. Are we testing the effect on the subscription set? ... and shouldn't it then show that we are not COMPLETE
after updating an existing subscription.
comment created time in an hour
Pull request review commentrealm/realm-kotlin
+package io.realm.kotlin.test.mongodb.shared++import io.realm.kotlin.Realm+import io.realm.kotlin.entities.sync.flx.FlexChildObject+import io.realm.kotlin.entities.sync.flx.FlexEmbeddedObject+import io.realm.kotlin.entities.sync.flx.FlexParentObject+import io.realm.kotlin.ext.query+import io.realm.kotlin.internal.platform.runBlocking+import io.realm.kotlin.mongodb.ext.subscribe+import io.realm.kotlin.mongodb.subscriptions+import io.realm.kotlin.mongodb.sync.Subscription+import io.realm.kotlin.mongodb.sync.SubscriptionSetState+import io.realm.kotlin.mongodb.sync.SyncConfiguration+import io.realm.kotlin.mongodb.sync.WaitForSync+import io.realm.kotlin.mongodb.syncSession+import io.realm.kotlin.query.RealmResults+import io.realm.kotlin.test.mongodb.TEST_APP_FLEX+import io.realm.kotlin.test.mongodb.TestApp+import io.realm.kotlin.test.mongodb.createUserAndLogIn+import io.realm.kotlin.test.util.TestHelper+import io.realm.kotlin.test.util.use+import kotlinx.coroutines.TimeoutCancellationException+import kotlin.random.Random+import kotlin.test.AfterTest+import kotlin.test.BeforeTest+import kotlin.test.Test+import kotlin.test.assertEquals+import kotlin.test.assertFailsWith+import kotlin.test.assertNotEquals+import kotlin.test.assertNull+import kotlin.time.Duration.Companion.nanoseconds+import kotlin.time.Duration.Companion.seconds++/**+ * Class for testing the various extension methods we have for bridging the gap between Subscriptions+ * and RealmQuery/RealmResults.+ */+class SubscriptionExtensionsTests {++ private lateinit var app: TestApp+ private lateinit var realm: Realm++ @BeforeTest+ fun setup() {+ app = TestApp(appName = TEST_APP_FLEX)+ val (email, password) = TestHelper.randomEmail() to "password1234"+ val user = runBlocking {+ app.createUserAndLogIn(email, password)+ }+ val config = SyncConfiguration.Builder(+ user,+ schema = setOf(FlexParentObject::class, FlexChildObject::class, FlexEmbeddedObject::class)+ )+ .build()+ realm = Realm.open(config)+ }++ @AfterTest+ fun tearDown() {+ if (this::realm.isInitialized && !realm.isClosed()) {+ realm.close()+ }+ if (this::app.isInitialized) {+ app.close()+ }+ }++ @Test+ fun realmQuery_subscribe_anonymous() = runBlocking {+ val subs = realm.subscriptions+ assertEquals(0, subs.size)+ val results: RealmResults<FlexParentObject> = realm.query<FlexParentObject>().subscribe()+ assertEquals(SubscriptionSetState.COMPLETE, subs.state)+ assertEquals(1, subs.size)+ val sub: Subscription = subs.first()+ assertNull(sub.name)+ assertEquals("TRUEPREDICATE ", sub.queryDescription)+ assertEquals(FlexParentObject::class.simpleName, sub.objectType)+ }++ // Check that subscribing twice to a query will result in the same subscription+ @Test+ fun realmQuery_subscribe_anonymousTwice() = runBlocking {+ val subs = realm.subscriptions+ assertEquals(0, subs.size)+ realm.query<FlexParentObject>().subscribe()+ realm.query<FlexParentObject>().subscribe()+ assertEquals(SubscriptionSetState.COMPLETE, subs.state)+ assertEquals(1, subs.size)+ val sub: Subscription = subs.first()+ assertNull(sub.name)+ assertEquals("TRUEPREDICATE ", sub.queryDescription)+ assertEquals(FlexParentObject::class.simpleName, sub.objectType)+ }++ // Check that anonymous RealmQuery and RealmResults .subscribe calls result in the same sub.+ @Test+ fun anonymousSubscriptionsOverlap() = runBlocking {+ val subs = realm.subscriptions+ assertEquals(0, subs.size)+ realm.query<FlexParentObject>().subscribe()+ realm.query<FlexParentObject>().find().subscribe()+ assertEquals(SubscriptionSetState.COMPLETE, subs.state)+ assertEquals(1, subs.size)+ val sub: Subscription = subs.first()+ assertNull(sub.name)+ assertEquals("TRUEPREDICATE ", sub.queryDescription)+ assertEquals(FlexParentObject::class.simpleName, sub.objectType)+ }++ // Verify that the realm query doesn't run against a frozen version previous to the Realm+ // being updated from `waitForSynchronization`.+ @Test+ fun realmQuery_subscribe_queryResultIsLatestVersion() = runBlocking {+ // Write data to a server Realm+ val section = Random.nextInt()+ val (email, password) = TestHelper.randomEmail() to "password1234"+ val user1 = app.createUserAndLogIn(email, password)+ val config = SyncConfiguration.Builder(+ user1,+ schema = setOf(FlexParentObject::class, FlexChildObject::class, FlexEmbeddedObject::class)+ ).initialSubscriptions { realm: Realm ->+ realm.query<FlexParentObject>("section = $0", section).subscribe()+ }.build()++ Realm.open(config).use { realmFromAnotherDevice ->+ realmFromAnotherDevice.writeBlocking {+ copyToRealm(FlexParentObject(section))+ }+ realmFromAnotherDevice.syncSession.uploadAllLocalChanges(30.seconds)+ }++ // Data still hasn't reached this device+ assertEquals(0, realm.query<FlexParentObject>().count().find())+ // Check that subscribing to a query, will run the query on the data downloaded from+ // the server and not just local data, due to WaitForSync.FIRST_TIME being the default.+ val result = realm.query<FlexParentObject>("section = $0", section).subscribe()+ assertEquals(1, result.size)+ assertEquals(section, result.first().section)+ }++ @Test+ fun realmQuery_subscribe_waitFirstTime() = runBlocking<Unit> {+ // Unnamed+ realm.query<FlexParentObject>().subscribe() // Default value is WaitForSync.FIRST_TIME+ var updatedSubs = realm.subscriptions+ assertEquals(1, updatedSubs.size)+ assertEquals(SubscriptionSetState.COMPLETE, updatedSubs.state)+ var sub: Subscription = updatedSubs.first()+ assertNull(sub.name)+ assertEquals("TRUEPREDICATE ", sub.queryDescription)+ assertEquals("FlexParentObject", sub.objectType)++ // Named+ realm.query<FlexParentObject>().subscribe("my-name") // Default value is WaitForSync.FIRST_TIME+ updatedSubs = realm.subscriptions+ assertEquals(2, updatedSubs.size)+ assertEquals(SubscriptionSetState.COMPLETE, updatedSubs.state)+ sub = updatedSubs.last()+ assertEquals("my-name", sub.name)+ assertEquals("TRUEPREDICATE ", sub.queryDescription)+ assertEquals("FlexParentObject", sub.objectType)+ }++ @Test+ fun realmQuery_subscribe_waitNever() = runBlocking {+ // Un-named+ realm.query<FlexParentObject>().subscribe(mode = WaitForSync.NEVER)+ var updatedSubs = realm.subscriptions+ assertEquals(1, updatedSubs.size)+ // Updating the subscription will happen in the background, but+ // hopefully hasn't reached COMPLETE yet.+ assertNotEquals(SubscriptionSetState.COMPLETE, updatedSubs.state)++ // Named+ realm.query<FlexParentObject>().subscribe(name = "my-name", mode = WaitForSync.NEVER)+ updatedSubs = realm.subscriptions+ assertEquals(2, updatedSubs.size)+ // Updating the subscription will happen in the background, but+ // hopefully hasn't reached COMPLETE yet.+ assertNotEquals(SubscriptionSetState.COMPLETE, updatedSubs.state)+ }++ @Test+ fun realmQuery_subscribe_waitAlways() = runBlocking {+ val sectionId = Random.nextInt()+ val results1 = realm.query<FlexParentObject>("section = $0", sectionId).subscribe() // Default value is WaitForSync.FIRST_TIME+ assertEquals(0, results1.size)+ uploadServerData(sectionId, 5)+ // Since the subscription is already present, we cannot control if the data is downloaded+ // before creating the next subscription. Instead we pause the syncSession and verify+ // that WaitForSync.ALWAYS timeout during network failures and resuming the session should+ // then work+ realm.syncSession.pause()+ assertFailsWith<TimeoutCancellationException> {+ realm.query<FlexParentObject>("section = $0", sectionId).subscribe(timeout = 3.seconds, mode = WaitForSync.ALWAYS)+ }+ realm.syncSession.resume()+ val results2 = realm.query<FlexParentObject>("section = $0", sectionId).subscribe(mode = WaitForSync.ALWAYS)+ assertEquals(5, results2.size)+ }++ @Test+ fun realmQuery_subscribe_timeOut_fails() = runBlocking<Unit> {+ assertFailsWith<TimeoutCancellationException> {+ realm.query<FlexParentObject>().subscribe(timeout = 1.nanoseconds)+ }+ }++ @Test+ fun realmQuery_subscribe_throwsInsideWrite() {+ realm.writeBlocking {+ // `subscribe()` being a suspend function make in hard to call+ // subscribe inside a write, but we should still detect it.+ runBlocking {+ assertFailsWith<IllegalStateException> {+ query<FlexParentObject>().subscribe()+ }+ assertFailsWith<IllegalStateException> {+ query<FlexParentObject>().subscribe(name = "my-name")+ }+ }+ }+ }++ @Test+ fun realmResults_subscribe_waitFirstTime() = runBlocking {+ // Unnamed+ realm.query<FlexParentObject>().find().subscribe() // Default value is WaitForSync.FIRST_TIME+ var updatedSubs = realm.subscriptions+ assertEquals(1, updatedSubs.size)+ assertEquals(SubscriptionSetState.COMPLETE, updatedSubs.state)+ var sub: Subscription = updatedSubs.first()+ assertNull(sub.name)+ assertEquals("TRUEPREDICATE ", sub.queryDescription)+ assertEquals("FlexParentObject", sub.objectType)++ // Named+ realm.query<FlexParentObject>().find().subscribe("my-name") // Default value is WaitForSync.FIRST_TIME+ updatedSubs = realm.subscriptions+ assertEquals(2, updatedSubs.size)+ assertEquals(SubscriptionSetState.COMPLETE, updatedSubs.state)+ sub = updatedSubs.last()+ assertEquals("my-name", sub.name)+ assertEquals("TRUEPREDICATE ", sub.queryDescription)+ assertEquals("FlexParentObject", sub.objectType)+ }++ @Test+ fun realmResults_subscribe_waitOnNever() = runBlocking {+ // Un-named+ realm.query<FlexParentObject>().find().subscribe(mode = WaitForSync.NEVER)+ var updatedSubs = realm.subscriptions+ assertEquals(1, updatedSubs.size)+ // Updating the subscription will happen in the background, but+ // hopefully hasn't reached COMPLETE yet.+ assertNotEquals(SubscriptionSetState.COMPLETE, updatedSubs.state)++ // Named+ realm.query<FlexParentObject>().find().subscribe(name = "my-name", mode = WaitForSync.NEVER)+ updatedSubs = realm.subscriptions+ assertEquals(2, updatedSubs.size)+ // Updating the subscription will happen in the background, but+ // hopefully hasn't reached COMPLETE yet.+ assertNotEquals(SubscriptionSetState.COMPLETE, updatedSubs.state)+ }++ @Test+ fun realmResults_subscribe_waitAlways() = runBlocking {+ val sectionId = Random.nextInt()+ val results1 = realm.query<FlexParentObject>("section = $0", sectionId).find().subscribe() // Default value is WaitForSync.FIRST_TIME+ assertEquals(0, results1.size)+ uploadServerData(sectionId, 5)+ // Since the subscription is already present, we cannot control if the data is downloaded+ // before creating the next subscription. Instead we pause the syncSession and verify+ // that WaitForSync.ALWAYS timeout during network failures and resuming the session should+ // then work+ realm.syncSession.pause()+ assertFailsWith<TimeoutCancellationException> {+ realm.query<FlexParentObject>("section = $0", sectionId).find().subscribe(timeout = 3.seconds, mode = WaitForSync.ALWAYS)+ }+ realm.syncSession.resume()+ val results2 = realm.query<FlexParentObject>("section = $0", sectionId).find().subscribe(mode = WaitForSync.ALWAYS)+ assertEquals(5, results2.size)+ }++ @Test+ fun realmResults_subscribe_subquery() = runBlocking {+ val topQueryResult: RealmResults<FlexParentObject> = realm.query<FlexParentObject>("section = 42").find()+ val subQueryResult: RealmResults<FlexParentObject> = topQueryResult.query("name == $0", "Jane").find()+ subQueryResult.subscribe()+ val subs = realm.subscriptions+ assertEquals(1, subs.size)+ assertEquals("section == 42 and name == \"Jane\" ", subs.first().queryDescription)+ subQueryResult.subscribe("my-name")+ assertEquals(2, subs.size)+ val lastSub = subs.last()+ assertEquals("my-name", lastSub.name)+ assertEquals("section == 42 and name == \"Jane\" ", lastSub.queryDescription)+ }++ @Test+ fun realmResults_subscribe_timeOut_fails() = runBlocking<Unit> {+ assertFailsWith<TimeoutCancellationException> {+ realm.query<FlexParentObject>().find().subscribe(timeout = 1.nanoseconds)
Again, any reason for not also testing the named variant?
comment created time in an hour
Pull request review commentrealm/realm-kotlin
+@file:Suppress("invisible_reference", "invisible_member")+package io.realm.kotlin.mongodb.ext++import io.realm.kotlin.mongodb.annotations.ExperimentalFlexibleSyncApi+import io.realm.kotlin.mongodb.sync.Subscription+import io.realm.kotlin.mongodb.sync.SubscriptionSet+import io.realm.kotlin.mongodb.sync.WaitForSync+import io.realm.kotlin.query.RealmQuery+import io.realm.kotlin.query.RealmResults+import io.realm.kotlin.types.RealmObject+import kotlin.time.Duration++/**+ * Automatically create an anonymous [Subscription] from a local query result in the background and+ * return the result of re-running the same query against the Realm file.+ *+ * This is a more streamlined alternative to doing something like this:+ *+ * ```+ * fun suspend getData(realm: Realm): RealmResults<Person> {+ * val results = realm.query<Person>().find()+ * realm.subscriptions.update { bgRealm ->+ * add("myquery", results.query(""))+ * }+ * realm.subscriptions.waitForSynchronization()+ * return realm.query<Person>().find()+ * }+ * ```+ *+ * It is possible to define whether or not to wait for the server to send all data before+ * running the local query. This is relevant as there might be delay from creating a subscription+ * to the data being available on the device due to either latency or because a large dataset needs+ * be downloaded.+ *+ * The default behaviour is that the first time `subscribe` is called, the query result will not+ * be returned until data has been downloaded from the server. On subsequent calls to `subscribe`+ * for the same query, the query will run immediately on the local database while any updates+ * are downloaded in the background.+ *+ * @param name name of the subscription. This can be used to identify it later in the [SubscriptionSet].+ * @param mode type of mode used to resolve the subscription. See [WaitForSync] for more details.+ * @param timeout How long to wait for the server to return the objects defined by the subscription.+ * This is only relevant for [WaitForSync.ALWAYS] and [WaitForSync.FIRST_TIME].+ * @return The result of running the query against the local Realm file. The results returned will+ * depend on which [mode] was used.+ * @throws kotlinx.coroutines.TimeoutCancellationException if the specified timeout was hit before+ * a query result could be returned.+ * @Throws IllegalStateException if this method is called on a Realm that isn't using Flexible Sync.+ */@ExperimentalFlexibleSyncApi+public suspend fun <T : RealmObject> RealmResults<T>.subscribe(+ mode: WaitForSync = WaitForSync.FIRST_TIME,+ timeout: Duration = Duration.INFINITE+): RealmResults<T> {+ val query: RealmQuery<T> = this.query("")+ return query.subscribe(mode, timeout)+}++/**+ * Automatically create a named [Subscription] from a local query result in the background and+ * return the result of re-running the same query against the Realm file.+ *+ * This is a more streamlined alternative to doing something like this:+ *+ * ```+ * fun suspend getData(realm: Realm): RealmResults<Person> {+ * val results = realm.query<Person>().find()+ * realm.subscriptions.update { bgRealm ->+ * add("myquery", results.query(""))+ * }+ * realm.subscriptions.waitForSynchronization()+ * return realm.query<Person>().find()+ * }+ * ```+ *+ * It is possible to define whether or not to wait for the server to send all data before+ * running the local query. This is relevant as there might be delay from creating a subscription+ * to the data being available on the device due to either latency or because a large dataset needs+ * be downloaded.+ *+ * The default behaviour is that the first time `subscribe` is called, the query result will not+ * be returned until data has been downloaded from the server. On subsequent calls to `subscribe`+ * for the same query, the query will run immediately on the local database while any updates+ * are downloaded in the background.+ *+ * @param name name of the subscription. This can be used to identify it later in the [SubscriptionSet].+ * @param mode type of mode used to resolve the subscription. See [WaitForSync] for more details.
* @param mode mode used to resolve the subscription. See [WaitForSync] for more details.
comment created time in an hour