diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScope.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScope.kt index 0a1604a92e4..99e714d93db 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScope.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScope.kt @@ -1805,6 +1805,7 @@ class UserSessionScope internal constructor( staleEpochVerifier, eventProcessor, legalHoldHandler, + notificationTokenRepository, this, userScopedLogger, ) diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/debug/DebugScope.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/debug/DebugScope.kt index d86bd11b89b..92d7176faaf 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/debug/DebugScope.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/debug/DebugScope.kt @@ -20,6 +20,7 @@ package com.wire.kalium.logic.feature.debug import com.wire.kalium.logger.KaliumLogger import com.wire.kalium.logic.cache.SelfConversationIdProvider +import com.wire.kalium.logic.configuration.notification.NotificationTokenRepository import com.wire.kalium.logic.data.asset.AssetRepository import com.wire.kalium.logic.data.client.ClientRepository import com.wire.kalium.logic.data.client.MLSClientProvider @@ -54,6 +55,8 @@ import com.wire.kalium.logic.feature.message.StaleEpochVerifier import com.wire.kalium.logic.feature.message.ephemeral.DeleteEphemeralMessageForSelfUserAsReceiverUseCaseImpl import com.wire.kalium.logic.feature.message.ephemeral.DeleteEphemeralMessageForSelfUserAsSenderUseCaseImpl import com.wire.kalium.logic.feature.message.ephemeral.EphemeralMessageDeletionHandlerImpl +import com.wire.kalium.logic.feature.notificationToken.SendFCMTokenUseCase +import com.wire.kalium.logic.feature.notificationToken.SendFCMTokenToAPIUseCaseImpl import com.wire.kalium.logic.sync.SyncManager import com.wire.kalium.logic.sync.incremental.EventProcessor import com.wire.kalium.logic.sync.receiver.handler.legalhold.LegalHoldHandler @@ -87,8 +90,9 @@ class DebugScope internal constructor( private val staleEpochVerifier: StaleEpochVerifier, private val eventProcessor: EventProcessor, private val legalHoldHandler: LegalHoldHandler, + private val notificationTokenRepository: NotificationTokenRepository, private val scope: CoroutineScope, - private val logger: KaliumLogger, + logger: KaliumLogger, internal val dispatcher: KaliumDispatcher = KaliumDispatcherImpl, ) { @@ -179,7 +183,12 @@ class DebugScope internal constructor( messageSendingInterceptor, userRepository, staleEpochVerifier, - { message, expirationData -> ephemeralMessageDeletionHandler.enqueueSelfDeletion(message, expirationData) }, + { message, expirationData -> + ephemeralMessageDeletionHandler.enqueueSelfDeletion( + message, + expirationData + ) + }, scope ) @@ -208,4 +217,11 @@ class DebugScope internal constructor( selfUserId = userId, kaliumLogger = logger ) + + val sendFCMTokenToServer: SendFCMTokenUseCase + get() = SendFCMTokenToAPIUseCaseImpl( + currentClientIdProvider, + clientRepository, + notificationTokenRepository, + ) } diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/notificationToken/SendFCMTokenUseCase.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/notificationToken/SendFCMTokenUseCase.kt new file mode 100644 index 00000000000..653308ec700 --- /dev/null +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/notificationToken/SendFCMTokenUseCase.kt @@ -0,0 +1,93 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.wire.kalium.logic.feature.notificationToken + +import com.wire.kalium.logic.configuration.notification.NotificationTokenRepository +import com.wire.kalium.logic.data.client.ClientRepository +import com.wire.kalium.logic.data.id.CurrentClientIdProvider +import com.wire.kalium.logic.functional.Either +import com.wire.kalium.logic.functional.fold +import com.wire.kalium.logic.functional.getOrNull +import com.wire.kalium.logic.functional.nullableFold + +/** + * Sends to the API locally stored FCM push token + */ +interface SendFCMTokenUseCase { + suspend operator fun invoke(): Either +} + +data class SendFCMTokenError( + val status: Reason, + val error: String? = null, +) { + enum class Reason { + CANT_GET_CLIENT_ID, CANT_GET_NOTIFICATION_TOKEN, CANT_REGISTER_TOKEN, + } +} + +class SendFCMTokenToAPIUseCaseImpl internal constructor( + private val currentClientIdProvider: CurrentClientIdProvider, + private val clientRepository: ClientRepository, + private val notificationTokenRepository: NotificationTokenRepository, +) : SendFCMTokenUseCase { + + override suspend fun invoke(): Either { + val clientIdResult = currentClientIdProvider() + val notificationTokenResult = notificationTokenRepository.getNotificationToken() + + val error: SendFCMTokenError? = clientIdResult.nullableFold( + { SendFCMTokenError(SendFCMTokenError.Reason.CANT_GET_CLIENT_ID, it.toString()) }, + { + notificationTokenResult.nullableFold( + { + SendFCMTokenError( + SendFCMTokenError.Reason.CANT_GET_NOTIFICATION_TOKEN, + it.toString() + ) + }, + { null } + ) + } + ) + + if (error != null) { + return Either.Left(error) + } + + val clientId = clientIdResult.getOrNull()!!.value + val notificationToken = notificationTokenResult.getOrNull()!! + + return clientRepository.registerToken( + senderId = notificationToken.applicationId, + client = clientId, + token = notificationToken.token, + transport = notificationToken.transport + ).fold( + { + Either.Left( + SendFCMTokenError( + SendFCMTokenError.Reason.CANT_REGISTER_TOKEN, + it.toString() + ) + ) + }, + { Either.Right(Unit) } + ) + } +} diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/notificationToken/SendFCMTokenToAPIUseCaseTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/notificationToken/SendFCMTokenToAPIUseCaseTest.kt new file mode 100644 index 00000000000..4b5c4637d88 --- /dev/null +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/notificationToken/SendFCMTokenToAPIUseCaseTest.kt @@ -0,0 +1,161 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.wire.kalium.logic.feature.notificationToken + +import com.wire.kalium.logic.CoreFailure +import com.wire.kalium.logic.NetworkFailure +import com.wire.kalium.logic.StorageFailure +import com.wire.kalium.logic.configuration.notification.NotificationToken +import com.wire.kalium.logic.configuration.notification.NotificationTokenRepository +import com.wire.kalium.logic.data.client.ClientRepository +import com.wire.kalium.logic.data.conversation.ClientId +import com.wire.kalium.logic.data.id.CurrentClientIdProvider +import com.wire.kalium.logic.functional.Either +import com.wire.kalium.logic.functional.fold +import io.mockative.Mock +import io.mockative.any +import io.mockative.coEvery +import io.mockative.every +import io.mockative.mock +import kotlinx.coroutines.test.runTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.fail + +class SendFCMTokenToAPIUseCaseTest { + @Test + fun whenInvokedAndSuccessfulResultInTokenRegistered() = runTest { + + val useCase = + Arrangement() + .withClientId() + .withNotificationToken() + .withClientRepositoryRegisterToken() + .arrange() + + val result = useCase.invoke() + assertEquals(Either.Right(Unit), result) + } + + @Test + fun whenInvokedAndFailureOnClientId() = runTest { + + val useCase = Arrangement() + .withClientIdFailure() + .withNotificationToken() + .arrange() + + val failReason = useCase.invoke().fold( + { it.status }, + { fail("Expected failure, but got success") } + ) + assertEquals(SendFCMTokenError.Reason.CANT_GET_CLIENT_ID, failReason) + + } + + @Test + fun whenInvokedAndFailureOnNotificationToken() = runTest { + + val useCase = Arrangement() + .withClientId() + .withNotificationTokenFailure() + .arrange() + + val failReason = useCase.invoke().fold( + { it.status }, + { fail("Expected failure, but got success") } + ) + assertEquals(SendFCMTokenError.Reason.CANT_GET_NOTIFICATION_TOKEN, failReason) + } + + @Test + fun whenInvokedAndFailureOnClientRepositoryRegisterToken() = runTest { + + val useCase = Arrangement() + .withClientId() + .withNotificationToken() + .withClientRepositoryRegisterTokenFailure() + .arrange() + + val failReason = useCase.invoke().fold( + { it.status }, + { fail("Expected failure, but got success") } + ) + assertEquals(SendFCMTokenError.Reason.CANT_REGISTER_TOKEN, failReason) + } + + + private class Arrangement { + + @Mock + private val currentClientIdProvider: CurrentClientIdProvider = + mock(CurrentClientIdProvider::class) + + @Mock + private val clientRepository: ClientRepository = mock(ClientRepository::class) + + @Mock + private val notificationTokenRepository: NotificationTokenRepository = + mock(NotificationTokenRepository::class) + + + fun arrange(): SendFCMTokenToAPIUseCaseImpl { + return SendFCMTokenToAPIUseCaseImpl( + currentClientIdProvider, clientRepository, notificationTokenRepository + ) + } + + suspend fun withClientId() = apply { + coEvery { + currentClientIdProvider.invoke() + }.returns(Either.Right(ClientId("clientId"))) + } + + suspend fun withClientIdFailure() = apply { + coEvery { + currentClientIdProvider.invoke() + }.returns(Either.Left(CoreFailure.MissingClientRegistration)) + } + + fun withNotificationToken() = apply { + every { + notificationTokenRepository.getNotificationToken() + }.returns(Either.Right(NotificationToken("applicationId", "token", "transport"))) + } + + fun withNotificationTokenFailure() = apply { + every { + notificationTokenRepository.getNotificationToken() + }.returns(Either.Left(StorageFailure.DataNotFound)) + } + + suspend fun withClientRepositoryRegisterToken() = apply { + coEvery { + clientRepository.registerToken(any(), any(), any(), any()) + }.returns(Either.Right(Unit)) + } + + suspend fun withClientRepositoryRegisterTokenFailure() = apply { + coEvery { + clientRepository.registerToken(any(), any(), any(), any()) + }.returns(Either.Left(NetworkFailure.FeatureNotSupported)) + } + + } + +}