Skip to content

Commit 9d0267a

Browse files
[Automated] Merge releases into main (#1776)
1 parent 5ccc249 commit 9d0267a

File tree

9 files changed

+121
-30
lines changed

9 files changed

+121
-30
lines changed

.github/workflows/include-check-cache.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ jobs:
9393
# This also include changes to Realm Core as they are hashed as part of `/packages/external/core`
9494
- name: Calculate ./packages SHAs
9595
id: packages-cache-key
96-
run: echo "sha=${{ hashFiles('./packages/**', './buildSrc/**', '!./packages/test-base/**', '!./packages/test-sync/**') }}" >> $GITHUB_OUTPUT
96+
run: echo "sha=${{ hashFiles('./packages/**', './buildSrc/**') }}" >> $GITHUB_OUTPUT
9797

9898
- name: Calculate ./benchmarks SHAs
9999
id: calculate-benchmarks-cache-key

CHANGELOG.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,35 @@
2727
### Internal
2828
- None.
2929

30+
## 2.0.1 (YYYY-MM-DD)
31+
32+
### Breaking changes
33+
* None.
34+
35+
### Enhancements
36+
* None.
37+
38+
### Fixed
39+
* [Sync] Fatal sync exceptions are now thrown as `UnrecoverableSyncException`. (Issue [#1767](https://github.com/realm/realm-kotlin/issues/1767) [RKOTLIN-1096](https://jira.mongodb.org/browse/RKOTLIN-1096)).
40+
* [Sync] Fix `NullPointerException` in `SubscriptionSet.waitForSynchronization`. (Issue [#1777](https://github.com/realm/realm-kotlin/issues/1777) [RKOTLIN-1102](https://jira.mongodb.org/browse/RKOTLIN-1102)).
41+
42+
### Compatibility
43+
* File format: Generates Realms with file format v24 (reads and upgrades file format v10 or later).
44+
* Realm Studio 15.0.0 or above is required to open Realms created by this version.
45+
* This release is compatible with the following Kotlin releases:
46+
* Kotlin 2.0.0 and above. Support for experimental K2-compilation with `kotlin.experimental.tryK2=true`.
47+
* Ktor 2.1.2 and above.
48+
* Coroutines 1.7.0 and above.
49+
* AtomicFu 0.18.3 and above.
50+
* The new memory model only. See https://github.com/realm/realm-kotlin#kotlin-memory-model-and-coroutine-compatibility
51+
* Minimum Kbson 0.4.0.
52+
* Minimum Gradle version: 7.2.
53+
* Minimum Android Gradle Plugin version: 7.1.3.
54+
* Minimum Android SDK: 16.
55+
* Minimum R8: 8.0.34.
56+
57+
### Internal
58+
- None.
3059

3160
## 2.0.0 (2024-06-03)
3261

packages/library-base/src/commonMain/kotlin/io/realm/kotlin/exceptions/RealmException.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ package io.realm.kotlin.exceptions
99
*/
1010
public open class RealmException : RuntimeException {
1111
public constructor() : super()
12-
public constructor(message: String) : super(message)
13-
public constructor(message: String, cause: Throwable) : super(message, cause)
14-
public constructor(cause: Throwable) : super(cause)
12+
public constructor(message: String?) : super(message)
13+
public constructor(message: String?, cause: Throwable?) : super(message, cause)
14+
public constructor(cause: Throwable?) : super(cause)
1515
}

packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/exceptions/AppException.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,5 +94,5 @@ import io.realm.kotlin.exceptions.RealmException
9494
* @see SyncException
9595
*/
9696
public open class AppException internal constructor(
97-
message: String,
97+
message: String?,
9898
) : RealmException(message)

packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/exceptions/SyncExceptions.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import io.realm.kotlin.types.RealmAny
3131
*
3232
* @see io.realm.kotlin.mongodb.sync.SyncConfiguration.Builder.errorHandler
3333
*/
34-
public open class SyncException internal constructor(message: String) : AppException(message)
34+
public open class SyncException internal constructor(message: String?) : AppException(message)
3535

3636
/**
3737
* Thrown when something has gone wrong with Device Sync in a way that is not recoverable.
@@ -60,7 +60,7 @@ public class WrongSyncTypeException internal constructor(message: String) : Sync
6060
* Thrown when the server does not support one or more of the queries defined in the
6161
* [io.realm.kotlin.mongodb.sync.SubscriptionSet].
6262
*/
63-
public class BadFlexibleSyncQueryException internal constructor(message: String) :
63+
public class BadFlexibleSyncQueryException internal constructor(message: String?) :
6464
SyncException(message)
6565

6666
/**

packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/internal/RealmSyncUtils.kt

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -78,26 +78,34 @@ internal fun <T, R> channelResultCallback(
7878
internal fun convertSyncError(syncError: SyncError): SyncException {
7979
val errorCode = syncError.errorCode
8080
val message = createMessageFromSyncError(errorCode)
81-
return when (errorCode.errorCode) {
82-
ErrorCode.RLM_ERR_WRONG_SYNC_TYPE -> WrongSyncTypeException(message)
81+
return if (syncError.isFatal) {
82+
// An unrecoverable exception happened
83+
UnrecoverableSyncException(message)
84+
} else {
85+
when (errorCode.errorCode) {
86+
ErrorCode.RLM_ERR_WRONG_SYNC_TYPE -> WrongSyncTypeException(message)
8387

84-
ErrorCode.RLM_ERR_INVALID_SUBSCRIPTION_QUERY -> {
85-
// Flexible Sync Query was rejected by the server
86-
BadFlexibleSyncQueryException(message)
87-
}
88-
ErrorCode.RLM_ERR_SYNC_COMPENSATING_WRITE -> CompensatingWriteException(message, syncError.compensatingWrites)
88+
ErrorCode.RLM_ERR_INVALID_SUBSCRIPTION_QUERY -> {
89+
// Flexible Sync Query was rejected by the server
90+
BadFlexibleSyncQueryException(message)
91+
}
8992

90-
ErrorCode.RLM_ERR_SYNC_PROTOCOL_INVARIANT_FAILED,
91-
ErrorCode.RLM_ERR_SYNC_PROTOCOL_NEGOTIATION_FAILED,
92-
ErrorCode.RLM_ERR_SYNC_PERMISSION_DENIED -> {
93-
// Permission denied errors should be unrecoverable according to Core, i.e. the
94-
// client will disconnect sync and transition to the "inactive" state
95-
UnrecoverableSyncException(message)
96-
}
97-
else -> {
98-
// An error happened we are not sure how to handle. Just report as a generic
99-
// SyncException.
100-
SyncException(message)
93+
ErrorCode.RLM_ERR_SYNC_COMPENSATING_WRITE -> CompensatingWriteException(
94+
message,
95+
syncError.compensatingWrites
96+
)
97+
ErrorCode.RLM_ERR_SYNC_PROTOCOL_INVARIANT_FAILED,
98+
ErrorCode.RLM_ERR_SYNC_PROTOCOL_NEGOTIATION_FAILED,
99+
ErrorCode.RLM_ERR_SYNC_PERMISSION_DENIED -> {
100+
// Permission denied errors should be unrecoverable according to Core, i.e. the
101+
// client will disconnect sync and transition to the "inactive" state
102+
UnrecoverableSyncException(message)
103+
}
104+
else -> {
105+
// An error happened we are not sure how to handle. Just report as a generic
106+
// SyncException.
107+
SyncException(message)
108+
}
101109
}
102110
}
103111
}

packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/internal/SubscriptionSetImpl.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ internal class SubscriptionSetImpl<T : BaseRealm>(
127127
if (result) {
128128
return true
129129
} else {
130-
throw BadFlexibleSyncQueryException(errorMessage!!)
130+
throw BadFlexibleSyncQueryException(errorMessage)
131131
}
132132
}
133133
else -> throw IllegalStateException("Unexpected value: $result")

packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/CredentialsTests.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ class CredentialsTests {
369369
payload = mapOf("mail" to TestHelper.randomEmail(), "id" to 0)
370370
)
371371

372-
assertFailsWithMessage<AuthException>("unauthorized") {
372+
assertFailsWithMessage<AuthException>("Authentication failed") {
373373
runBlocking {
374374
app.login(credentials)
375375
}

packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SyncedRealmTests.kt

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
@file:Suppress("invisible_reference", "invisible_member")
16+
@file:Suppress("invisible_member", "invisible_reference")
1717

1818
package io.realm.kotlin.test.mongodb.common
1919

@@ -28,15 +28,19 @@ import io.realm.kotlin.entities.sync.flx.FlexChildObject
2828
import io.realm.kotlin.entities.sync.flx.FlexEmbeddedObject
2929
import io.realm.kotlin.entities.sync.flx.FlexParentObject
3030
import io.realm.kotlin.ext.query
31+
import io.realm.kotlin.internal.interop.ErrorCode
32+
import io.realm.kotlin.internal.interop.RealmInterop
3133
import io.realm.kotlin.internal.platform.fileExists
3234
import io.realm.kotlin.internal.platform.pathOf
3335
import io.realm.kotlin.internal.platform.runBlocking
3436
import io.realm.kotlin.log.RealmLog
3537
import io.realm.kotlin.mongodb.App
38+
import io.realm.kotlin.mongodb.Credentials
3639
import io.realm.kotlin.mongodb.User
3740
import io.realm.kotlin.mongodb.exceptions.DownloadingRealmTimeOutException
3841
import io.realm.kotlin.mongodb.exceptions.SyncException
3942
import io.realm.kotlin.mongodb.exceptions.UnrecoverableSyncException
43+
import io.realm.kotlin.mongodb.internal.SyncSessionImpl
4044
import io.realm.kotlin.mongodb.subscriptions
4145
import io.realm.kotlin.mongodb.sync.InitialSubscriptionsCallback
4246
import io.realm.kotlin.mongodb.sync.SubscriptionSetState
@@ -313,6 +317,56 @@ class SyncedRealmTests {
313317
realm3.close()
314318
}
315319

320+
@Test
321+
fun errorHandlerProcessFatalSyncErrors() {
322+
val channel = TestChannel<Throwable>()
323+
val user = runBlocking {
324+
app.login(Credentials.anonymous())
325+
}
326+
327+
val config = SyncConfiguration.Builder(
328+
schema = setOf(ParentPk::class, ChildPk::class),
329+
user = user,
330+
partitionValue = partitionValue
331+
).errorHandler { _, error ->
332+
channel.trySendOrFail(error)
333+
}.build()
334+
335+
runBlocking {
336+
val deferred = async {
337+
Realm.open(config).use { realm ->
338+
RealmInterop.realm_sync_session_handle_error_for_testing(
339+
syncSession = (realm.syncSession as SyncSessionImpl).nativePointer,
340+
error = ErrorCode.RLM_ERR_ACCOUNT_NAME_IN_USE,
341+
errorMessage = "Non fatal error",
342+
isFatal = true, // flipped https://jira.mongodb.org/browse/RCORE-2146
343+
)
344+
345+
RealmInterop.realm_sync_session_handle_error_for_testing(
346+
syncSession = (realm.syncSession as SyncSessionImpl).nativePointer,
347+
error = ErrorCode.RLM_ERR_INTERNAL_SERVER_ERROR,
348+
errorMessage = "Fatal error",
349+
isFatal = false, // flipped https://jira.mongodb.org/browse/RCORE-2146
350+
)
351+
}
352+
}
353+
354+
// First error
355+
channel.receiveOrFail().let { error ->
356+
assertNotNull(error.message)
357+
assertIs<SyncException>(error)
358+
}
359+
360+
// Second
361+
channel.receiveOrFail().let { error ->
362+
assertNotNull(error.message)
363+
assertIs<UnrecoverableSyncException>(error)
364+
}
365+
366+
deferred.cancel()
367+
}
368+
}
369+
316370
@Test
317371
fun errorHandlerReceivesPermissionDeniedSyncError() {
318372
val channel = TestChannel<Throwable>()
@@ -336,15 +390,15 @@ class SyncedRealmTests {
336390
Realm.open(config).use {
337391
// Make sure that the test eventually fail. Coroutines can cancel a delay
338392
// so this doesn't always block the test for 10 seconds.
339-
delay(10 * 1000)
393+
delay(10_000)
340394
channel.send(AssertionError("Realm was successfully opened"))
341395
}
342396
}
343397

344398
val error = channel.receiveOrFail()
345-
assertTrue(error is UnrecoverableSyncException, "Was $error")
346399
val message = error.message
347400
assertNotNull(message)
401+
assertTrue(error is UnrecoverableSyncException, "Was $error")
348402
assertTrue(
349403
message.lowercase().contains("permission denied"),
350404
"The error should be 'PermissionDenied' but it was: $message"

0 commit comments

Comments
 (0)