Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: harden ClientCapabilityDTO for api v7 [WPB-14882] #3151

Open
wants to merge 16 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,9 @@ enum class DeviceType {
Unknown;
}

enum class ClientCapability {
LegalHoldImplicitConsent;
sealed class ClientCapability {
data object LegalHoldImplicitConsent : ClientCapability()
data class Unknown(val name: String) : ClientCapability()
}

data class OtherUserClient(
Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ pbandk = "0.14.2"
turbine = "1.1.0"
avs = "9.10.16"
jna = "5.14.0"
core-crypto = "2.0.0"
core-crypto = "3.0.0"
core-crypto-multiplatform = "0.6.0-rc.3-multiplatform-pre1"
completeKotlin = "1.1.0"
desugar-jdk = "2.0.4"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ interface ServerConfigRepository {
*/
suspend fun configForUser(userId: UserId): Either<StorageFailure, ServerConfig>
suspend fun commonApiVersion(domain: String): Either<CoreFailure, Int>
suspend fun getTeamUrlForUser(userId: UserId): String?
}

@Suppress("LongParameterList", "TooManyFunctions")
Expand Down Expand Up @@ -165,4 +166,6 @@ internal class ServerConfigDataSource(
is ApiVersionDTO.Valid -> Either.Right(it)
}
}.map { serverConfigMapper.fromDTO(it) }

override suspend fun getTeamUrlForUser(userId: UserId): String? = dao.teamUrlForUser(userId.toDao())
}
Original file line number Diff line number Diff line change
Expand Up @@ -198,10 +198,12 @@ class ClientMapper(

private fun toClientCapabilityDTO(clientCapability: ClientCapability): ClientCapabilityDTO = when (clientCapability) {
ClientCapability.LegalHoldImplicitConsent -> ClientCapabilityDTO.LegalHoldImplicitConsent
is ClientCapability.Unknown -> ClientCapabilityDTO.Unknown(clientCapability.name)
}

private fun fromClientCapabilityDTO(clientCapabilityDTO: ClientCapabilityDTO): ClientCapability = when (clientCapabilityDTO) {
ClientCapabilityDTO.LegalHoldImplicitConsent -> ClientCapability.LegalHoldImplicitConsent
is ClientCapabilityDTO.Unknown -> ClientCapability.Unknown(clientCapabilityDTO.name)
}

fun fromOtherUsersClientsDTO(otherUsersClients: List<ClientEntity>): List<OtherUserClient> =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ import com.wire.kalium.logic.feature.selfDeletingMessages.ObserveSelfDeletionTim
import com.wire.kalium.logic.feature.selfDeletingMessages.ObserveTeamSettingsSelfDeletingStatusUseCase
import com.wire.kalium.logic.feature.selfDeletingMessages.ObserveTeamSettingsSelfDeletingStatusUseCaseImpl
import com.wire.kalium.logic.feature.selfDeletingMessages.PersistNewSelfDeletionTimerUseCaseImpl
import com.wire.kalium.logic.feature.server.GetTeamUrlUseCase
import com.wire.kalium.logic.feature.service.ServiceScope
import com.wire.kalium.logic.feature.session.GetProxyCredentialsUseCase
import com.wire.kalium.logic.feature.session.GetProxyCredentialsUseCaseImpl
Expand Down Expand Up @@ -2144,6 +2145,13 @@ class UserSessionScope internal constructor(
kaliumLogger = userScopedLogger,
)

val getTeamUrlUseCase: GetTeamUrlUseCase by lazy {
GetTeamUrlUseCase(
userId,
authenticationScope.serverConfigRepository,
)
}

/**
* This will start subscribers of observable work per user session, as long as the user is logged in.
* When the user logs out, this work will be canceled.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* 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.server

import com.wire.kalium.logic.configuration.server.ServerConfigRepository
import com.wire.kalium.logic.data.user.UserId

/**
* Use case to get the team url for the current user.
*/
class GetTeamUrlUseCase internal constructor(
private val selfUserId: UserId,
private val serverConfigRepository: ServerConfigRepository
) {
suspend operator fun invoke(): String = serverConfigRepository.getTeamUrlForUser(selfUserId) ?: ""
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ internal class UserPropertiesEventReceiverImpl internal constructor(
}

is Event.UserProperty.FoldersUpdate -> {
handleFoldersUpdate(event)
Either.Right(Unit)
// TODO will be handled in 4.11.0
// handleFoldersUpdate(event)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ import com.wire.kalium.logic.framework.TestEvent
import com.wire.kalium.logic.functional.Either
import io.mockative.Mock
import io.mockative.any
import io.mockative.coEvery
import io.mockative.coVerify
import io.mockative.every
import io.mockative.mock
import io.mockative.once
Expand All @@ -49,19 +47,19 @@ class UserPropertiesEventReceiverTest {
}.wasInvoked(exactly = once)
}

@Test
fun givenFoldersUpdateEvent_repositoryIsInvoked() = runTest {
val event = TestEvent.foldersUpdate()
val (arrangement, eventReceiver) = Arrangement()
.withUpdateConversationFolders()
.arrange()

eventReceiver.onEvent(event, TestEvent.liveDeliveryInfo)

coVerify {
arrangement.conversationFolderRepository.updateConversationFolders(any())
}.wasInvoked(exactly = once)
}
// @Test
// fun givenFoldersUpdateEvent_repositoryIsInvoked() = runTest {
// val event = TestEvent.foldersUpdate()
// val (arrangement, eventReceiver) = Arrangement()
// .withUpdateConversationFolders()
// .arrange()
//
// eventReceiver.onEvent(event, TestEvent.liveDeliveryInfo)
//
// coVerify {
// arrangement.conversationFolderRepository.updateConversationFolders(any())
// }.wasInvoked(exactly = once)
// }

private class Arrangement {

Expand All @@ -82,11 +80,11 @@ class UserPropertiesEventReceiverTest {
}.returns(Either.Right(Unit))
}

suspend fun withUpdateConversationFolders() = apply {
coEvery {
conversationFolderRepository.updateConversationFolders(any())
}.returns(Either.Right(Unit))
}
// suspend fun withUpdateConversationFolders() = apply {
// coEvery {
// conversationFolderRepository.updateConversationFolders(any())
// }.returns(Either.Right(Unit))
// }

fun arrange() = this to userPropertiesEventReceiver
}
Expand Down
6 changes: 6 additions & 0 deletions network-model/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ kotlin {

}
}
val commonTest by getting {
dependencies {
// mocks
implementation(project(":mocks"))
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,14 @@ import com.wire.kalium.network.api.authenticated.client.DeviceTypeDTO.Unknown
import com.wire.kalium.network.api.authenticated.prekey.PreKeyDTO
import com.wire.kalium.network.api.model.MLSPublicKey
import com.wire.kalium.network.api.model.UserId
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder

@Serializable
data class RegisterClientRequest(
Expand Down Expand Up @@ -86,12 +92,33 @@ enum class DeviceTypeDTO {
}
}

@Serializable
enum class ClientCapabilityDTO {
@Serializable(with = ClientCapabilityDTOSerializer::class)
sealed class ClientCapabilityDTO {
@SerialName("legalhold-implicit-consent")
LegalHoldImplicitConsent {
override fun toString(): String {
return "legalhold-implicit-consent"
data object LegalHoldImplicitConsent : ClientCapabilityDTO()
data class Unknown(val name: String) : ClientCapabilityDTO()
}

object ClientCapabilityDTOSerializer : KSerializer<ClientCapabilityDTO> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor(
serialName = "ClientCapabilityDTO",
kind = PrimitiveKind.STRING
)

override fun serialize(encoder: Encoder, value: ClientCapabilityDTO) {
when (value) {
is ClientCapabilityDTO.LegalHoldImplicitConsent ->
encoder.encodeString("legalhold-implicit-consent")

is ClientCapabilityDTO.Unknown ->
encoder.encodeString(value.name)
}
}

override fun deserialize(decoder: Decoder): ClientCapabilityDTO {
return when (val value = decoder.decodeString()) {
"legalhold-implicit-consent" -> ClientCapabilityDTO.LegalHoldImplicitConsent
else -> ClientCapabilityDTO.Unknown(value)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ data class ConversationResponse(
@Serializable
data class ConversationResponseV3(
@SerialName("creator")
val creator: String,
val creator: String?,

@SerialName("members")
val members: ConversationMembersResponse,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* 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.network.api.authenticated.client

import kotlinx.serialization.SerializationException
import kotlinx.serialization.json.Json
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith


class ClientCapabilityDTOSerializerTest {

private val json = Json { ignoreUnknownKeys = true }

@Test
fun serialize_legal_hold_implicit_consent() {
val capability = ClientCapabilityDTO.LegalHoldImplicitConsent
val result = json.encodeToString(ClientCapabilityDTO.serializer(), capability)
assertEquals("\"legalhold-implicit-consent\"", result)
}

@Test
fun serialize_unknown_capability() {
val capability = ClientCapabilityDTO.Unknown("custom-capability")
val result = json.encodeToString(ClientCapabilityDTO.serializer(), capability)
assertEquals("\"custom-capability\"", result)
}

@Test
fun deserialize_legal_hold_implicit_consent() {
val jsonString = "\"legalhold-implicit-consent\""
val result = json.decodeFromString(ClientCapabilityDTO.serializer(), jsonString)
assertEquals(ClientCapabilityDTO.LegalHoldImplicitConsent, result)
}

@Test
fun deserialize_unknown_capability() {
val jsonString = "\"unknown-capability\""
val result = json.decodeFromString(ClientCapabilityDTO.serializer(), jsonString)
assertEquals(ClientCapabilityDTO.Unknown("unknown-capability"), result)
}

@Test
fun deserialization_fails_on_invalid_json_format() {
val invalidJson = "12345" // Invalid format for a string
assertFailsWith<SerializationException> {
json.decodeFromString(ClientCapabilityDTO.serializer(), invalidJson)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ SELECT * FROM ServerConfiguration WHERE title = ? AND apiBaseUrl = ? AND webSock
getByUser:
SELECT * FROM ServerConfiguration WHERE id = (SELECT server_config_id FROM Accounts WHERE id = :userId);

getTeamUrlByUser:
SELECT teamsUrl FROM ServerConfiguration WHERE id = (SELECT server_config_id FROM Accounts WHERE id = :userId);

getServerConfigsWithAccIdWithLastCheckBeforeDate:
SELECT sc.*, acc.id
FROM Accounts AS acc LEFT JOIN ServerConfiguration AS sc ON acc.server_config_id == sc.id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ internal object ServerConfigMapper {
)
}

@Suppress("TooManyFunctions")
interface ServerConfigurationDAO {
suspend fun deleteById(id: String)
suspend fun insert(insertData: InsertData)
Expand All @@ -129,6 +130,7 @@ interface ServerConfigurationDAO {
suspend fun updateServerMetaData(id: String, federation: Boolean, commonApiVersion: Int)
suspend fun updateApiVersionAndDomain(id: String, domain: String, commonApiVersion: Int)
suspend fun configForUser(userId: UserIDEntity): ServerConfigEntity?
suspend fun teamUrlForUser(userId: UserIDEntity): String?
suspend fun setFederationToTrue(id: String)
suspend fun getServerConfigsWithAccIdWithLastCheckBeforeDate(date: String): Flow<List<ServerConfigWithUserIdEntity>>
suspend fun updateBlackListCheckDate(configIds: Set<String>, date: String)
Expand All @@ -152,6 +154,7 @@ interface ServerConfigurationDAO {
)
}

@Suppress("TooManyFunctions")
internal class ServerConfigurationDAOImpl internal constructor(
private val queries: ServerConfigurationQueries,
private val queriesContext: CoroutineContext,
Expand Down Expand Up @@ -240,4 +243,8 @@ internal class ServerConfigurationDAOImpl internal constructor(
override suspend fun updateBlackListCheckDate(configIds: Set<String>, date: String) = withContext(queriesContext) {
queries.updateLastBlackListCheckByIds(date, configIds)
}

override suspend fun teamUrlForUser(userId: UserIDEntity): String? = withContext(queriesContext) {
queries.getTeamUrlByUser(userId).executeAsOneOrNull()
}
}
Loading