From 4a7c2f1227556e1b2ed31b78e23806123d733868 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20R=C3=B8rbech?= Date: Tue, 6 Aug 2024 14:34:41 +0200 Subject: [PATCH 1/2] Add support for switching user --- CHANGELOG.md | 2 +- .../kotlin/internal/interop/RealmInterop.kt | 1 + .../kotlin/internal/interop/RealmInterop.kt | 4 ++ .../kotlin/internal/interop/RealmInterop.kt | 4 ++ .../kotlin/io/realm/kotlin/mongodb/App.kt | 9 +++ .../realm/kotlin/mongodb/internal/AppImpl.kt | 5 ++ .../kotlin/test/mongodb/common/AppTests.kt | 64 ++++++++----------- 7 files changed, 49 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a602ae51df..01c287e4c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ * None. ### Enhancements -* None. +* [Sync] Add support for switching users with `App.switchUser(User)`. (Issue [#1813](https://github.com/realm/realm-kotlin/issues/1813)/[RKOTLIN-1115](https://jira.mongodb.org/browse/RKOTLIN-1115)). ### Fixed * None. diff --git a/packages/cinterop/src/commonMain/kotlin/io/realm/kotlin/internal/interop/RealmInterop.kt b/packages/cinterop/src/commonMain/kotlin/io/realm/kotlin/internal/interop/RealmInterop.kt index e012f627d4..de839b4a52 100644 --- a/packages/cinterop/src/commonMain/kotlin/io/realm/kotlin/internal/interop/RealmInterop.kt +++ b/packages/cinterop/src/commonMain/kotlin/io/realm/kotlin/internal/interop/RealmInterop.kt @@ -529,6 +529,7 @@ expect object RealmInterop { fun realm_app_remove_user(app: RealmAppPointer, user: RealmUserPointer, callback: AppCallback) fun realm_app_delete_user(app: RealmAppPointer, user: RealmUserPointer, callback: AppCallback) fun realm_app_link_credentials(app: RealmAppPointer, user: RealmUserPointer, credentials: RealmCredentialsPointer, callback: AppCallback) + fun realm_app_switch_user(app: RealmAppPointer, user: RealmUserPointer) fun realm_clear_cached_apps() fun realm_app_sync_client_get_default_file_path_for_realm( syncConfig: RealmSyncConfigurationPointer, diff --git a/packages/cinterop/src/jvm/kotlin/io/realm/kotlin/internal/interop/RealmInterop.kt b/packages/cinterop/src/jvm/kotlin/io/realm/kotlin/internal/interop/RealmInterop.kt index facb5d5814..e5b291899e 100644 --- a/packages/cinterop/src/jvm/kotlin/io/realm/kotlin/internal/interop/RealmInterop.kt +++ b/packages/cinterop/src/jvm/kotlin/io/realm/kotlin/internal/interop/RealmInterop.kt @@ -1201,6 +1201,10 @@ actual object RealmInterop { realmc.realm_app_link_user(app.cptr(), user.cptr(), credentials.cptr(), callback) } + actual fun realm_app_switch_user(app: RealmAppPointer, user: RealmUserPointer) { + realmc.realm_app_switch_user(app.cptr(), user.cptr()) + } + actual fun realm_app_get_current_user(app: RealmAppPointer): RealmUserPointer? { val ptr = realmc.realm_app_get_current_user(app.cptr()) return nativePointerOrNull(ptr) diff --git a/packages/cinterop/src/nativeDarwin/kotlin/io/realm/kotlin/internal/interop/RealmInterop.kt b/packages/cinterop/src/nativeDarwin/kotlin/io/realm/kotlin/internal/interop/RealmInterop.kt index da0ae06505..171709cb4f 100644 --- a/packages/cinterop/src/nativeDarwin/kotlin/io/realm/kotlin/internal/interop/RealmInterop.kt +++ b/packages/cinterop/src/nativeDarwin/kotlin/io/realm/kotlin/internal/interop/RealmInterop.kt @@ -2357,6 +2357,10 @@ actual object RealmInterop { ) } + actual fun realm_app_switch_user(app: RealmAppPointer, user: RealmUserPointer) { + realm_wrapper.realm_app_switch_user(app.cptr(), user.cptr()) + } + actual fun realm_clear_cached_apps() { realm_wrapper.realm_clear_cached_apps() } diff --git a/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/App.kt b/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/App.kt index e56f4793f2..bbb72a76ba 100644 --- a/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/App.kt +++ b/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/App.kt @@ -121,6 +121,15 @@ public interface App { */ public suspend fun login(credentials: Credentials): User + /** + * Switch current user. + * + * @param user the user that should be the new current user. The user must be one of the users + * that are already logged in. + * @throws IllegalStateException If the [user] is not logged in. + */ + public fun switchUser(user: User) + /** * Create a [Flow] of [AuthenticationChange]-events to receive notifications of updates to all * app user authentication states: login, logout and removal. diff --git a/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/internal/AppImpl.kt b/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/internal/AppImpl.kt index 34c6cc95fb..8bc6ee91c4 100644 --- a/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/internal/AppImpl.kt +++ b/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/internal/AppImpl.kt @@ -163,6 +163,11 @@ public class AppImpl( } } + override fun switchUser(user: User) { + Validation.isType(user) + RealmInterop.realm_app_switch_user(this.nativePointer, user.nativePointer) + } + internal fun reportAuthenticationChange(user: User, change: User.State) { val event: AuthenticationChange = when (change) { User.State.LOGGED_OUT -> LoggedOutImpl(user) diff --git a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/AppTests.kt b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/AppTests.kt index 373678ac1c..04c23304a5 100644 --- a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/AppTests.kt +++ b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/AppTests.kt @@ -62,6 +62,7 @@ import kotlin.test.assertNotEquals import kotlin.test.assertNull import kotlin.test.assertSame import kotlin.test.assertTrue +import kotlin.test.fail class AppTests { @@ -210,30 +211,30 @@ class AppTests { assertTrue(app.allUsers().isEmpty()) } -// @Test -// fun switchUser() { -// val user1: User = app.login(Credentials.anonymous()) -// assertEquals(user1, app.currentUser()) -// val user2: User = app.login(Credentials.anonymous()) -// assertEquals(user2, app.currentUser()) -// -// assertEquals(user1, app.switchUser(user1)) -// assertEquals(user1, app.currentUser()) -// } -// -// @Test -// fun switchUser_throwIfUserNotLoggedIn() = runBlocking { -// val user1 = app.login(Credentials.anonymous()) -// val user2 = app.login(Credentials.anonymous()) -// assertEquals(user2, app.currentUser) -// -// user1.logOut() -// try { -// app.switchUser(user1) -// fail() -// } catch (ignore: IllegalArgumentException) { -// } -// } + @Test + fun switchUser() = runBlocking { + val user1: User = app.login(Credentials.anonymous()) + assertEquals(user1, app.currentUser) + val user2: User = app.login(Credentials.anonymous()) + assertEquals(user2, app.currentUser) + + app.switchUser(user1) + assertEquals(user1, app.currentUser) + } + + @Test + fun switchUser_throwIfUserNotLoggedIn() = runBlocking { + val user1 = app.login(Credentials.anonymous()) + val user2 = app.login(Credentials.anonymous()) + assertEquals(user2, app.currentUser) + + user1.logOut() + try { + app.switchUser(user1) + fail() + } catch (ignore: IllegalStateException) { + } + } @Test fun currentUser_FallbackToNextValidUser() = runBlocking { @@ -262,21 +263,6 @@ class AppTests { assertNull(app.currentUser) } -// @Test -// fun switchUser_nullThrows() { -// try { -// app.switchUser(TestHelper.getNull()) -// fail() -// } catch (ignore: IllegalArgumentException) { -// } -// } -// -// @Ignore("Add this test once we have support for both EmailPassword and ApiKey Auth Providers") -// @Test -// fun switchUser_authProvidersLockUsers() { -// TODO("FIXME") -// } -// @Test fun authenticationChangeAsFlow() = runBlocking { val c = TestChannel() From 8e4c2ddbe077eed3e4bc7990efafb9a6ef4ecac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20R=C3=B8rbech?= Date: Tue, 6 Aug 2024 16:07:37 +0200 Subject: [PATCH 2/2] Fix failing tests --- .../kotlin/io/realm/kotlin/internal/interop/RealmInterop.kt | 2 +- .../io/realm/kotlin/test/mongodb/common/FunctionsTests.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cinterop/src/nativeDarwin/kotlin/io/realm/kotlin/internal/interop/RealmInterop.kt b/packages/cinterop/src/nativeDarwin/kotlin/io/realm/kotlin/internal/interop/RealmInterop.kt index 171709cb4f..323a82962a 100644 --- a/packages/cinterop/src/nativeDarwin/kotlin/io/realm/kotlin/internal/interop/RealmInterop.kt +++ b/packages/cinterop/src/nativeDarwin/kotlin/io/realm/kotlin/internal/interop/RealmInterop.kt @@ -2358,7 +2358,7 @@ actual object RealmInterop { } actual fun realm_app_switch_user(app: RealmAppPointer, user: RealmUserPointer) { - realm_wrapper.realm_app_switch_user(app.cptr(), user.cptr()) + checkedBooleanResult(realm_wrapper.realm_app_switch_user(app.cptr(), user.cptr())) } actual fun realm_clear_cached_apps() { diff --git a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/FunctionsTests.kt b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/FunctionsTests.kt index 20646dd322..39fcab3ce7 100644 --- a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/FunctionsTests.kt +++ b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/FunctionsTests.kt @@ -1020,7 +1020,7 @@ class FunctionsTests { runBlocking { anonUser.logOut() } - assertFailsWithMessage("[Service][Unknown(4351)] unauthorized") { + assertFailsWithMessage("unauthorized") { runBlocking { functions.call(FIRST_ARG_FUNCTION.name, 1, 2, 3) }