Skip to content

Commit 5395490

Browse files
author
Christian Melchior
committed
Merge branch 'main' into releases
# Conflicts: # CHANGELOG.md # packages/cinterop/src/commonMain/kotlin/io/realm/kotlin/internal/interop/ErrorCode.kt # packages/cinterop/src/jvm/kotlin/io/realm/kotlin/internal/interop/ErrorCode.kt # packages/cinterop/src/nativeDarwin/kotlin/io/realm/kotlin/internal/interop/ErrorCode.kt # packages/external/core
2 parents 4557761 + 9138b66 commit 5395490

File tree

118 files changed

+4228
-1487
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

118 files changed

+4228
-1487
lines changed

CHANGELOG.md

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,34 @@
1-
## 1.10.3-SNAPSHOT (YYYY-MM-DD)
1+
## 1.11.0-SNAPSHOT (YYYY-MM-DD)
22

33
### Breaking Changes
4-
* None.
4+
* `BaseRealmObject.equals()` has changed from being identity-based only (===) to instead return `true` if two objects come from the same Realm version. This e.g means that reading the same object property twice will now be identical. Note, two Realm objects, even with identical values will not be considered equal if they belong to different versions.
5+
6+
```
7+
val childA: Child = realm.query<Child>().first().find()!!
8+
val childB: Child = realm.query<Child>().first().find()!!
9+
10+
// This behavior is the same both before 1.11.0 and before
11+
childA === childB // false
12+
13+
// This will return true in 1.11.0 and onwards. Before it will return false
14+
childA == childB
15+
16+
realm.writeBlocking { /* Do a write */ }
17+
val childC = realm.query<Child>().first().find()!!
18+
19+
// This will return false because childA belong to version 1, while childC belong to version 2.
20+
// Override equals/hashCode if value semantics are wanted.
21+
childA == childC
22+
```
523

624
### Enhancements
7-
* None.
25+
* Fulltext queries now support prefix search by using the * operator, like `description TEXT 'alex*'`. (Core issue [#6860](https://github.com/realm/realm-core/issues/6860))
26+
* Realm model classes now generate custom `toString`, `equals` and `hashCode` implementations. This makes it possible to compare by object reference across multiple collections. Note that two objects at different versions will not be considered equal, even
27+
if the content is the same. Custom implementations of these methods will be respected if they are present. (Issue [#1097](https://github.com/realm/realm-kotlin/issues/1097))
28+
* Support for performing geospatial queries using the new classes: `GeoPoint`, `GeoCircle`, `GeoBox`, and `GeoPolygon`. See `GeoPoint` documentation on how to persist locations. (Issue [#1403](https://github.com/realm/realm-kotlin/pull/1403))
29+
* Support for automatic resolution of embedded object constraints during migration through `RealmConfiguration.Builder.migration(migration: AutomaticSchemaMigration, resolveEmbeddedObjectConstraints: Boolean)`. (Issue [#1464](https://github.com/realm/realm-kotlin/issues/1464)
30+
* [Sync] Add support for customizing authorization headers and adding additional custom headers to all Atlas App service requests with `AppConfiguration.Builder.authorizationHeaderName()` and `AppConfiguration.Builder.addCustomRequestHeader(...)`. (Issue [#1453](https://github.com/realm/realm-kotlin/pull/1453))
31+
* [Sync] Added support for manually triggering a reconnect attempt for Device Sync. This is done through a new `App.Sync.reconnect()` method. This method is also now called automatically when a mobile device toggles off airplane mode. (Issue [#1479](https://github.com/realm/realm-kotlin/issues/1479))
832

933
### Fixed
1034
* Rare corruption causing 'Invalid streaming format cookie'-exception. Typically following compact, convert or copying to a new file. (Issue [#1440](https://github.com/realm/realm-kotlin/issues/1440))
@@ -27,7 +51,7 @@
2751
* Minimum Android SDK: 16.
2852

2953
### Internal
30-
* Updated to Realm Core 13.17.1, commit fb5bdccba1daad0bd6e65757a6a1596dc98cebf4.
54+
* Updated to Realm Core 13.19.1, commit c258e2681bca5fb33bbd23c112493817b43bfa86.
3155

3256

3357
## 1.10.2 (2023-07-21)

buildSrc/src/main/kotlin/Config.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ val HOST_OS: OperatingSystem = findHostOs()
6262

6363
object Realm {
6464
val ciBuild = (System.getenv("JENKINS_HOME") != null)
65-
const val version = "1.10.3-SNAPSHOT"
65+
const val version = "1.11.0-SNAPSHOT"
6666
const val group = "io.realm.kotlin"
6767
const val projectUrl = "https://realm.io"
6868
const val pluginPortalId = "io.realm.kotlin"

packages/cinterop/src/androidInstrumentedTest/kotlin/io/realm/kotlin/test/sync/SyncEnumTests.kt

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,30 +16,29 @@
1616

1717
package io.realm.kotlin.test.sync
1818

19+
import io.realm.kotlin.internal.interop.CategoryFlags
1920
import io.realm.kotlin.internal.interop.ErrorCategory
2021
import io.realm.kotlin.internal.interop.ErrorCode
2122
import io.realm.kotlin.internal.interop.realm_auth_provider_e
2223
import io.realm.kotlin.internal.interop.realm_errno_e
2324
import io.realm.kotlin.internal.interop.realm_error_category_e
2425
import io.realm.kotlin.internal.interop.realm_sync_client_metadata_mode_e
2526
import io.realm.kotlin.internal.interop.realm_sync_connection_state_e
26-
import io.realm.kotlin.internal.interop.realm_sync_errno_client_e
2727
import io.realm.kotlin.internal.interop.realm_sync_errno_connection_e
2828
import io.realm.kotlin.internal.interop.realm_sync_errno_session_e
29-
import io.realm.kotlin.internal.interop.realm_sync_error_category_e
3029
import io.realm.kotlin.internal.interop.realm_sync_session_resync_mode_e
3130
import io.realm.kotlin.internal.interop.realm_sync_session_state_e
3231
import io.realm.kotlin.internal.interop.realm_user_state_e
32+
import io.realm.kotlin.internal.interop.realm_web_socket_errno_e
3333
import io.realm.kotlin.internal.interop.sync.AuthProvider
3434
import io.realm.kotlin.internal.interop.sync.CoreConnectionState
3535
import io.realm.kotlin.internal.interop.sync.CoreSyncSessionState
3636
import io.realm.kotlin.internal.interop.sync.CoreUserState
3737
import io.realm.kotlin.internal.interop.sync.MetadataMode
38-
import io.realm.kotlin.internal.interop.sync.ProtocolClientErrorCode
39-
import io.realm.kotlin.internal.interop.sync.ProtocolConnectionErrorCode
40-
import io.realm.kotlin.internal.interop.sync.ProtocolSessionErrorCode
41-
import io.realm.kotlin.internal.interop.sync.SyncErrorCodeCategory
38+
import io.realm.kotlin.internal.interop.sync.SyncConnectionErrorCode
39+
import io.realm.kotlin.internal.interop.sync.SyncSessionErrorCode
4240
import io.realm.kotlin.internal.interop.sync.SyncSessionResyncMode
41+
import io.realm.kotlin.internal.interop.sync.WebsocketErrorCode
4342
import org.junit.Test
4443
import kotlin.reflect.KClass
4544
import kotlin.test.BeforeTest
@@ -59,10 +58,11 @@ class SyncEnumTests {
5958
}
6059

6160
@Test
62-
fun appErrorCategory() {
61+
fun errorCategory() {
6362
checkEnum(realm_error_category_e::class) { nativeValue ->
6463
ErrorCategory.of(nativeValue)
6564
}
65+
assertEquals(ErrorCategory.values().size, CategoryFlags.CATEGORY_ORDER.size)
6666
}
6767

6868
@Test
@@ -94,30 +94,23 @@ class SyncEnumTests {
9494
}
9595

9696
@Test
97-
fun protocolClientErrorCode() {
98-
checkEnum(realm_sync_errno_client_e::class) { nativeValue ->
99-
ProtocolClientErrorCode.of(nativeValue)
100-
}
101-
}
102-
103-
@Test
104-
fun protocolConnectionErrorCode() {
97+
fun syncConnectionErrorCode() {
10598
checkEnum(realm_sync_errno_connection_e::class) { nativeValue ->
106-
ProtocolConnectionErrorCode.of(nativeValue)
99+
SyncConnectionErrorCode.of(nativeValue)
107100
}
108101
}
109102

110103
@Test
111-
fun protocolSessionErrorCode() {
104+
fun syncSessionErrorCode() {
112105
checkEnum(realm_sync_errno_session_e::class) { nativeValue ->
113-
ProtocolSessionErrorCode.of(nativeValue)
106+
SyncSessionErrorCode.of(nativeValue)
114107
}
115108
}
116109

117110
@Test
118-
fun syncErrorCodeCategory() {
119-
checkEnum(realm_sync_error_category_e::class) { nativeValue ->
120-
SyncErrorCodeCategory.of(nativeValue)
111+
fun websocketErrorCode() {
112+
checkEnum(realm_web_socket_errno_e::class) { nativeValue ->
113+
WebsocketErrorCode.of(nativeValue)
121114
}
122115
}
123116

packages/cinterop/src/commonMain/kotlin/io/realm/kotlin/internal/interop/Callback.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package io.realm.kotlin.internal.interop
1919
import io.realm.kotlin.internal.interop.sync.AppError
2020
import io.realm.kotlin.internal.interop.sync.CoreSubscriptionSetState
2121
import io.realm.kotlin.internal.interop.sync.SyncError
22-
import io.realm.kotlin.internal.interop.sync.SyncErrorCode
2322

2423
// TODO Could be replace by lambda. See realm_app_config_new networkTransportFactory for example.
2524
interface Callback<T : RealmNativePointer> {
@@ -39,7 +38,7 @@ fun interface SyncErrorCallback {
3938

4039
// Interface exposed towards `library-sync`
4140
interface SyncSessionTransferCompletionCallback {
42-
fun invoke(error: SyncErrorCode?)
41+
fun invoke(error: CoreError?)
4342
}
4443

4544
interface LogCallback {
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package io.realm.kotlin.internal.interop
2+
3+
/**
4+
* Wrapper for C-API `realm_error_t`.
5+
* See https://github.com/realm/realm-core/blob/master/src/realm.h#L231
6+
*/
7+
class CoreError(
8+
categoriesNativeValue: Int,
9+
val errorCodeNativeValue: Int,
10+
messageNativeValue: String?,
11+
) {
12+
val categories: CategoryFlags = CategoryFlags((categoriesNativeValue))
13+
val errorCode: ErrorCode? = ErrorCode.of(errorCodeNativeValue)
14+
val message = messageNativeValue
15+
16+
operator fun contains(category: ErrorCategory): Boolean = category in categories
17+
}
18+
19+
data class CategoryFlags(val categoryFlags: Int) {
20+
21+
companion object {
22+
/**
23+
* See error code mapping to categories here:
24+
* https://github.com/realm/realm-core/blob/master/src/realm/error_codes.cpp#L29
25+
*
26+
* In most cases, only 1 category is assigned, but some errors have multiple. So instead of
27+
* overwhelming the user with many categories, we only select the most important to show
28+
* in the error message. "important" is of course tricky to define, but generally
29+
* we consider vague categories like [ErrorCategory.RLM_ERR_CAT_RUNTIME] as less important
30+
* than more specific ones like [ErrorCategory.RLM_ERR_CAT_JSON_ERROR].
31+
*
32+
* In the current implementation, categories between index 0 and 7 are considered equal
33+
* and the order is somewhat arbitrary. No error codes has multiple of these categories
34+
* associated either.
35+
*/
36+
val CATEGORY_ORDER: List<ErrorCategory> = listOf(
37+
ErrorCategory.RLM_ERR_CAT_CUSTOM_ERROR,
38+
ErrorCategory.RLM_ERR_CAT_WEBSOCKET_ERROR,
39+
ErrorCategory.RLM_ERR_CAT_SYNC_ERROR,
40+
ErrorCategory.RLM_ERR_CAT_SERVICE_ERROR,
41+
ErrorCategory.RLM_ERR_CAT_JSON_ERROR,
42+
ErrorCategory.RLM_ERR_CAT_CLIENT_ERROR,
43+
ErrorCategory.RLM_ERR_CAT_SYSTEM_ERROR,
44+
ErrorCategory.RLM_ERR_CAT_FILE_ACCESS,
45+
ErrorCategory.RLM_ERR_CAT_HTTP_ERROR,
46+
ErrorCategory.RLM_ERR_CAT_INVALID_ARG,
47+
ErrorCategory.RLM_ERR_CAT_APP_ERROR,
48+
ErrorCategory.RLM_ERR_CAT_LOGIC,
49+
ErrorCategory.RLM_ERR_CAT_RUNTIME,
50+
)
51+
}
52+
53+
/**
54+
* Returns a description of the most important category defined in [categoryFlags].
55+
* If no known categories are found, the integer values for all the categories is returned
56+
* as debugging information.
57+
*/
58+
val description: String = CATEGORY_ORDER.firstOrNull { category ->
59+
this.contains(category)
60+
}?.description ?: "$categoryFlags"
61+
62+
/**
63+
* Check whether a given [ErrorCategory] is included in the [categoryFlags].
64+
*/
65+
operator fun contains(category: ErrorCategory): Boolean = (categoryFlags and category.nativeValue) != 0
66+
}

packages/cinterop/src/commonMain/kotlin/io/realm/kotlin/internal/interop/CoreErrorConverter.kt

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,22 +31,21 @@ object CoreErrorConverter {
3131
path: String?,
3232
userError: Throwable?
3333
): Throwable {
34-
val categories: CategoryFlag = CategoryFlag(categoriesNativeValue)
34+
val categories: CategoryFlags = CategoryFlags(categoriesNativeValue)
3535
val errorCode: ErrorCode? = ErrorCode.of(errorCodeNativeValue)
3636
val message: String = "[$errorCode]: $messageNativeValue"
3737

3838
return userError ?: when {
3939
ErrorCode.RLM_ERR_INDEX_OUT_OF_BOUNDS == errorCode ->
4040
IndexOutOfBoundsException(message)
41-
ErrorCategory.RLM_ERR_CAT_INVALID_ARG in categories ->
41+
ErrorCategory.RLM_ERR_CAT_INVALID_ARG in categories && ErrorCategory.RLM_ERR_CAT_SYNC_ERROR !in categories -> {
42+
// Some sync errors flagged as both logical and illegal. In our case, we consider those
43+
// IllegalState, so discard them them here and let them fall through to the bottom case
4244
IllegalArgumentException(message)
45+
}
4346
ErrorCategory.RLM_ERR_CAT_LOGIC in categories || ErrorCategory.RLM_ERR_CAT_RUNTIME in categories ->
4447
IllegalStateException(message)
4548
else -> Error(message) // This can happen when propagating user level exceptions.
4649
}
4750
}
48-
49-
data class CategoryFlag(val categoryCode: Int) {
50-
operator fun contains(other: ErrorCategory): Boolean = categoryCode and other.nativeValue != 0
51-
}
5251
}

packages/cinterop/src/commonMain/kotlin/io/realm/kotlin/internal/interop/ErrorCategory.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ expect enum class ErrorCategory : CodeDescription {
3232
RLM_ERR_CAT_SERVICE_ERROR,
3333
RLM_ERR_CAT_HTTP_ERROR,
3434
RLM_ERR_CAT_CUSTOM_ERROR,
35-
RLM_ERR_CAT_WEBSOCKET_ERROR;
35+
RLM_ERR_CAT_WEBSOCKET_ERROR,
36+
RLM_ERR_CAT_SYNC_ERROR;
3637

3738
companion object {
3839
internal fun of(nativeValue: Int): ErrorCategory?

packages/cinterop/src/commonMain/kotlin/io/realm/kotlin/internal/interop/ErrorCode.kt

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,23 @@ expect enum class ErrorCode : CodeDescription {
5050
RLM_ERR_SCHEMA_VERSION_MISMATCH,
5151
RLM_ERR_NO_SUBSCRIPTION_FOR_WRITE,
5252
RLM_ERR_OPERATION_ABORTED,
53+
RLM_ERR_AUTO_CLIENT_RESET_FAILED,
54+
RLM_ERR_BAD_SYNC_PARTITION_VALUE,
55+
RLM_ERR_CONNECTION_CLOSED,
56+
RLM_ERR_INVALID_SUBSCRIPTION_QUERY,
57+
RLM_ERR_SYNC_CLIENT_RESET_REQUIRED,
58+
RLM_ERR_SYNC_COMPENSATING_WRITE,
59+
RLM_ERR_SYNC_CONNECT_FAILED,
60+
RLM_ERR_SYNC_CONNECT_TIMEOUT,
61+
RLM_ERR_SYNC_INVALID_SCHEMA_CHANGE,
62+
RLM_ERR_SYNC_PERMISSION_DENIED,
63+
RLM_ERR_SYNC_PROTOCOL_INVARIANT_FAILED,
64+
RLM_ERR_SYNC_PROTOCOL_NEGOTIATION_FAILED,
65+
RLM_ERR_SYNC_SERVER_PERMISSIONS_CHANGED,
66+
RLM_ERR_SYNC_USER_MISMATCH,
67+
RLM_ERR_TLS_HANDSHAKE_FAILED,
68+
RLM_ERR_WRONG_SYNC_TYPE,
69+
RLM_ERR_SYNC_WRITE_NOT_ALLOWED,
5370
RLM_ERR_SYSTEM_ERROR,
5471
RLM_ERR_LOGIC,
5572
RLM_ERR_NOT_SUPPORTED,
@@ -162,10 +179,7 @@ expect enum class ErrorCode : CodeDescription {
162179
RLM_ERR_MAINTENANCE_IN_PROGRESS,
163180
RLM_ERR_USERPASS_TOKEN_INVALID,
164181
RLM_ERR_INVALID_SERVER_RESPONSE,
165-
RLM_ERR_APP_SERVER_ERROR,
166-
RLM_ERR_WEBSOCKET_RESOLVE_FAILED_ERROR,
167-
RLM_ERR_WEBSOCKET_CONNECTION_CLOSED_CLIENT_ERROR,
168-
RLM_ERR_WEBSOCKET_CONNECTION_CLOSED_SERVER_ERROR,
182+
REALM_ERR_APP_SERVER_ERROR,
169183
RLM_ERR_CALLBACK,
170184
RLM_ERR_UNKNOWN;
171185

packages/cinterop/src/commonMain/kotlin/io/realm/kotlin/internal/interop/RealmInterop.kt

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ import io.realm.kotlin.internal.interop.sync.CoreUserState
2727
import io.realm.kotlin.internal.interop.sync.MetadataMode
2828
import io.realm.kotlin.internal.interop.sync.NetworkTransport
2929
import io.realm.kotlin.internal.interop.sync.ProgressDirection
30-
import io.realm.kotlin.internal.interop.sync.ProtocolClientErrorCode
31-
import io.realm.kotlin.internal.interop.sync.SyncErrorCodeCategory
3230
import io.realm.kotlin.internal.interop.sync.SyncSessionResyncMode
3331
import io.realm.kotlin.internal.interop.sync.SyncUserIdentity
3432
import kotlinx.coroutines.CoroutineDispatcher
@@ -129,8 +127,6 @@ typealias RealmMutableSubscriptionSetPointer = NativePointer<RealmMutableSubscri
129127
@Suppress("LongParameterList")
130128
class SyncConnectionParams(
131129
sdkVersion: String,
132-
localAppName: String?,
133-
localAppVersion: String?,
134130
bundleId: String,
135131
platformVersion: String,
136132
device: String,
@@ -139,8 +135,6 @@ class SyncConnectionParams(
139135
frameworkVersion: String
140136
) {
141137
val sdkName = "Kotlin"
142-
val localAppName: String?
143-
val localAppVersion: String?
144138
val bundleId: String
145139
val sdkVersion: String
146140
val platformVersion: String
@@ -158,8 +152,6 @@ class SyncConnectionParams(
158152
init {
159153
this.sdkVersion = sdkVersion
160154
this.bundleId = bundleId
161-
this.localAppName = localAppName
162-
this.localAppVersion = localAppVersion
163155
this.platformVersion = platformVersion
164156
this.device = device
165157
this.deviceVersion = deviceVersion
@@ -188,6 +180,7 @@ expect object RealmInterop {
188180
fun realm_config_get_encryption_key(config: RealmConfigurationPointer): ByteArray?
189181
fun realm_config_set_should_compact_on_launch_function(config: RealmConfigurationPointer, callback: CompactOnLaunchCallback)
190182
fun realm_config_set_migration_function(config: RealmConfigurationPointer, callback: MigrationCallback)
183+
fun realm_config_set_automatic_backlink_handling(config: RealmConfigurationPointer, enabled: Boolean)
191184
fun realm_config_set_data_initialization_function(config: RealmConfigurationPointer, callback: DataInitializationCallback)
192185
fun realm_config_set_in_memory(config: RealmConfigurationPointer, inMemory: Boolean)
193186
fun realm_schema_validate(schema: RealmSchemaPointer, mode: SchemaValidationMode): Boolean
@@ -627,8 +620,7 @@ expect object RealmInterop {
627620
fun realm_sync_session_resume(syncSession: RealmSyncSessionPointer)
628621
fun realm_sync_session_handle_error_for_testing(
629622
syncSession: RealmSyncSessionPointer,
630-
errorCode: ProtocolClientErrorCode,
631-
category: SyncErrorCodeCategory,
623+
error: ErrorCode,
632624
errorMessage: String,
633625
isFatal: Boolean
634626
)
@@ -719,6 +711,11 @@ expect object RealmInterop {
719711
callback: AppCallback<String>
720712
)
721713

714+
// Sync Client
715+
fun realm_app_sync_client_reconnect(app: RealmAppPointer)
716+
fun realm_app_sync_client_has_sessions(app: RealmAppPointer): Boolean
717+
fun realm_app_sync_client_wait_for_sessions_to_terminate(app: RealmAppPointer)
718+
722719
// Sync config
723720
fun realm_config_set_sync_config(
724721
realmConfiguration: RealmConfigurationPointer,

0 commit comments

Comments
 (0)