diff --git a/CHANGELOG.md b/CHANGELOG.md index 39f6d94104..ff4f8bd01e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,8 @@ * None. ### Fixed -* None. +* Rare corruption causing 'Invalid streaming format cookie'-exception. Typically following compact, convert or copying to a new file. (Issue [#7775](https://github.com/realm/realm-java/issues/7775), since v10.13.0 (Core v12.12.0)) +* [RealmApp] Crash when opening a Realm with a proxy enabled. (Issue [#7828](https://github.com/realm/realm-java/issues/7828)) ### Compatibility * File format: Generates Realms with format v23. Unsynced Realms will be upgraded from Realm Java 2.0 and later. Synced Realms can only be read and upgraded if created with Realm Java v10.0.0-BETA.1. @@ -17,7 +18,7 @@ * Android Gradle Plugin 7.4.0 and above. ### Internal -* None. +* Updated to Realm Core 13.22.0, commit 0b19887b5b25746afaf474fe298e38f450f0486e. ## 10.16.1 (2023-06-26) diff --git a/realm/realm-library/src/androidTest/java/io/realm/LinkingObjectsQueryTests.java b/realm/realm-library/src/androidTest/java/io/realm/LinkingObjectsQueryTests.java index b1d34d5845..bf6b0c62f6 100644 --- a/realm/realm-library/src/androidTest/java/io/realm/LinkingObjectsQueryTests.java +++ b/realm/realm-library/src/androidTest/java/io/realm/LinkingObjectsQueryTests.java @@ -17,7 +17,6 @@ import org.bson.types.Decimal128; import org.bson.types.ObjectId; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -401,11 +400,11 @@ public void isEmpty_acrossLink() { case LINKING_OBJECTS: // Rows 0 and 1 are not included as they are linked to another row through FIELD_OBJECT // Row 2 is included (no link) - assertEquals(1, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_LO_OBJECT).count()); + assertEquals(0, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_LO_OBJECT).count()); // Row 0 has link to row 0 which has a backlink (list); not included // Row 1 has link to row 1 which has a backlink (list); not included - // Row 2 has no link; included - assertEquals(1, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_LO_LIST).count()); + // Row 2 has no link; no included + assertEquals(0, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_LO_LIST).count()); break; default: // tested in RealmQueryTests @@ -427,17 +426,17 @@ public void isEmpty_acrossLinkingObjectObjectLink() { case LIST: // Row 0: backlink to row 0, linklist is empty; included // Row 1: backlink to row 1, linklist to row 0; not included - // Row 2: no backlink; included - assertEquals(2, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_LO_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_LIST).count()); + // Row 2: no backlink; not included + assertEquals(1, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_LO_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_LIST).count()); break; case LINKING_OBJECTS: // Both row 0 and 1 have a link/backlink; not included - // row 2 has no link/backlink and an empty list; included - assertEquals(1, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_LO_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_LO_OBJECT).count()); - assertEquals(1, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_LO_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_LO_LIST).count()); + // row 2 has no link/backlink and an empty list; not included + assertEquals(0, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_LO_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_LO_OBJECT).count()); + assertEquals(0, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_LO_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_LO_LIST).count()); break; case OBJECT: - assertEquals(1, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_LO_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_OBJECT).count()); + assertEquals(0, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_LO_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_OBJECT).count()); break; case INTEGER_LIST: assertEquals(2, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_LO_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_INTEGER_LIST).count()); @@ -542,7 +541,7 @@ public void isEmpty_acrossLinkingObjectObjectLink() { assertEquals(2, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_LO_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_DECIMAL128_SET).count()); break; case LINK_SET: - assertEquals(3, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_LO_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_LINK_SET).count()); + assertEquals(2, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_LO_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_LINK_SET).count()); break; default: fail("Unknown type: " + type); @@ -565,24 +564,24 @@ public void isEmpty_acrossLinkingObjectListLink() { assertEquals(1, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_LO_LIST + "." + AllJavaTypesUnsupportedTypes.FIELD_BINARY).count()); break; case LIST: - assertEquals(1, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_LO_LIST + "." + AllJavaTypesUnsupportedTypes.FIELD_LIST).count()); + assertEquals(0, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_LO_LIST + "." + AllJavaTypesUnsupportedTypes.FIELD_LIST).count()); break; case LINKING_OBJECTS: // Row 0: Backlink (list) to row 1, row 1 backlink to row 1; not included // Row 1: Backlink (list) to row 2, row 2 no backlink; included - // Row 2: No backlink (list); included - assertEquals(2, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_LO_LIST + "." + AllJavaTypesUnsupportedTypes.FIELD_LO_OBJECT).count()); + // Row 2: No backlink (list); not included + assertEquals(1, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_LO_LIST + "." + AllJavaTypesUnsupportedTypes.FIELD_LO_OBJECT).count()); // Step 1: // Row 0 skipped; FIELD_LO_LIST.count > 0 // Row 1 included; FIELD_LO_LIST.count() == 0 // // Step 2: now checking Row 2 - // Row 0 included: goes to Row 1 where FIELD_LO_LIST.count() == 0 - assertEquals(2, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_LO_LIST + "." + AllJavaTypesUnsupportedTypes.FIELD_LO_LIST).count()); + // Row 0 not included: goes to Row 1 where FIELD_LO_LIST.count() == 0 + assertEquals(1, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_LO_LIST + "." + AllJavaTypesUnsupportedTypes.FIELD_LO_LIST).count()); break; case OBJECT: - assertEquals(2, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_LO_LIST + "." + AllJavaTypesUnsupportedTypes.FIELD_OBJECT).count()); + assertEquals(1, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_LO_LIST + "." + AllJavaTypesUnsupportedTypes.FIELD_OBJECT).count()); break; case INTEGER_LIST: assertEquals(2, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_LO_LIST + "." + AllJavaTypesUnsupportedTypes.FIELD_INTEGER_LIST).count()); @@ -862,7 +861,8 @@ public void isNotEmpty_acrossLinkingObjectObjectLink() { assertEquals(0, realm.where(AllJavaTypesUnsupportedTypes.class).isNotEmpty(AllJavaTypesUnsupportedTypes.FIELD_LO_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_REALM_ANY_SET).count()); break; default: - fail("Unknown type: " + type); + // Do nothing + fail("Unknown type: " + type); } } } diff --git a/realm/realm-library/src/androidTest/java/io/realm/RealmQueryTests.java b/realm/realm-library/src/androidTest/java/io/realm/RealmQueryTests.java index 42aa99ff56..6f32a23282 100644 --- a/realm/realm-library/src/androidTest/java/io/realm/RealmQueryTests.java +++ b/realm/realm-library/src/androidTest/java/io/realm/RealmQueryTests.java @@ -2665,18 +2665,18 @@ public void isEmpty_acrossLink() { // Row 0: Backlink list to row 1, list to row 0; included // Row 1: Backlink list to row 2, list to row 1; included // Row 2: No backlink list; not included - assertEquals(2, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_LIST).count()); + assertEquals(1, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_LIST).count()); break; case LINKING_OBJECTS: - assertEquals(1, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_LO_LIST).count()); + assertEquals(0, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_LO_LIST).count()); // Row 0: Link to row 0, backlink to row 0; not included // Row 1: Link to row 1m backlink to row 1; not included - // Row 2: Empty link; included - assertEquals(1, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_LO_OBJECT).count()); + // Row 2: Empty link; not included + assertEquals(0, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_LO_OBJECT).count()); break; case OBJECT: - assertEquals(1, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_OBJECT).count()); + assertEquals(0, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_OBJECT).count()); break; case INTEGER_LIST: assertEquals(3, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_INTEGER_LIST).count()); @@ -2745,7 +2745,7 @@ public void isEmpty_acrossLink() { assertEquals(3, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_DECIMAL128_DICTIONARY).count()); break; case STRING_TO_LINK_MAP: - assertEquals(3, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_LINK_DICTIONARY).count()); + assertEquals(2, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_LINK_DICTIONARY).count()); break; case MIXED_SET: assertEquals(3, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_REALM_ANY_SET).count()); @@ -2781,7 +2781,7 @@ public void isEmpty_acrossLink() { assertEquals(3, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_DECIMAL128_SET).count()); break; case LINK_SET: - assertEquals(3, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_LINK_SET).count()); + assertEquals(2, realm.where(AllJavaTypesUnsupportedTypes.class).isEmpty(AllJavaTypesUnsupportedTypes.FIELD_OBJECT + "." + AllJavaTypesUnsupportedTypes.FIELD_LINK_SET).count()); break; default: fail("Unknown type: " + type); diff --git a/realm/realm-library/src/androidTest/java/io/realm/RealmResultsTests.java b/realm/realm-library/src/androidTest/java/io/realm/RealmResultsTests.java index de9dca6e33..9577cfaadb 100644 --- a/realm/realm-library/src/androidTest/java/io/realm/RealmResultsTests.java +++ b/realm/realm-library/src/androidTest/java/io/realm/RealmResultsTests.java @@ -33,6 +33,7 @@ import java.math.BigDecimal; import java.text.SimpleDateFormat; +import java.time.temporal.ChronoUnit; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; @@ -1939,7 +1940,7 @@ public void setValue_specificType_internalNameOnDynamicRealms() { @Test public void asJSON() throws JSONException { - Date date = Calendar.getInstance().getTime(); + Date date = Date.from(Calendar.getInstance().getTime().toInstant().truncatedTo(ChronoUnit.SECONDS)); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); sdf.setTimeZone(TimeZone.getTimeZone("GMT")); // Core return dates in UTC time String now = sdf.format(date); @@ -2144,7 +2145,7 @@ public void asJSON_withEscaping() throws JSONException { @Test public void asJSON_cycles() throws JSONException { - Date date = Calendar.getInstance().getTime(); + Date date = Date.from(Calendar.getInstance().getTime().toInstant().truncatedTo(ChronoUnit.SECONDS)); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); sdf.setTimeZone(TimeZone.getTimeZone("GMT")); // Core return dates in UTC time String now = sdf.format(date); diff --git a/realm/realm-library/src/androidTest/java/io/realm/RealmTests.java b/realm/realm-library/src/androidTest/java/io/realm/RealmTests.java index 55712c3303..6921776ad7 100644 --- a/realm/realm-library/src/androidTest/java/io/realm/RealmTests.java +++ b/realm/realm-library/src/androidTest/java/io/realm/RealmTests.java @@ -1193,6 +1193,7 @@ public void compactRealm_populatedRealm() throws IOException { assertTrue(before >= after); } + // This is only supported on API level 30 and below @Test public void compactRealm_onExternalStorage() { final File externalFilesDir = context.getExternalFilesDir(null); @@ -4431,6 +4432,7 @@ public void getLocalInstanceCount() { assertEquals(0, Realm.getLocalInstanceCount(config)); } + // This is only supported on API 30 and below @Test public void namedPipeDirForExternalStorage() { @@ -4851,14 +4853,14 @@ public void getNumberOfActiveVersions() throws InterruptedException { for (int i = 0; i < 5; i++) { bgRealm.executeTransaction(r -> { /* empty */ }); } - assertEquals(6, bgRealm.getNumberOfActiveVersions()); + assertEquals(3, bgRealm.getNumberOfActiveVersions()); bgWritesCompleted.countDown(); TestHelper.awaitOrFail(closeBgRealm); bgRealm.close(); }); t.start(); TestHelper.awaitOrFail(bgWritesCompleted); - assertEquals(6, realm.getNumberOfActiveVersions()); + assertEquals(3, realm.getNumberOfActiveVersions()); closeBgRealm.countDown(); t.join(); realm.refresh(); // Release old versions for GC diff --git a/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/EmailPasswordAuthTests.kt b/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/EmailPasswordAuthTests.kt index 48b1dfa3a7..cf14d22015 100644 --- a/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/EmailPasswordAuthTests.kt +++ b/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/EmailPasswordAuthTests.kt @@ -36,6 +36,7 @@ import io.realm.TEST_APP_1 import io.realm.TEST_APP_2 import io.realm.TEST_APP_3 import kotlin.random.Random +import kotlin.test.assertContains abstract class EmailPasswordAuthTests { @@ -569,7 +570,7 @@ class EmailPasswordAuthWithCustomFunctionConfirmTests: EmailPasswordAuthTests() val exception = assertFailsWith { provider.retryCustomConfirmation(email) } - assertEquals("""failed to confirm user "$email"""", exception.errorMessage) + assertContains(exception.errorMessage!!, "failed to confirm user \"$email\"") } @Test diff --git a/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/SchemaTests.kt b/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/SchemaTests.kt index f9191475a4..503621e489 100644 --- a/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/SchemaTests.kt +++ b/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/SchemaTests.kt @@ -19,6 +19,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import io.realm.entities.SyncStringOnly import io.realm.mongodb.SyncTestUtils.Companion.createTestUser import io.realm.mongodb.close +import io.realm.mongodb.registerUserAndLogin import io.realm.mongodb.sync.SyncConfiguration import io.realm.mongodb.sync.testSchema import io.realm.util.assertFailsWith @@ -47,7 +48,7 @@ class SchemaTests { @Before fun setUp() { app = TestApp() - val user = createTestUser(app) + val user = app.registerUserAndLogin(TestHelper.getRandomEmail(), "123456") config = configFactory .createSyncConfigurationBuilder(user) .testSchema(SyncStringOnly::class.java) diff --git a/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/SyncedRealmMigrationTests.kt b/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/SyncedRealmMigrationTests.kt index 91e0ff1e4a..8a8ebffed3 100644 --- a/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/SyncedRealmMigrationTests.kt +++ b/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/SyncedRealmMigrationTests.kt @@ -24,8 +24,8 @@ import io.realm.internal.OsObjectSchemaInfo import io.realm.internal.OsRealmConfig import io.realm.internal.OsSchemaInfo import io.realm.internal.OsSharedRealm -import io.realm.mongodb.SyncTestUtils.Companion.createTestUser import io.realm.mongodb.close +import io.realm.mongodb.registerUserAndLogin import io.realm.mongodb.sync.testSchema import io.realm.util.assertFailsWithMessage import org.bson.types.ObjectId @@ -63,7 +63,7 @@ class SyncedRealmMigrationTests { @Test fun migrateRealm_syncConfigurationThrows() { - val config = configFactory.createSyncConfigurationBuilder(createTestUser(app)).build() + val config = configFactory.createSyncConfigurationBuilder(app.registerUserAndLogin()).build() assertFailsWith { Realm.migrateRealm(config) } @@ -73,7 +73,7 @@ class SyncedRealmMigrationTests { // automatically. @Test fun addField_worksWithMigrationError() { - val config = configFactory.createSyncConfigurationBuilder(createTestUser(app)) + val config = configFactory.createSyncConfigurationBuilder(app.registerUserAndLogin()) .testSchema(SyncStringOnly::class.java) .build() @@ -95,7 +95,7 @@ class SyncedRealmMigrationTests { // The underlying field should not be deleted, just hidden. @Test fun missingFields_hiddenSilently() { - val config = configFactory.createSyncConfigurationBuilder(createTestUser(app)) + val config = configFactory.createSyncConfigurationBuilder(app.registerUserAndLogin()) .testSchema(SyncStringOnly::class.java) .build() @@ -124,7 +124,7 @@ class SyncedRealmMigrationTests { // Check that a Realm cannot be opened if it contain breaking schema changes, like changing a primary key @Test fun breakingSchemaChange_throws() { - val config = configFactory.createSyncConfigurationBuilder(createTestUser(app)) + val config = configFactory.createSyncConfigurationBuilder(app.registerUserAndLogin()) .testSchema(PrimaryKeyAsString::class.java) .build() @@ -146,7 +146,7 @@ class SyncedRealmMigrationTests { // Check that indexes are not being added if the schema version is the same @Test fun sameSchemaVersion_doNotRebuildIndexes() { - val config = configFactory.createSyncConfigurationBuilder(createTestUser(app)) + val config = configFactory.createSyncConfigurationBuilder(app.registerUserAndLogin()) .testSchema(IndexedFields::class.java) .schemaVersion(42) .build() @@ -173,7 +173,7 @@ class SyncedRealmMigrationTests { // Check that indexes are being added if the schema version is different @Test fun differentSchemaVersions_rebuildIndexes() { - val config = configFactory.createSyncConfigurationBuilder(createTestUser(app)) + val config = configFactory.createSyncConfigurationBuilder(app.registerUserAndLogin()) .testSchema(IndexedFields::class.java) .schemaVersion(42) .build() @@ -201,7 +201,7 @@ class SyncedRealmMigrationTests { // Check that indexes are being added if other fields are being added as well @Test fun addingFields_rebuildIndexes() { - val config = configFactory.createSyncConfigurationBuilder(createTestUser(app)) + val config = configFactory.createSyncConfigurationBuilder(app.registerUserAndLogin()) .testSchema(IndexedFields::class.java) .schemaVersion(42) .build() @@ -228,7 +228,7 @@ class SyncedRealmMigrationTests { @Test fun schemaVersionUpgradedWhenMigrating() { - val config = configFactory.createSyncConfigurationBuilder(createTestUser(app)) + val config = configFactory.createSyncConfigurationBuilder(app.registerUserAndLogin()) .testSchema(SyncStringOnly::class.java) .schemaVersion(42) .build() @@ -252,7 +252,7 @@ class SyncedRealmMigrationTests { @Test fun moreFieldsThanExpectedIsAllowed() { val config = configFactory - .createSyncConfigurationBuilder(createTestUser(app)) + .createSyncConfigurationBuilder(app.registerUserAndLogin()) .testSchema(SyncStringOnly::class.java) .build() diff --git a/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/mongodb/AppExt.kt b/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/mongodb/AppExt.kt index ca147b3df8..55af2fd928 100644 --- a/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/mongodb/AppExt.kt +++ b/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/mongodb/AppExt.kt @@ -19,6 +19,7 @@ import io.realm.RealmExt import io.realm.admin.ServerAdmin import io.realm.mongodb.sync.testReset import io.realm.testClearApplicationContext +import io.realm.TestHelper /** * Resets the Realm Application and delete all local state. @@ -37,7 +38,7 @@ fun App.close() { * Helper function for quickly logging in test users. * This only works if users in the Realm Application are configured to be automatically confirmed. */ -fun App.registerUserAndLogin(email: String, password: String): User { +fun App.registerUserAndLogin(email: String = TestHelper.getRandomEmail(), password: String = "123456"): User { emailPassword.registerUser(email, password) return login(Credentials.emailPassword(email, password)) } diff --git a/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/mongodb/MongoClientTest.kt b/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/mongodb/MongoClientTest.kt index e13cf57efe..982602e1fe 100644 --- a/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/mongodb/MongoClientTest.kt +++ b/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/mongodb/MongoClientTest.kt @@ -327,7 +327,7 @@ class MongoClientTest { assertFailsWith { insertOne(doc1).get()!! }.let { e -> - assertEquals("insert not permitted", e.errorMessage) + assertContains(e.errorMessage!!, "insert not permitted") } } } @@ -440,7 +440,7 @@ class MongoClientTest { assertFailsWith { insertMany(documents).get()!! }.let { e -> - assertEquals("insert not permitted", e.errorMessage) + assertContains(e.errorMessage!!, "insert not permitted") } } } diff --git a/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/mongodb/UserTests.kt b/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/mongodb/UserTests.kt index 1ce7638112..381c705509 100644 --- a/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/mongodb/UserTests.kt +++ b/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/mongodb/UserTests.kt @@ -29,6 +29,7 @@ import org.junit.Before import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith +import kotlin.test.assertContains import kotlin.test.assertFails import kotlin.test.assertFailsWith @@ -208,7 +209,7 @@ class UserTests { anonUser.linkCredentials(Credentials.apiKey(apiKey.value)) } - assertEquals("invalid user link request", exception.errorMessage); + assertContains(exception.errorMessage!!, "invalid user link request"); assertEquals(ErrorCode.Category.FATAL, exception.errorCode.category); assertEquals("realm::app::ServiceError", exception.errorCode.type); assertEquals(ErrorCode.INVALID_PARAMETER, exception.errorCode); diff --git a/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/mongodb/sync/SessionTests.kt b/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/mongodb/sync/SessionTests.kt index 4adb9ba87f..efdb7b33ca 100644 --- a/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/mongodb/sync/SessionTests.kt +++ b/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/mongodb/sync/SessionTests.kt @@ -168,7 +168,7 @@ class SessionTests { val realm = Realm.getInstance(config) looperThread.closeAfterTest(realm) - user.app.sync.simulateClientReset(realm.syncSession, ErrorCode.AUTO_CLIENT_RESET_FAILURE) + user.app.sync.simulateClientReset(realm.syncSession, ErrorCode.AUTOMATIC_CLIENT_RESET_FAILED) } // Check that a if Seamless loss Client Reset fails the error is correctly reported. @@ -213,7 +213,7 @@ class SessionTests { val realm = Realm.getInstance(config) looperThread.closeAfterTest(realm) - user.app.sync.simulateClientReset(realm.syncSession, ErrorCode.AUTO_CLIENT_RESET_FAILURE) + user.app.sync.simulateClientReset(realm.syncSession, ErrorCode.AUTOMATIC_CLIENT_RESET_FAILED) } @Test @@ -253,7 +253,7 @@ class SessionTests { val realm = Realm.getInstance(config) looperThread.closeAfterTest(realm) - user.app.sync.simulateClientReset(realm.syncSession, ErrorCode.AUTO_CLIENT_RESET_FAILURE) + user.app.sync.simulateClientReset(realm.syncSession, ErrorCode.AUTOMATIC_CLIENT_RESET_FAILED) } // Check that a Client Reset is correctly reported. @@ -744,7 +744,7 @@ class SessionTests { looperThread.closeAfterTest(realm) // Trigger error - user.app.sync.simulateClientReset(realm.syncSession, ErrorCode.AUTO_CLIENT_RESET_FAILURE) + user.app.sync.simulateClientReset(realm.syncSession, ErrorCode.AUTOMATIC_CLIENT_RESET_FAILED) } @Test diff --git a/realm/realm-library/src/main/cpp/io_realm_internal_OsObject.cpp b/realm/realm-library/src/main/cpp/io_realm_internal_OsObject.cpp index 07f409f60a..c0ad49f9fb 100644 --- a/realm/realm-library/src/main/cpp/io_realm_internal_OsObject.cpp +++ b/realm/realm-library/src/main/cpp/io_realm_internal_OsObject.cpp @@ -80,7 +80,7 @@ struct ChangeCallback { // The local ref of jstring needs to be released to avoid reach the local ref table size limitation. std::vector field_names; - auto table = m_wrapper->m_object.obj().get_table(); + auto table = m_wrapper->m_object.get_obj().get_table(); for (const auto& col: change_set.columns) { if (col.second.empty()) { continue; diff --git a/realm/realm-library/src/main/cpp/io_realm_internal_OsRealmConfig.cpp b/realm/realm-library/src/main/cpp/io_realm_internal_OsRealmConfig.cpp index a7c64b6c99..06ab4f94e1 100644 --- a/realm/realm-library/src/main/cpp/io_realm_internal_OsRealmConfig.cpp +++ b/realm/realm-library/src/main/cpp/io_realm_internal_OsRealmConfig.cpp @@ -265,31 +265,14 @@ JNIEXPORT jstring JNICALL Java_io_realm_internal_OsRealmConfig_nativeCreateAndSe // Doing the methods lookup from the thread that loaded the lib, to avoid // https://developer.android.com/training/articles/perf-jni.html#faq_FindClass static JavaMethod java_error_callback_method(env, sync_manager_class, "notifyErrorHandler", - "(BILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); + "(BILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); // error handler will be called form the sync client thread auto error_handler = [sync_service_object = JavaGlobalRefByCopy(env, j_java_sync_service)](std::shared_ptr session, SyncError error) { - - auto std_error_code = error.to_status().get_std_error_code(); - int error_code = error.to_status().get_std_error_code().value(); - const std::error_category& error_category = std_error_code.category(); - - jbyte category; - if (error_category == realm::sync::client_error_category()) { - category = io_realm_internal_ErrorCategory_RLM_SYNC_ERROR_CATEGORY_CLIENT; - } else if (error_category == realm::sync::protocol_error_category()) { - if (realm::sync::is_session_level_error(realm::sync::ProtocolError(std_error_code.value()))) { - category = io_realm_internal_ErrorCategory_RLM_SYNC_ERROR_CATEGORY_SESSION; - } - else { - category = io_realm_internal_ErrorCategory_RLM_SYNC_ERROR_CATEGORY_CONNECTION; - } - } else if (error_category == std::system_category() || error_category == realm::util::error::basic_system_error_category()) { - category = io_realm_internal_ErrorCategory_RLM_SYNC_ERROR_CATEGORY_SYSTEM; - } else { - category = io_realm_internal_ErrorCategory_RLM_SYNC_ERROR_CATEGORY_UNKNOWN; - } - + JNIEnv* env = realm::jni_util::JniUtils::get_env(true); + ErrorCodes::Error error_code = error.status.code(); + auto error_code_value = static_cast(error_code); + jbyte error_category = categoryAsJByte(error.status); // System/Connection errors are defined by constants in // https://android.googlesource.com/kernel/lk/+/upstream-master/include/errno.h @@ -297,21 +280,19 @@ JNIEXPORT jstring JNICALL Java_io_realm_internal_OsRealmConfig_nativeCreateAndSe // // For this reason we manually map the constants to the error integer values defined in Java. // For simplicity Java re-use the values currently defined in errno.h. - if (error_category == realm::util::error::basic_system_error_category()) { - switch(error_code) { - case ECONNRESET: error_code = 104; break; - case ESHUTDOWN: error_code = 110; break; - case ECONNREFUSED: error_code = 111; break; - case EADDRINUSE: error_code = 112; break; - case ECONNABORTED: error_code = 113; break; + if (ErrorCodes::error_categories(error.status.code()).test(realm::ErrorCategory::system_error)) { + switch(error_code_value) { + case ECONNRESET: error_code_value = 104; break; + case ESHUTDOWN: error_code_value = 110; break; + case ECONNREFUSED: error_code_value = 111; break; + case EADDRINUSE: error_code_value = 112; break; + case ECONNABORTED: error_code_value = 113; break; default: /* Do nothing */ (void)0; } } - auto error_message = error.what(); - std::string client_reset_path_info; // All client reset errors will be in the protocol category. Re-assign the error code @@ -319,22 +300,23 @@ JNIEXPORT jstring JNICALL Java_io_realm_internal_OsRealmConfig_nativeCreateAndSe // This way we only have one error in Java representing Client Reset. if (error.is_client_reset_requested()) { client_reset_path_info = error.user_info[SyncError::c_recovery_file_path_key]; - error_code = 7; // See ErrorCode.java - category = io_realm_internal_ErrorCategory_RLM_APP_ERROR_CATEGORY_CUSTOM; + error_code_value = 7; // See ErrorCode.java + error_category = io_realm_internal_ErrorCategory_RLM_SYNC_ERROR_CATEGORY_CUSTOM; } - - JNIEnv* env = realm::jni_util::JniUtils::get_env(true); - jstring jerror_message = to_jstring(env, error_message); + jstring jerror_message = to_jstring(env, error.simple_message); + jstring jerror_message_log_url = to_jstring(env, error.logURL); jstring jclient_reset_path_info = to_jstring(env, client_reset_path_info); jstring jsession_path = to_jstring(env, session->path()); env->CallVoidMethod(sync_service_object.get(), java_error_callback_method, - category, - error_code, + error_category, + error_code_value, jerror_message, + jerror_message_log_url, jclient_reset_path_info, jsession_path); env->DeleteLocalRef(jerror_message); + env->DeleteLocalRef(jerror_message_log_url); env->DeleteLocalRef(jsession_path); if (env->ExceptionCheck()) { env->ExceptionDescribe(); @@ -353,7 +335,7 @@ JNIEXPORT jstring JNICALL Java_io_realm_internal_OsRealmConfig_nativeCreateAndSe JStringAccessor refresh_token(env, j_refresh_token); JStringAccessor access_token(env, j_access_token); JStringAccessor device_id(env, j_device_id); - user = app->sync_manager()->get_user(user_id, refresh_token, access_token, user_provider, device_id); + user = app->sync_manager()->get_user(user_id, refresh_token, access_token, device_id); } SyncSessionStopPolicy session_stop_policy = static_cast(j_session_stop_policy); diff --git a/realm/realm-library/src/main/cpp/io_realm_internal_objectstore_OsApp.cpp b/realm/realm-library/src/main/cpp/io_realm_internal_objectstore_OsApp.cpp index f92e2ae36e..a4d1067904 100644 --- a/realm/realm-library/src/main/cpp/io_realm_internal_objectstore_OsApp.cpp +++ b/realm/realm-library/src/main/cpp/io_realm_internal_objectstore_OsApp.cpp @@ -142,8 +142,6 @@ JNIEXPORT jlong JNICALL Java_io_realm_internal_objectstore_OsApp_nativeCreate(JN app_id, network_transport, util::Optional(base_url), - util::Optional(app_name), - util::Optional(app_version), util::Optional(j_request_timeout_ms), { platform_version, diff --git a/realm/realm-library/src/main/cpp/io_realm_internal_objectstore_OsAppCredentials.cpp b/realm/realm-library/src/main/cpp/io_realm_internal_objectstore_OsAppCredentials.cpp index 394c97ebda..fe4431d9d6 100644 --- a/realm/realm-library/src/main/cpp/io_realm_internal_objectstore_OsAppCredentials.cpp +++ b/realm/realm-library/src/main/cpp/io_realm_internal_objectstore_OsAppCredentials.cpp @@ -80,12 +80,7 @@ JNIEXPORT jlong JNICALL Java_io_realm_internal_objectstore_OsAppCredentials_nati } case io_realm_internal_objectstore_OsAppCredentials_TYPE_API_KEY: { JStringAccessor token(env, (jstring) env->GetObjectArrayElement(j_args, 0)); - creds = AppCredentials::user_api_key(token); - break; - } - case io_realm_internal_objectstore_OsAppCredentials_TYPE_SERVER_API_KEY: { - JStringAccessor token(env, (jstring) env->GetObjectArrayElement(j_args, 0)); - creds = AppCredentials::server_api_key(token); + creds = AppCredentials::api_key(token); break; } case io_realm_internal_objectstore_OsAppCredentials_TYPE_CUSTOM_FUNCTION: { diff --git a/realm/realm-library/src/main/cpp/io_realm_internal_objectstore_OsAsyncOpenTask.cpp b/realm/realm-library/src/main/cpp/io_realm_internal_objectstore_OsAsyncOpenTask.cpp index c5b1e87339..363ac195cf 100644 --- a/realm/realm-library/src/main/cpp/io_realm_internal_objectstore_OsAsyncOpenTask.cpp +++ b/realm/realm-library/src/main/cpp/io_realm_internal_objectstore_OsAsyncOpenTask.cpp @@ -56,7 +56,7 @@ JNIEXPORT jlong JNICALL Java_io_realm_internal_objectstore_OsAsyncOpenTask_start } catch(const realm::Exception& e) { const realm::Status& status = e.to_status(); - jint error_code = status.get_std_error_code().value(); + auto error_code = static_cast(status.code()); jbyte category = categoryAsJByte(e.to_status()); jstring j_error_msg = to_jstring(local_env, e.what()); local_env->CallVoidMethod(task.get(), java_notify_error, category, error_code, j_error_msg); diff --git a/realm/realm-library/src/main/cpp/io_realm_internal_objectstore_OsObjectBuilder.cpp b/realm/realm-library/src/main/cpp/io_realm_internal_objectstore_OsObjectBuilder.cpp index a65e89b381..950f011d8d 100644 --- a/realm/realm-library/src/main/cpp/io_realm_internal_objectstore_OsObjectBuilder.cpp +++ b/realm/realm-library/src/main/cpp/io_realm_internal_objectstore_OsObjectBuilder.cpp @@ -224,7 +224,7 @@ JNIEXPORT jlong JNICALL Java_io_realm_internal_objectstore_OsObjectBuilder_nativ auto list = *reinterpret_cast(builder_ptr); JavaValue values = JavaValue(list); Object obj = Object::create(ctx, shared_realm, object_schema, values, policy); - return reinterpret_cast(new Obj(obj.obj())); + return reinterpret_cast(new Obj(obj.get_obj())); } CATCH_STD() return realm::npos; @@ -249,7 +249,7 @@ JNIEXPORT jlong JNICALL Java_io_realm_internal_objectstore_OsObjectBuilder_nativ auto list = *reinterpret_cast(builder_ptr); JavaValue values = JavaValue(list); Object obj = Object::create(ctx, shared_realm, object_schema, values, policy, embedded_object_key); - return reinterpret_cast(new Obj(obj.obj())); + return reinterpret_cast(new Obj(obj.get_obj())); } CATCH_STD() return realm::npos; diff --git a/realm/realm-library/src/main/cpp/io_realm_internal_objectstore_OsSyncUser.cpp b/realm/realm-library/src/main/cpp/io_realm_internal_objectstore_OsSyncUser.cpp index 64565a532f..0c7de9617f 100644 --- a/realm/realm-library/src/main/cpp/io_realm_internal_objectstore_OsSyncUser.cpp +++ b/realm/realm-library/src/main/cpp/io_realm_internal_objectstore_OsSyncUser.cpp @@ -185,17 +185,6 @@ JNIEXPORT jstring JNICALL Java_io_realm_internal_objectstore_OsSyncUser_nativeGe return nullptr; } -JNIEXPORT jstring JNICALL Java_io_realm_internal_objectstore_OsSyncUser_nativeGetLocalIdentity(JNIEnv* env, jclass, jlong j_native_ptr) -{ - try { - auto user = *reinterpret_cast*>(j_native_ptr); - return to_jstring(env, user->local_identity()); - } - CATCH_STD(); - return nullptr; -} - - JNIEXPORT jbyte JNICALL Java_io_realm_internal_objectstore_OsSyncUser_nativeGetState(JNIEnv* env, jclass, jlong j_native_ptr) { try { @@ -212,32 +201,11 @@ JNIEXPORT jbyte JNICALL Java_io_realm_internal_objectstore_OsSyncUser_nativeGetS return static_cast(-1); } -JNIEXPORT void JNICALL Java_io_realm_internal_objectstore_OsSyncUser_nativeSetState(JNIEnv* env, jclass, jlong j_native_ptr, jbyte j_state) -{ - try { - auto user = *reinterpret_cast*>(j_native_ptr); - switch(j_state) { - case io_realm_internal_objectstore_OsSyncUser_STATE_LOGGED_OUT: - user->set_state(SyncUser::State::LoggedOut); - break; - case io_realm_internal_objectstore_OsSyncUser_STATE_LOGGED_IN: - user->set_state(SyncUser::State::LoggedIn); - break; - case io_realm_internal_objectstore_OsSyncUser_STATE_REMOVED: - user->set_state(SyncUser::State::Removed); - break; - default: - throw std::logic_error(util::format("Unknown state: %1", j_state)); - } - } - CATCH_STD(); -} - JNIEXPORT jstring JNICALL Java_io_realm_internal_objectstore_OsSyncUser_nativeGetProviderType(JNIEnv* env, jclass, jlong j_native_ptr) { try { auto user = *reinterpret_cast*>(j_native_ptr); - return to_jstring(env, user->provider_type()); + return to_jstring(env, user->identities().begin()->provider_type); } CATCH_STD(); return nullptr; diff --git a/realm/realm-library/src/main/cpp/io_realm_mongodb_sync_Sync.cpp b/realm/realm-library/src/main/cpp/io_realm_mongodb_sync_Sync.cpp index 19d5140ce7..5e783379aa 100644 --- a/realm/realm-library/src/main/cpp/io_realm_mongodb_sync_Sync.cpp +++ b/realm/realm-library/src/main/cpp/io_realm_mongodb_sync_Sync.cpp @@ -63,17 +63,9 @@ JNIEXPORT void JNICALL Java_io_realm_mongodb_sync_Sync_nativeSimulateSyncError(J return; } - std::error_code code = std::error_code{static_cast(err_code), - type == "realm::sync::ProtocolError" ? - realm::sync::protocol_error_category() : realm::sync::client_error_category() - }; - - sync::SessionErrorInfo sync_error( - code, - std::string(message), - to_bool(is_fatal) - ); - sync_error.server_requests_action = sync::ProtocolErrorInfo::Action::ClientReset; + auto code = static_cast(err_code); + auto status = Status(code, message); + auto sync_error = sync::SessionErrorInfo{status, sync::IsFatal{static_cast(is_fatal)}}; SyncSession::OnlyForTesting::handle_error(*session, std::move(sync_error)); } CATCH_STD() diff --git a/realm/realm-library/src/main/cpp/io_realm_mongodb_sync_SyncSession.cpp b/realm/realm-library/src/main/cpp/io_realm_mongodb_sync_SyncSession.cpp index 55bf0fcdd3..54dafc3d01 100644 --- a/realm/realm-library/src/main/cpp/io_realm_mongodb_sync_SyncSession.cpp +++ b/realm/realm-library/src/main/cpp/io_realm_mongodb_sync_SyncSession.cpp @@ -69,10 +69,8 @@ void handleCompletion(jint callback_id, JavaMethod &java_notify_result_method, if (!status.is_ok()) { jbyte category = categoryAsJByte(status); java_error_category = JavaLocalRef(env, JavaClassGlobalDef::new_long(env,category)); - java_error_code = JavaLocalRef(env, JavaClassGlobalDef::new_long(env, - status.get_std_error_code().value())); - java_error_message = JavaLocalRef(env, env->NewStringUTF( - status.reason().c_str())); + java_error_code = JavaLocalRef(env, JavaClassGlobalDef::new_long(env, static_cast(status.code()))); + java_error_message = JavaLocalRef(env, env->NewStringUTF( status.reason().c_str())); } env->CallVoidMethod(session_ref.get(), java_notify_result_method, diff --git a/realm/realm-library/src/main/cpp/java_network_transport.hpp b/realm/realm-library/src/main/cpp/java_network_transport.hpp index f96a838849..4d97814ce1 100644 --- a/realm/realm-library/src/main/cpp/java_network_transport.hpp +++ b/realm/realm-library/src/main/cpp/java_network_transport.hpp @@ -93,7 +93,7 @@ struct JavaNetworkTransport : public app::GenericNetworkTransport { static void handleError(const JavaGlobalRefByCopy &callback, util::Optional &error, JNIEnv *env, const JavaClass &java_callback_class) { - static JavaMethod java_notify_onerror(env, java_callback_class, "onError", "(BILjava/lang/String;)V"); + static JavaMethod java_notify_onerror(env, java_callback_class, "onError", "(BILjava/lang/String;Ljava/lang/String;)V"); auto err = error.value(); jbyte category = categoryAsJByte(err.to_status()); int error_code = err.is_custom_error() || err.is_http_error() ? err.additional_status_code.value() : err.code(); @@ -101,7 +101,8 @@ struct JavaNetworkTransport : public app::GenericNetworkTransport { java_notify_onerror, category, error_code, - to_jstring(env, err.what())); + to_jstring(env, err.what()), + to_jstring(env, err.link_to_server_logs)); } // Helper method for constructing callbacks for REST calls that must return an actual result to Java diff --git a/realm/realm-library/src/main/cpp/java_object_accessor.hpp b/realm/realm-library/src/main/cpp/java_object_accessor.hpp index aa82c67b0f..2f920b3225 100644 --- a/realm/realm-library/src/main/cpp/java_object_accessor.hpp +++ b/realm/realm-library/src/main/cpp/java_object_accessor.hpp @@ -654,7 +654,7 @@ inline Obj JavaContext::unbox(JavaValue const& v, CreatePolicy policy, ObjKey cu return Obj(); } REALM_ASSERT(object_schema); - return Object::create(const_cast(*this), realm, *object_schema, v, policy, current_row).obj(); + return Object::create(const_cast(*this), realm, *object_schema, v, policy, current_row).get_obj(); } template <> diff --git a/realm/realm-library/src/main/cpp/realm-core b/realm/realm-library/src/main/cpp/realm-core index 79183be641..0b19887b5b 160000 --- a/realm/realm-library/src/main/cpp/realm-core +++ b/realm/realm-library/src/main/cpp/realm-core @@ -1 +1 @@ -Subproject commit 79183be6417821431fff44a6d416a68664957c66 +Subproject commit 0b19887b5b25746afaf474fe298e38f450f0486e diff --git a/realm/realm-library/src/main/cpp/util.cpp b/realm/realm-library/src/main/cpp/util.cpp index af31ea40eb..36dbcba760 100644 --- a/realm/realm-library/src/main/cpp/util.cpp +++ b/realm/realm-library/src/main/cpp/util.cpp @@ -15,6 +15,7 @@ */ #include +#include #include #include diff --git a/realm/realm-library/src/main/cpp/util.hpp b/realm/realm-library/src/main/cpp/util.hpp index 6581ee6c0e..5bf17e5aa5 100644 --- a/realm/realm-library/src/main/cpp/util.hpp +++ b/realm/realm-library/src/main/cpp/util.hpp @@ -141,33 +141,49 @@ void ThrowNullValueException(JNIEnv* env, const realm::TableRef table, realm::Co #include inline jbyte categoryAsJByte(const realm::Status &status) { - auto std_error_code = status.get_std_error_code(); - const std::error_category& error_category = std_error_code.category(); + // See error code mapping to categories here: + // https://github.com/realm/realm-core/blob/master/src/realm/error_codes.cpp#L29 + // + // In most cases, only 1 category is assigned, but some errors have multiple. However we only + // want to assign one category to each error (in order to preserve backwards compatibility) + // + // So we attempt to find the most "important" category to assign the error to. This is tricky, + // but generally we consider vague categories like + // `io_realm_internal_ErrorCategory_RLM_SYNC_ERROR_CATEGORY_RUNTIME` is + // less important than more specific ones like `io_realm_internal_ErrorCategory_RLM_APP_ERROR_CATEGORY_JSON` + // + // In the current implementation, categories between index 0 and 7 are considered equal + // and the order is somewhat arbitrary. No error codes has multiple of these categories + // associated either. realm::ErrorCategory categories = realm::ErrorCodes::error_categories(status.code()); jbyte category = io_realm_internal_ErrorCategory_RLM_SYNC_ERROR_CATEGORY_UNKNOWN; - if (categories.test(realm::ErrorCategory::client_error)) { - category = io_realm_internal_ErrorCategory_RLM_APP_ERROR_CATEGORY_CLIENT; - } else if (categories.test(realm::ErrorCategory::json_error)) { - category = io_realm_internal_ErrorCategory_RLM_APP_ERROR_CATEGORY_JSON; + if (categories.test(realm::ErrorCategory::custom_error)) { + category = io_realm_internal_ErrorCategory_RLM_SYNC_ERROR_CATEGORY_CUSTOM; + } else if (categories.test(realm::ErrorCategory::websocket_error)) { + category = io_realm_internal_ErrorCategory_RLM_SYNC_ERROR_CATEGORY_WEBSOCKET; + } else if (categories.test(realm::ErrorCategory::sync_error)) { + category = io_realm_internal_ErrorCategory_RLM_SYNC_ERROR_CATEGORY_SYNC; } else if (categories.test(realm::ErrorCategory::service_error)) { - category = io_realm_internal_ErrorCategory_RLM_APP_ERROR_CATEGORY_SERVICE; - } else if (categories.test(realm::ErrorCategory::http_error)) { - category = io_realm_internal_ErrorCategory_RLM_APP_ERROR_CATEGORY_HTTP; - } else if (categories.test(realm::ErrorCategory::custom_error)) { - category = io_realm_internal_ErrorCategory_RLM_APP_ERROR_CATEGORY_CUSTOM; - } else if (error_category == realm::sync::client_error_category()) { + category = io_realm_internal_ErrorCategory_RLM_SYNC_ERROR_CATEGORY_SERVICE; + } else if (categories.test(realm::ErrorCategory::json_error)) { + category = io_realm_internal_ErrorCategory_RLM_SYNC_ERROR_CATEGORY_JSON; + } else if (categories.test(realm::ErrorCategory::client_error)) { category = io_realm_internal_ErrorCategory_RLM_SYNC_ERROR_CATEGORY_CLIENT; - } else if (error_category == realm::sync::protocol_error_category()) { - if (realm::sync::is_session_level_error(realm::sync::ProtocolError(std_error_code.value()))) { - category = io_realm_internal_ErrorCategory_RLM_SYNC_ERROR_CATEGORY_SESSION; - } - else { - category = io_realm_internal_ErrorCategory_RLM_SYNC_ERROR_CATEGORY_CONNECTION; - } - } else if (error_category == std::system_category() || error_category == realm::util::error::basic_system_error_category()) { - category = io_realm_internal_ErrorCategory_RLM_SYNC_ERROR_CATEGORY_SYSTEM; + } else if (categories.test(realm::ErrorCategory::system_error)) { + category = io_realm_internal_ErrorCategory_RLM_APP_ERROR_CATEGORY_SYSTEM; + } else if (categories.test(realm::ErrorCategory::file_access)) { + category = io_realm_internal_ErrorCategory_RLM_APP_ERROR_CATEGORY_FILE_ACCESS; + } else if (categories.test(realm::ErrorCategory::http_error)) { + category = io_realm_internal_ErrorCategory_RLM_SYNC_ERROR_CATEGORY_HTTP; + } else if (categories.test(realm::ErrorCategory::invalid_argument)) { + category = io_realm_internal_ErrorCategory_RLM_APP_ERROR_CATEGORY_INVALID_ARGUMENT; + } else if (categories.test(realm::ErrorCategory::app_error)) { + category = io_realm_internal_ErrorCategory_RLM_SYNC_ERROR_CATEGORY_APP; + } else if (categories.test(realm::ErrorCategory::logic_error)) { + category = io_realm_internal_ErrorCategory_RLM_APP_ERROR_CATEGORY_LOGIC; + } else if (categories.test(realm::ErrorCategory::runtime_error)) { + category = io_realm_internal_ErrorCategory_RLM_APP_ERROR_CATEGORY_RUNTIME; } - return category; } diff --git a/realm/realm-library/src/main/java/io/realm/internal/OsRealmConfig.java b/realm/realm-library/src/main/java/io/realm/internal/OsRealmConfig.java index 247ce46afe..93abd243b4 100644 --- a/realm/realm-library/src/main/java/io/realm/internal/OsRealmConfig.java +++ b/realm/realm-library/src/main/java/io/realm/internal/OsRealmConfig.java @@ -355,13 +355,17 @@ private OsRealmConfig(final RealmConfiguration config, SocketAddress address = proxy.address(); if (address instanceof InetSocketAddress) { InetSocketAddress inetAddress = (InetSocketAddress) address; - - nativeSetSyncConfigProxySettings( - nativePtr, - proxyType, - getHostString(inetAddress), - inetAddress.getPort() - ); + String hostname = getHostString(inetAddress); + if (hostname != null) { + nativeSetSyncConfigProxySettings( + nativePtr, + proxyType, + hostname, + inetAddress.getPort() + ); + } else { + RealmLog.error("Could not retrieve proxy's hostname."); + } } else { RealmLog.error("Unsupported proxy socket address type: " + address.getClass().getName()); } @@ -378,15 +382,26 @@ private OsRealmConfig(final RealmConfiguration config, this.resolvedRealmURI = resolvedRealmURI; } - // Backport the behavior from InetSocketAddress.getHostString, a function that is only available - // on SDK>=19 (KITKAT) + /** + * Backport the behavior from InetSocketAddress.getHostString, a function that is only available + * from SDK 19 (KITKAT) + * + * @param socketAddress address to extract the hostname from. + * @return hostname or the String form of the address. + */ private String getHostString(InetSocketAddress socketAddress) { - InetAddress address = socketAddress.getAddress(); + if (socketAddress.getHostName() != null) { + return socketAddress.getHostName(); + } + if (socketAddress.getAddress() != null) { + InetAddress address = socketAddress.getAddress(); - if (address.getHostName() != null) - return address.getHostName(); - else - return address.getHostAddress(); + if (address.getHostName() != null) { + return address.getHostName(); + } else + return address.getHostAddress(); + } + return null; } @Override diff --git a/realm/realm-library/src/objectServer/java/io/realm/internal/ErrorCategory.java b/realm/realm-library/src/objectServer/java/io/realm/internal/ErrorCategory.java index bc889dd778..4e25740488 100644 --- a/realm/realm-library/src/objectServer/java/io/realm/internal/ErrorCategory.java +++ b/realm/realm-library/src/objectServer/java/io/realm/internal/ErrorCategory.java @@ -22,60 +22,66 @@ public class ErrorCategory { @Native // annotate a member to force JNI header generation - public static final byte RLM_APP_ERROR_CATEGORY_JSON = 0; - public static final byte RLM_APP_ERROR_CATEGORY_SERVICE = 1; - public static final byte RLM_APP_ERROR_CATEGORY_HTTP = 2; - public static final byte RLM_APP_ERROR_CATEGORY_CUSTOM = 3; - public static final byte RLM_APP_ERROR_CATEGORY_CLIENT = 4; - - public static final byte RLM_SYNC_ERROR_CATEGORY_CLIENT = 5; - public static final byte RLM_SYNC_ERROR_CATEGORY_CONNECTION = 6; // protocol - public static final byte RLM_SYNC_ERROR_CATEGORY_SESSION = 7; - - /** - * System error - POSIX errno, Win32 HRESULT, etc. - */ - public static final byte RLM_SYNC_ERROR_CATEGORY_SYSTEM = 8; - - /** - * Unknown source of error. - */ - public static final byte RLM_SYNC_ERROR_CATEGORY_UNKNOWN = 9; + public static final byte RLM_APP_ERROR_CATEGORY_LOGIC = 0; + public static final byte RLM_APP_ERROR_CATEGORY_RUNTIME = 1; + public static final byte RLM_APP_ERROR_CATEGORY_INVALID_ARGUMENT = 2; + public static final byte RLM_APP_ERROR_CATEGORY_FILE_ACCESS = 3; + public static final byte RLM_APP_ERROR_CATEGORY_SYSTEM = 4; // System error - POSIX errno, Win32 HRESULT, etc. + public static final byte RLM_SYNC_ERROR_CATEGORY_APP = 5; + public static final byte RLM_SYNC_ERROR_CATEGORY_CLIENT = 6; + public static final byte RLM_SYNC_ERROR_CATEGORY_JSON = 7; + public static final byte RLM_SYNC_ERROR_CATEGORY_SERVICE = 8; + public static final byte RLM_SYNC_ERROR_CATEGORY_HTTP = 9; + public static final byte RLM_SYNC_ERROR_CATEGORY_CUSTOM = 10; + public static final byte RLM_SYNC_ERROR_CATEGORY_WEBSOCKET = 11; + public static final byte RLM_SYNC_ERROR_CATEGORY_SYNC = 12; + public static final byte RLM_SYNC_ERROR_CATEGORY_UNKNOWN = 13; // Unknown source of error. This is not a category exposed by Core. public static String toCategory(byte value) { String category; switch (value) { - case RLM_APP_ERROR_CATEGORY_JSON: - category = ErrorCode.Type.JSON; + case RLM_APP_ERROR_CATEGORY_LOGIC: + category = ErrorCode.Type.LOGIC; break; - case RLM_APP_ERROR_CATEGORY_SERVICE: - category = ErrorCode.Type.SERVICE; + case RLM_APP_ERROR_CATEGORY_RUNTIME: + category = ErrorCode.Type.RUNTIME; break; - case RLM_APP_ERROR_CATEGORY_HTTP: - category = ErrorCode.Type.HTTP; + case RLM_APP_ERROR_CATEGORY_INVALID_ARGUMENT: + category = ErrorCode.Type.INVALID_ARGUMENT; + break; + case RLM_APP_ERROR_CATEGORY_SYSTEM: + category = ErrorCode.Type.SYSTEM; break; - case RLM_APP_ERROR_CATEGORY_CUSTOM: - category = ErrorCode.Type.JAVA; + case RLM_APP_ERROR_CATEGORY_FILE_ACCESS: + category = ErrorCode.Type.FILE_ACCESS; break; - case RLM_APP_ERROR_CATEGORY_CLIENT: + case RLM_SYNC_ERROR_CATEGORY_APP: category = ErrorCode.Type.APP; break; case RLM_SYNC_ERROR_CATEGORY_CLIENT: category = ErrorCode.Type.CLIENT; break; - case RLM_SYNC_ERROR_CATEGORY_CONNECTION: - category = ErrorCode.Type.PROTOCOL; + case RLM_SYNC_ERROR_CATEGORY_JSON: + category = ErrorCode.Type.JSON; break; - case RLM_SYNC_ERROR_CATEGORY_SESSION: - category = ErrorCode.Type.SESSION; + case RLM_SYNC_ERROR_CATEGORY_SERVICE: + category = ErrorCode.Type.SERVICE; break; - case RLM_SYNC_ERROR_CATEGORY_SYSTEM: - category = ErrorCode.Type.SYSTEM; + case RLM_SYNC_ERROR_CATEGORY_HTTP: + category = ErrorCode.Type.HTTP; break; - default: // RLM_SYNC_ERROR_CATEGORY_UNKNOWN + case RLM_SYNC_ERROR_CATEGORY_CUSTOM: + category = ErrorCode.Type.CUSTOM; + break; + case RLM_SYNC_ERROR_CATEGORY_WEBSOCKET: + category = ErrorCode.Type.WEBSOCKET; + break; + case RLM_SYNC_ERROR_CATEGORY_SYNC: + category = ErrorCode.Type.SYNC; + break; + default: category = ErrorCode.Type.UNKNOWN; } - return category; } } diff --git a/realm/realm-library/src/objectServer/java/io/realm/internal/SyncObjectServerFacade.java b/realm/realm-library/src/objectServer/java/io/realm/internal/SyncObjectServerFacade.java index 075cbce801..66be12df93 100644 --- a/realm/realm-library/src/objectServer/java/io/realm/internal/SyncObjectServerFacade.java +++ b/realm/realm-library/src/objectServer/java/io/realm/internal/SyncObjectServerFacade.java @@ -103,6 +103,10 @@ public Object[] getSyncConfigurationOptions(RealmConfiguration config) { if (config instanceof SyncConfiguration) { SyncConfiguration syncConfig = (SyncConfiguration) config; User user = syncConfig.getUser(); + // Check if user is logged out + if (!user.isLoggedIn()) { + throw new IllegalStateException("User is not logged in: " + user); + } App app = user.getApp(); String rosServerUrl = syncConfig.getServerUrl().toString(); String rosUserIdentity = user.getId(); diff --git a/realm/realm-library/src/objectServer/java/io/realm/internal/network/NetworkRequest.java b/realm/realm-library/src/objectServer/java/io/realm/internal/network/NetworkRequest.java index f877a004d9..5f80833e36 100644 --- a/realm/realm-library/src/objectServer/java/io/realm/internal/network/NetworkRequest.java +++ b/realm/realm-library/src/objectServer/java/io/realm/internal/network/NetworkRequest.java @@ -6,6 +6,8 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReference; +import javax.annotation.Nullable; + import io.realm.internal.ErrorCategory; import io.realm.internal.Keep; import io.realm.internal.objectstore.OsJavaNetworkTransport; @@ -59,14 +61,31 @@ public void onSuccess(Object result) { */ @SuppressWarnings("unused") // Called by JNI @Override - public void onError(byte nativeErrorCategory, int nativeErrorCode, String errorMessage) { + public void onError(byte nativeErrorCategory, int nativeErrorCode, @Nullable String errorMessage, @Nullable String logUrl) { + String formattedErrorMessage = ""; + if (errorMessage != null && !errorMessage.isEmpty()) { + if (formattedErrorMessage.endsWith(".")) { + formattedErrorMessage = errorMessage; + } else { + formattedErrorMessage = errorMessage + "."; + } + } + if (logUrl != null && !logUrl.isEmpty()) { + String logMsg = "Server log entry: " + logUrl; + if (formattedErrorMessage.isEmpty()) { + formattedErrorMessage = logMsg; + } else { + formattedErrorMessage += " " + logMsg; + } + } + ErrorCode code = ErrorCode.fromNativeError(ErrorCategory.toCategory(nativeErrorCategory), nativeErrorCode); if (code == ErrorCode.UNKNOWN) { // In case of UNKNOWN errors parse as much error information on as possible. - String detailedErrorMessage = String.format("{%s::%s} %s", nativeErrorCategory, nativeErrorCode, errorMessage); + String detailedErrorMessage = String.format("{%s::%s} %s", nativeErrorCategory, nativeErrorCode, formattedErrorMessage); error.set(new AppException(code, detailedErrorMessage)); } else { - error.set(new AppException(code, errorMessage)); + error.set(new AppException(code, formattedErrorMessage)); } latch.countDown(); } diff --git a/realm/realm-library/src/objectServer/java/io/realm/internal/objectstore/OsAppCredentials.java b/realm/realm-library/src/objectServer/java/io/realm/internal/objectstore/OsAppCredentials.java index c38d189923..05ce33d7db 100644 --- a/realm/realm-library/src/objectServer/java/io/realm/internal/objectstore/OsAppCredentials.java +++ b/realm/realm-library/src/objectServer/java/io/realm/internal/objectstore/OsAppCredentials.java @@ -32,14 +32,13 @@ public class OsAppCredentials implements NativeObject { private static final int TYPE_ANONYMOUS = 1; private static final int TYPE_API_KEY = 2; - private static final int TYPE_SERVER_API_KEY = 3; - private static final int TYPE_APPLE = 4; - private static final int TYPE_CUSTOM_FUNCTION = 5; - private static final int TYPE_EMAIL_PASSWORD = 6; - private static final int TYPE_FACEBOOK = 7; - private static final int TYPE_JWT = 8; - private static final int TYPE_GOOGLE_AUTH_CODE = 9; - private static final int TYPE_GOOGLE_ID_TOKEN = 10; + private static final int TYPE_APPLE = 3; + private static final int TYPE_CUSTOM_FUNCTION = 4; + private static final int TYPE_EMAIL_PASSWORD = 5; + private static final int TYPE_FACEBOOK = 6; + private static final int TYPE_JWT = 7; + private static final int TYPE_GOOGLE_AUTH_CODE = 8; + private static final int TYPE_GOOGLE_ID_TOKEN = 9; private static final long finalizerPtr = nativeGetFinalizerMethodPtr(); @@ -51,10 +50,6 @@ public static OsAppCredentials apiKey(String key) { return new OsAppCredentials(nativeCreate(TYPE_API_KEY, key)); } - public static OsAppCredentials serverApiKey(String key) { - return new OsAppCredentials(nativeCreate(TYPE_SERVER_API_KEY, key)); - } - public static OsAppCredentials apple(String idToken) { return new OsAppCredentials(nativeCreate(TYPE_APPLE, idToken)); } diff --git a/realm/realm-library/src/objectServer/java/io/realm/internal/objectstore/OsJavaNetworkTransport.java b/realm/realm-library/src/objectServer/java/io/realm/internal/objectstore/OsJavaNetworkTransport.java index 8faa7b47ad..1cb72755e6 100644 --- a/realm/realm-library/src/objectServer/java/io/realm/internal/objectstore/OsJavaNetworkTransport.java +++ b/realm/realm-library/src/objectServer/java/io/realm/internal/objectstore/OsJavaNetworkTransport.java @@ -250,7 +250,7 @@ public String getBody() { @Keep public abstract static class NetworkTransportJNIResultCallback { public void onSuccess(Object result) {} - public void onError(byte nativeErrorCategory, int nativeErrorCode, String errorMessage) {} + public void onError(byte nativeErrorCategory, int nativeErrorCode, String errorMessage, String logUrl) {} } private static native void nativeHandleResponse(Response response, long completionBlockPtr); diff --git a/realm/realm-library/src/objectServer/java/io/realm/internal/objectstore/OsSyncUser.java b/realm/realm-library/src/objectServer/java/io/realm/internal/objectstore/OsSyncUser.java index 10938d02a7..68c36e923f 100644 --- a/realm/realm-library/src/objectServer/java/io/realm/internal/objectstore/OsSyncUser.java +++ b/realm/realm-library/src/objectServer/java/io/realm/internal/objectstore/OsSyncUser.java @@ -87,10 +87,6 @@ public String getIdentity() { return nativeGetIdentity(nativePtr); } - public String getLocalIdentity() { - return nativeGetLocalIdentity(nativePtr); - } - public String getAccessToken() { return nativeGetAccessToken(nativePtr); } @@ -135,10 +131,6 @@ protected void execute(NetworkRequest callback) { }.execute(); } - public void invalidate() { - nativeSetState(nativePtr, STATE_REMOVED); - } - public String getProviderType() { return nativeGetProviderType(nativePtr); } @@ -168,12 +160,10 @@ public int hashCode() { private static native String nativeGetMinAge(long nativePtr); private static native String nativeGetMaxAge(long nativePtr); private static native String nativeGetIdentity(long nativePtr); - private static native String nativeGetLocalIdentity(long nativePtr); private static native String nativeGetAccessToken(long nativePtr); private static native String nativeGetRefreshToken(long nativePtr); private static native String[] nativeGetIdentities(long nativePtr); // Returns pairs of {id, provider} private static native byte nativeGetState(long nativePtr); - private static native void nativeSetState(long nativePtr, byte state); private static native String nativeGetProviderType(long nativePtr); private static native String nativeGetDeviceId(long nativePtr); private static native String nativeCustomData(long nativeUserPtr); diff --git a/realm/realm-library/src/objectServer/java/io/realm/mongodb/ErrorCode.java b/realm/realm-library/src/objectServer/java/io/realm/mongodb/ErrorCode.java index 45d1eb2ecc..540cfd5f3f 100644 --- a/realm/realm-library/src/objectServer/java/io/realm/mongodb/ErrorCode.java +++ b/realm/realm-library/src/objectServer/java/io/realm/mongodb/ErrorCode.java @@ -55,8 +55,30 @@ public enum ErrorCode { EVENT_DESERIALIZING(Type.JAVA, 1200), // Custom Object Store errors - CLIENT_RESET(Type.JAVA, 7), // Client Reset required. Don't change this value without modifying io_realm_internal_OsRealmConfig.cpp - + CLIENT_RESET(Type.CUSTOM, 7), // Client Reset required. Don't change this value without modifying io_realm_internal_OsRealmConfig.cpp + + // Sync errors + // Catch-all sync errors. The error code should be part of the error message. + RUNTIME_ERROR(Type.RUNTIME, 1000), + UNKNOWN_SYNC_ERROR(Type.SYNC, -1), + AUTOMATIC_CLIENT_RESET_FAILED(Type.SYNC, 1028), + BAD_CHANGESET(Type.SYNC, 1015), + BAD_SYNC_PARTITION_VALUE(Type.SYNC, 1029), + CONNECTION_CLOSED(Type.SYNC, 1030, Category.RECOVERABLE), + INVALID_SUBSCRIPTION_QUERY(Type.SYNC, 1031), + SYNC_CLIENT_RESET_REQUIRED(Type.SYNC, 1032), + SYNC_COMPENSATING_WRITE(Type.SYNC, 1033), + SYNC_CONNECT_FAILED(Type.SYNC, 1034), + SYNC_INVALID_SCHEMA_CHANGE(Type.SYNC, 1035), + SYNC_PERMISSION_DENIED(Type.SYNC, 1036), + SYNC_PROTOCOL_INVARIANT_FAILED(Type.SYNC, 1037), + SYNC_PROTOCOL_NEGOTIATION_FAILED(Type.SYNC, 1038), + SYNC_SERVER_PERMISSIONS_CHANGED(Type.SYNC, 1039), + SYNC_USER_MISMATCH(Type.SYNC, 1040), + TLS_HANDSHAKE_FAILED(Type.SYNC, 1041), + WRONG_SYNC_TYPE(Type.SYNC, 1042), + SYNC_WRITE_NOT_ALLOWED(Type.SYNC, 1043), + // // Type.Protocol // @@ -64,57 +86,97 @@ public enum ErrorCode { // // See https://github.com/realm/realm-core/blob/master/src/realm/sync/protocol.hpp#L260 // - CONNECTION_CLOSED(Type.PROTOCOL, 100, Category.RECOVERABLE), // Connection closed (no error) - OTHER_ERROR(Type.PROTOCOL, 101), // Other connection level error + @Deprecated // Use RUNTIME_ERROR instead + OTHER_ERROR(Type.PROTOCOL, 101), // Other connection level error + @Deprecated // Use SYNC_PROTOCOL_INVARIANT_FAILED instead UNKNOWN_MESSAGE(Type.PROTOCOL, 102), // Unknown type of input message + @Deprecated // Use SYNC_PROTOCOL_INVARIANT_FAILED instead BAD_SYNTAX(Type.PROTOCOL, 103), // Bad syntax in input message head + @Deprecated // Is not used anymore LIMITS_EXCEEDED(Type.PROTOCOL, 104), // Limits exceeded in input message + @Deprecated // Use SYNC_PROTOCOL_NEGOTIATION_FAILED instead WRONG_PROTOCOL_VERSION(Type.PROTOCOL, 105), // Wrong protocol version (CLIENT) + @Deprecated // Use SYNC_PROTOCOL_INVARIANT_FAILED instead BAD_SESSION_IDENT(Type.PROTOCOL, 106), // Bad session identifier in input message + @Deprecated // Use SYNC_PROTOCOL_INVARIANT_FAILED instead REUSE_OF_SESSION_IDENT(Type.PROTOCOL, 107), // Overlapping reuse of session identifier (BIND) + @Deprecated // Use SYNC_PROTOCOL_INVARIANT_FAILED instead BOUND_IN_OTHER_SESSION(Type.PROTOCOL, 108), // Client file bound in other session (IDENT) + @Deprecated // Use SYNC_PROTOCOL_INVARIANT_FAILED instead BAD_MESSAGE_ORDER(Type.PROTOCOL, 109), // Bad input message order - BAD_DECOMPRESSION(Type.PROTOCOL, 110), // Error in decompression (UPLOAD) + @Deprecated // Use RUNTIME_ERROR instead + BAD_DECOMPRESSION(Type.PROTOCOL, 110), // Error in decompression (UPLOAD) + @Deprecated // Use SYNC_PROTOCOL_INVARIANT_FAILED instead BAD_CHANGESET_HEADER_SYNTAX(Type.PROTOCOL, 111), // Bad server version in changeset header (DOWNLOAD) + @Deprecated // Use SYNC_PROTOCOL_INVARIANT_FAILED instead BAD_CHANGESET_SIZE(Type.PROTOCOL, 112), // Bad size specified in changeset header (UPLOAD) + @Deprecated // Use BAD_CHANGESET instead BAD_CHANGESETS(Type.PROTOCOL, 113), // Bad changesets (UPLOAD) // Session level errors from the native Sync Client + @Deprecated // Use CONNECTION_CLOSED instead SESSION_CLOSED(Type.SESSION, 200, Category.RECOVERABLE), // Session closed (no error) - OTHER_SESSION_ERROR(Type.SESSION, 201, Category.RECOVERABLE), // Other session level error + @Deprecated // Use RUNTIME_ERROR instead + OTHER_SESSION_ERROR(Type.SESSION, 201, Category.RECOVERABLE), // Other session level error + @Deprecated // Not used anymore TOKEN_EXPIRED(Type.SESSION, 202, Category.RECOVERABLE), // Access token expired // Session fatal: Auth wrong. Cannot be fixed without a new User/SyncConfiguration. + @Deprecated // Not used anymore BAD_AUTHENTICATION(Type.SESSION, 203), // Bad user authentication (BIND, REFRESH) + @Deprecated // Use BAD_SYNC_PARTITION_VALUE instead ILLEGAL_REALM_PATH(Type.SESSION, 204), // Illegal Realm path (BIND) + @Deprecated // Not used anymore NO_SUCH_PATH(Type.SESSION, 205), // No such Realm (BIND) + @Deprecated // Use SYNC_PERMISSION_DENIED instead PERMISSION_DENIED(Type.SESSION, 206), // Permission denied (BIND, REFRESH) // Fatal: Wrong server/client versions. Trying to sync incompatible files or the file was corrupted. + @Deprecated // Not used anymore BAD_SERVER_FILE_IDENT(Type.SESSION, 207), // Bad server file identifier (IDENT) + @Deprecated // Use SYNC_CLIENT_RESET_REQUIRED instead BAD_CLIENT_FILE_IDENT(Type.SESSION, 208), // Bad client file identifier (IDENT) + @Deprecated // Use SYNC_CLIENT_RESET_REQUIRED instead BAD_SERVER_VERSION(Type.SESSION, 209), // Bad server version (IDENT, UPLOAD) + @Deprecated // Use SYNC_CLIENT_RESET_REQUIRED instead BAD_CLIENT_VERSION(Type.SESSION, 210), // Bad client version (IDENT, UPLOAD) + @Deprecated // Use SYNC_CLIENT_RESET_REQUIRED instead DIVERGING_HISTORIES(Type.SESSION, 211), // Diverging histories (IDENT) - BAD_CHANGESET(Type.SESSION, 212), // Bad changeset (UPLOAD) - DISABLED_SESSION(Type.SESSION, 213), // Disabled session + @Deprecated // Not used anymore + DISABLED_SESSION(Type.SESSION, 213), // Disabled session + @Deprecated // Not used anymore PARTIAL_SYNC_DISABLED(Type.SESSION, 214), // Partial sync disabled (BIND) + @Deprecated // Not used anymore UNSUPPORTED_SESSION_FEATURE(Type.SESSION, 215), // Unsupported session-level feature + @Deprecated // Use SYNC_PROTOCOL_INVARIANT_FAILED instead BAD_ORIGIN_FILE_IDENT(Type.SESSION, 216), // Bad origin file identifier (UPLOAD) + @Deprecated // Use SYNC_CLIENT_RESET_REQUIRED instead BAD_CLIENT_FILE(Type.SESSION, 217), // Synchronization no longer possible for client-side file + @Deprecated // Not used anymore SERVER_FILE_DELETED(Type.SESSION, 218), // Server file was deleted while session was bound to it + @Deprecated // Not used anymore CLIENT_FILE_BLACKLISTED(Type.SESSION, 219), // Client file has been blacklisted (IDENT) + @Deprecated // Not used anymore USER_BLACKLISTED(Type.SESSION, 220), // User has been blacklisted (BIND) + @Deprecated // Not used anymore TRANSACT_BEFORE_UPLOAD(Type.SESSION, 221), // Serialized transaction before upload completion + @Deprecated // Use SYNC_CLIENT_RESET_REQUIRED instead CLIENT_FILE_EXPIRED(Type.SESSION, 222), // Client file has expired + @Deprecated // Use SYNC_USER_MISMATCH instead USER_MISMATCH(Type.SESSION, 223), // User mismatch for client file identifier (IDENT) + @Deprecated // Not used anymore TOO_MANY_SESSIONS(Type.SESSION, 224), // Too many sessions in connection (BIND) + @Deprecated // Use INVALID_SCHEMA_CHANGE instead INVALID_SCHEMA_CHANGE(Type.SESSION, 225), // Invalid schema change (UPLOAD) + @Deprecated // Use INVALID_SUBSCRIPTION_QUERY instead BAD_QUERY(Type.SESSION, 226), // Client query is invalid/malformed (IDENT, QUERY) - OBJECT_ALREADY_EXISTS(Type.SESSION, 227), // Client tried to create an object that already exists outside their view (UPLOAD) + @Deprecated // Use SYNC_SERVER_PERMISSIONS_CHANGED instead SERVER_PERMISSIONS_CHANGED(Type.SESSION, 228), // Server permissions for this file ident have changed since the last time it was used (IDENT) + @Deprecated // Use CONNECTION_CLOSED instead INITIAL_SYNC_NOT_COMPLETE(Type.SESSION, 229), // Client tried to open a session before initial sync is complete (BIND) + @Deprecated // Use SYNC_WRITE_NOT_ALLOWED WRITE_NOT_ALLOWED(Type.SESSION, 230), // Client attempted a write that is disallowed by permissions, or modifies an object outside the current query - requires client reset (UPLOAD) + @Deprecated // Use SYNC_COMPENSATING_WRITE instead COMPENSATING_WRITE(Type.SESSION, 231), // Client attempted a write that is disallowed by permissions, or modifies an object outside the current query, and the server undid the change // @@ -123,37 +185,69 @@ public enum ErrorCode { // Sync Network Client errors. // See https://github.com/realm/realm-core/blob/master/src/realm/sync/client_base.hpp#L75 // + @Deprecated // Use CONNECTION_CLOSED instead CLIENT_CONNECTION_CLOSED(Type.CLIENT, 100), // Connection closed (no error) + @Deprecated // Use SYNC_PROTOCOL_INVARIANT_FAILED instead CLIENT_UNKNOWN_MESSAGE(Type.CLIENT, 101), // Unknown type of input message + @Deprecated // Not used anymore CLIENT_LIMITS_EXCEEDED(Type.CLIENT, 103), // Limits exceeded in input message + @Deprecated // Use SYNC_PROTOCOL_INVARIANT_FAILED instead CLIENT_BAD_SESSION_IDENT(Type.CLIENT, 104), // Bad session identifier in input message + @Deprecated // Use SYNC_PROTOCOL_INVARIANT_FAILED instead CLIENT_BAD_MESSAGE_ORDER(Type.CLIENT, 105), // Bad input message order + @Deprecated // Use SYNC_PROTOCOL_INVARIANT_FAILED instead CLIENT_BAD_CLIENT_FILE_IDENT(Type.CLIENT, 106), // Bad client file identifier (IDENT) + @Deprecated // Use SYNC_PROTOCOL_INVARIANT_FAILED instead CLIENT_BAD_PROGRESS(Type.CLIENT, 107), // Bad progress information (DOWNLOAD) + @Deprecated // Use SYNC_PROTOCOL_INVARIANT_FAILED instead CLIENT_BAD_CHANGESET_HEADER_SYNTAX(Type.CLIENT, 108), // Bad syntax in changeset header (DOWNLOAD) + @Deprecated // Use SYNC_PROTOCOL_INVARIANT_FAILED instead CLIENT_BAD_CHANGESET_SIZE(Type.CLIENT, 109), // Bad changeset size in changeset header (DOWNLOAD) + @Deprecated // Use SYNC_PROTOCOL_INVARIANT_FAILED instead CLIENT_BAD_ORIGIN_FILE_IDENT(Type.CLIENT, 110), // Bad origin file identifier in changeset header (DOWNLOAD) + @Deprecated // Use CLIENT_RESET instead CLIENT_BAD_SERVER_VERSION(Type.CLIENT, 111), // Bad server version in changeset header (DOWNLOAD) + @Deprecated // Use BAD_CHANGESET instead CLIENT_BAD_CHANGESET(Type.CLIENT, 112), // Bad changeset (DOWNLOAD) + @Deprecated // Use UNKNOWN_SYNC_ERROR instead CLIENT_BAD_REQUEST_IDENT(Type.CLIENT, 113), // Bad request identifier (MARK) + @Deprecated // Use SYNC_PROTOCOL_INVARIANT_FAILED instead CLIENT_BAD_ERROR_CODE(Type.CLIENT, 114), // Bad error code (ERROR) + @Deprecated // Use UNKNOWN_SYNC_ERROR instead CLIENT_BAD_COMPRESSION(Type.CLIENT, 115), // Bad compression (DOWNLOAD) + @Deprecated // Not used anymore CLIENT_BAD_CLIENT_VERSION_DOWNLOAD(Type.CLIENT, 116), // Bad last integrated client version in changeset header (DOWNLOAD) + @Deprecated // Use TLS_HANDSHAKE_FAILED instead CLIENT_SSL_SERVER_CERT_REJECTED(Type.CLIENT, 117), // SSL server certificate rejected + @Deprecated // Use CONNECTION_CLOSED instead CLIENT_PONG_TIMEOUT(Type.CLIENT, 118), // Timeout on reception of PONG respone message + @Deprecated // Use UNKNOWN_SYNC_ERROR instead CLIENT_BAD_CLIENT_FILE_IDENT_SALT(Type.CLIENT, 119), // Bad client file identifier salt (IDENT) + @Deprecated // Use UNKNOWN_SYNC_ERROR instead CLIENT_FILE_IDENT(Type.CLIENT, 120), // Bad file identifier (ALLOC) + @Deprecated // Use UNKNOWN_SYNC_ERROR instead CLIENT_CONNECT_TIMEOUT(Type.CLIENT, 121), // Sync connection was not fully established in time + @Deprecated // Use UNKNOWN_SYNC_ERROR instead CLIENT_BAD_TIMESTAMP(Type.CLIENT, 122), // Bad timestamp (PONG) + @Deprecated // Use SYNC_PROTOCOL_NEGOTIATION_FAILED instead CLIENT_BAD_PROTOCOL_FROM_SERVER(Type.CLIENT, 123), // Bad or missing protocol version information from server + @Deprecated // Use SYNC_PROTOCOL_NEGOTIATION_FAILED instead CLIENT_TOO_OLD_FOR_SERVER(Type.CLIENT, 124), // Protocol version negotiation failed: Client is too old for server + @Deprecated // Use SYNC_PROTOCOL_NEGOTIATION_FAILED instead CLIENT_TOO_NEW_FOR_SERVER(Type.CLIENT, 125), // Protocol version negotiation failed: Client is too new for server + @Deprecated // Use SYNC_PROTOCOL_NEGOTIATION_FAILED instead CLIENT_PROTOCOL_MISMATCH(Type.CLIENT, 126), // Protocol version negotiation failed: No version supported by both client and server + @Deprecated // Use UNKNOWN_SYNC_ERROR instead CLIENT_BAD_STATE_MESSAGE(Type.CLIENT, 127), // Bad values in state message (STATE) + @Deprecated // Use UNKNOWN_SYNC_ERROR instead CLIENT_MISSING_PROTOCOL_FEATURE(Type.CLIENT, 128), // Requested feature missing in negotiated protocol version + @Deprecated // Use UNKNOWN_SYNC_ERROR instead CLIENT_BAD_SERIAL_TRANSACT_STATUS(Type.CLIENT, 129), // Bad status of serialized transaction (TRANSACT) + @Deprecated // Use UNKNOWN_SYNC_ERROR instead CLIENT_BAD_OBJECT_ID_SUBSTITUTIONS(Type.CLIENT, 130), // Bad encoded object identifier substitutions (TRANSACT) + @Deprecated // Use UNKNOWN_SYNC_ERROR instead CLIENT_HTTP_TUNNEL_FAILED(Type.CLIENT, 131), // Failed to establish HTTP tunnel with configured proxy + @Deprecated // Use AUTOMATIC_CLIENT_RESET_FAILED instead AUTO_CLIENT_RESET_FAILURE(Type.CLIENT, 132), // Automatic client reset failed // @@ -372,27 +466,35 @@ public static ErrorCode fromNativeError(String type, int errorCode) { } public static class Type { + + // Generic errors + public static final String LOGIC = "LogicError"; + public static final String RUNTIME = "RuntimeError"; + public static final String INVALID_ARGUMENT = "IllegalArgumentError"; + public static final String FILE_ACCESS = "FileAccessError"; + public static final String SYSTEM = "realm.basic_system"; // Connection/System errors from the native Sync Client + // App error types + public static final String APP = "realm::app::ClientError"; // Session level errors from the native App Client public static final String JSON = "realm::app::JSONError"; // Errors when parsing JSON public static final String SERVICE = "realm::app::ServiceError"; // MongoDB Realm Response errors public static final String HTTP = "realm::app::HttpError"; // Errors from the HTTP layer + @Deprecated // Use CUSTOM instead. public static final String JAVA = "realm::app::CustomError"; // Errors from the Java layer - - public static final String APP = "realm::app::ClientError"; // Session level errors from the native App Client + public static final String CUSTOM = "realm::app::CustomError"; // Sync error types - - // This one is category CLIENT + @Deprecated // Use SYNC instead public static final String CLIENT = "realm::sync::ClientError"; // Session level errors from the native Sync Client - // connection level errors + @Deprecated // Use SYNC instead public static final String PROTOCOL = "realm::sync::ProtocolError"; // Protocol level errors from the native Sync Client - // session level errors + @Deprecated // Use SYNC instead public static final String SESSION = "realm::sync::Session"; // Session errors from the native Sync Client + public static final String WEBSOCKET = "realm::sync::WebSocketError"; + public static final String SYNC = "realm::sync::SyncError"; - public static final String SYSTEM = "realm.basic_system"; // Connection/System errors from the native Sync Client - - // unknown to client - public static final String UNKNOWN = "unknown"; // Catch-all category + // Catch-all category + public static final String UNKNOWN = "unknown"; } public enum Category { diff --git a/realm/realm-library/src/objectServer/java/io/realm/mongodb/User.java b/realm/realm-library/src/objectServer/java/io/realm/mongodb/User.java index c49de02135..ecfdac8a05 100644 --- a/realm/realm-library/src/objectServer/java/io/realm/mongodb/User.java +++ b/realm/realm-library/src/objectServer/java/io/realm/mongodb/User.java @@ -154,10 +154,14 @@ public List getIdentities() { } /** - * Returns the provider type used to log the user + * Returns the provider type of this users first identity. This is normally the one + * used to log the user in, but not always. * - * @return the provider type of the user + * @return the provider type of the user. + * + * @deprecated Use {@link #getIdentities()} instead. */ + @Deprecated public Credentials.Provider getProviderType() { return Credentials.Provider.fromId(osUser.getProviderType()); } diff --git a/realm/realm-library/src/objectServer/java/io/realm/mongodb/sync/Sync.java b/realm/realm-library/src/objectServer/java/io/realm/mongodb/sync/Sync.java index 26fd1519d6..9ae011c0b7 100644 --- a/realm/realm-library/src/objectServer/java/io/realm/mongodb/sync/Sync.java +++ b/realm/realm-library/src/objectServer/java/io/realm/mongodb/sync/Sync.java @@ -249,7 +249,14 @@ private synchronized void removeSession(SyncConfiguration syncConfiguration) { * session to contact. */ @SuppressWarnings("unused") - private void notifyErrorHandler(byte nativeErrorCategory, int nativeErrorCode, String errorMessage, String clientResetPathInfo, String path) { + private void notifyErrorHandler( + byte nativeErrorCategory, + int nativeErrorCode, + String errorMessage, + String logUrl, + String clientResetPathInfo, + String path + ) { ErrorCode errCode = ErrorCode.fromNativeError(ErrorCategory.toCategory(nativeErrorCategory), nativeErrorCode); if (errCode == ErrorCode.CLIENT_RESET) { diff --git a/realm/realm-library/src/syncIntegrationTest/kotlin/io/realm/SyncSessionTests.kt b/realm/realm-library/src/syncIntegrationTest/kotlin/io/realm/SyncSessionTests.kt index 6665001f48..26ba31e318 100644 --- a/realm/realm-library/src/syncIntegrationTest/kotlin/io/realm/SyncSessionTests.kt +++ b/realm/realm-library/src/syncIntegrationTest/kotlin/io/realm/SyncSessionTests.kt @@ -26,6 +26,7 @@ import org.junit.* import org.junit.Assert.* import org.junit.runner.RunWith import java.io.Closeable +import java.lang.IllegalStateException import java.lang.Thread import java.util.* import java.util.concurrent.CountDownLatch @@ -657,15 +658,14 @@ class SyncSessionTests { realm.executeTransaction { realm.createObject(SyncDog::class.java, ObjectId()) } - - val error = assertFailsWith { - // This throws as the server has NOT been configured to have long partitions! + try { realm.syncSession.uploadAllLocalChanges() - }.also { + fail() + } catch (ex: AppException) { + assertEquals(ErrorCode.Type.SYNC, ex.errorType) + assertEquals(ErrorCode.BAD_SYNC_PARTITION_VALUE, ex.errorCode) looperThread.testComplete() } - assertEquals(ErrorCode.Type.SESSION, error.errorType) - assertEquals(ErrorCode.ILLEGAL_REALM_PATH, error.errorCode) } } } @@ -723,7 +723,9 @@ class SyncSessionTests { @Test fun session_throwOnLogoutUser() { user.logOut() - Realm.getInstance(syncConfiguration).use { } + assertFailsWith { + Realm.getInstance(syncConfiguration).use { } + } } @Test