All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.io.realm.kotlin.mongodb.internal.UserImpl.kt Maven / Gradle / Ivy

Go to download

Sync Library code for Realm Kotlin. This artifact is not supposed to be consumed directly, but through 'io.realm.kotlin:gradle-plugin:1.6.2' instead.

There is a newer version: 2.3.0
Show newest version
/*
 * Copyright 2021 Realm Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.realm.kotlin.mongodb.internal

import io.realm.kotlin.internal.interop.RealmInterop
import io.realm.kotlin.internal.interop.RealmUserPointer
import io.realm.kotlin.internal.interop.sync.CoreUserState
import io.realm.kotlin.internal.util.use
import io.realm.kotlin.mongodb.AuthenticationProvider
import io.realm.kotlin.mongodb.Credentials
import io.realm.kotlin.mongodb.Functions
import io.realm.kotlin.mongodb.User
import io.realm.kotlin.mongodb.UserIdentity
import io.realm.kotlin.mongodb.auth.ApiKeyAuth
import io.realm.kotlin.mongodb.exceptions.CredentialsCannotBeLinkedException
import io.realm.kotlin.mongodb.exceptions.ServiceException
import kotlinx.coroutines.channels.Channel

// TODO Public due to being a transitive dependency to SyncConfigurationImpl
public class UserImpl(
    public val nativePointer: RealmUserPointer,
    override val app: AppImpl
) : User {
    override val apiKeyAuth: ApiKeyAuth by lazy {
        ApiKeyAuthImpl(app, this)
    }
    override val state: User.State
        get() = fromCoreState(RealmInterop.realm_user_get_state(nativePointer))

    // TODO Can maybe fail, but we could also cache the return value?
    override val identity: String
        get() = id
    override val id: String
        get() = RealmInterop.realm_user_get_identity(nativePointer)
    override val loggedIn: Boolean
        get() = RealmInterop.realm_user_is_logged_in(nativePointer)
    override val provider: AuthenticationProvider
        get() = AuthenticationProviderImpl.fromId(RealmInterop.realm_user_get_auth_provider(nativePointer))
    override val accessToken: String
        get() = RealmInterop.realm_user_get_access_token(nativePointer)
    override val refreshToken: String
        get() = RealmInterop.realm_user_get_refresh_token(nativePointer)
    override val deviceId: String
        get() = RealmInterop.realm_user_get_device_id(nativePointer)
    override val functions: Functions by lazy { FunctionsImpl(app, this) }

    @PublishedApi
    internal fun  profileInternal(block: (ejsonEncodedProfile: String) -> T): T =
        block(RealmInterop.realm_user_get_profile(nativePointer))

    @PublishedApi
    internal fun  customDataInternal(block: (ejsonEncodedCustomData: String) -> T?): T? =
        RealmInterop.realm_user_get_custom_data(nativePointer)?.let(block)

    override suspend fun refreshCustomData() {
        Channel>(1).use { channel ->
            RealmInterop.realm_user_refresh_custom_data(
                app = app.nativePointer,
                user = nativePointer,
                callback = channelResultCallback(channel) {
                    // No-op
                }
            )
            return channel.receive()
                .getOrThrow()
        }
    }

    override val identities: List
        get() = RealmInterop.realm_user_get_all_identities(nativePointer).map {
            UserIdentity(it.id, AuthenticationProviderImpl.fromId(it.provider))
        }

    override suspend fun logOut() {
        Channel>(1).use { channel ->
            val reportLoggedOut = loggedIn
            RealmInterop.realm_app_log_out(
                app.nativePointer,
                nativePointer,
                channelResultCallback(channel) {
                    if (reportLoggedOut) {
                        User.State.LOGGED_OUT
                    } else {
                        null
                    }
                }
            )
            return@use channel.receive()
                .getOrThrow().also { state: User.State? ->
                    if (state != null) {
                        app.reportAuthenticationChange(this, state)
                    }
                }
        }
    }

    override suspend fun remove(): User {
        Channel>(1).use { channel ->
            val reportRemoved = loggedIn
            RealmInterop.realm_app_remove_user(
                app.nativePointer,
                nativePointer,
                channelResultCallback(channel) {
                    if (reportRemoved) {
                        User.State.REMOVED
                    } else {
                        null
                    }
                }
            )
            return@use channel.receive()
                .getOrThrow().also { state: User.State? ->
                    if (state != null) {
                        app.reportAuthenticationChange(this, state)
                    }
                }
        }
        return this
    }

    override suspend fun delete() {
        if (state != User.State.LOGGED_IN) {
            throw IllegalStateException("User must be logged in, in order to be deleted.")
        }
        Channel>(1).use { channel ->
            RealmInterop.realm_app_delete_user(
                app.nativePointer,
                nativePointer,
                channelResultCallback(channel) {
                    User.State.REMOVED
                }
            )
            return@use channel.receive()
                .getOrThrow().also { state: User.State ->
                    app.reportAuthenticationChange(this, state)
                }
        }
    }

    override suspend fun linkCredentials(credentials: Credentials): User {
        if (state != User.State.LOGGED_IN) {
            throw IllegalStateException("User must be logged in, in order to link credentials to it.")
        }
        try {
            Channel>(1).use { channel ->
                RealmInterop.realm_app_link_credentials(
                    app.nativePointer,
                    nativePointer,
                    (credentials as CredentialsImpl).nativePointer,
                    channelResultCallback(channel) { userPointer ->
                        UserImpl(userPointer, app)
                    }
                )
                channel.receive().getOrThrow()
                return this
            }
        } catch (ex: ServiceException) {
            // Linking an account with itself throws a different error code than other linking errors:
            // It is unclear if this error is shared between other error scenarios, so for now,
            // we remap the exception type here instead of in the generic handler in
            // `RealmSyncUtils.kt`.
            if (ex.message?.contains("[Service][InvalidSession(2)] a user already exists with the specified provider.") == true) {
                throw CredentialsCannotBeLinkedException(ex.message!!)
            } else {
                throw ex
            }
        }
    }

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other == null || this::class != other::class) return false

        other as UserImpl

        if (identity != (other.identity)) return false
        return app.configuration == other.app.configuration
    }

    override fun hashCode(): Int {
        var result = identity.hashCode()
        result = 31 * result + app.configuration.appId.hashCode()
        return result
    }

    public companion object {
        /**
         * Converts a Core state value to a library state value.
         *
         * For internal use only.
         */
        public fun fromCoreState(coreState: CoreUserState): User.State = when (coreState) {
            CoreUserState.RLM_USER_STATE_LOGGED_OUT -> User.State.LOGGED_OUT
            CoreUserState.RLM_USER_STATE_LOGGED_IN -> User.State.LOGGED_IN
            CoreUserState.RLM_USER_STATE_REMOVED -> User.State.REMOVED
            else -> throw IllegalArgumentException("Invalid user state: ${coreState.name}")
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy