Repository: wireapp/kalium
Pull request #3349: feat(channels): Add usecase to ckeck if user is allowed to add participants (WPB-15885)
Author: login: ohassine
Labels: name: 🚨 Potential breaking changes
name: type: feature ✨
name: 👕 size: M
Created: 2025-03-27T11:46:05Z | Merged: 2025-04-02T10:21:17Z

<!--do not remove this marker, its needed to replace info when ticket title is updated -->
<!--jira-description-action-hidden-marker-start-->

<table>
<td>
  <a href="https://wearezeta.atlassian.net/browse/WPB-15885" title="WPB-15885" target="_blank"><img alt="Story" src="https://wearezeta.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10815?size=medium" />WPB-15885</a>  [Android] Channel Details - Members can add other members if options allow
  </td></table>
  <br />
 

<!--jira-description-action-hidden-marker-end-->
<!--do not remove this marker, its needed to replace info when ticket title is updated -->

<!--do not remove this marker, its needed to replace info when ticket title is updated -->

<!--do not remove this marker, its needed to replace info when ticket title is updated -->

<!--do not remove this marker, its needed to replace info when ticket title is updated -->

<!--do not remove this marker, its needed to replace info when ticket title is updated -->

<!--do not remove this marker, its needed to replace info when ticket title is updated -->

----
#### PR Submission Checklist for internal contributors

- The **PR Title**
  - [ ] conforms to the style of semantic commits messages¹ supported in Wire's Github Workflow²
  - [ ] contains a reference JIRA issue number like `SQPIT-764`
  - [ ] answers the question: _If merged, this PR will: ..._ ³

- The **PR Description**
  - [ ] is free of optional paragraphs and you have filled the relevant parts to the best of your ability
----

# What's new in this PR?

### Issues

Add usecase to ckeck if user is allowed to add participants 

This PR contains changes from https://github.com/wireapp/kalium/pull/3348, please review that one first.

Needs releases with:

- [ ] GitHub link to other pull request

### Testing

#### Test Coverage (Optional)

- [ ] I have added automated test to this contribution

#### How to Test

_Briefly describe how this change was tested and if applicable the exact steps taken to verify that it works as expected._

### Notes (Optional)

_Specify here any other facts that you think are important for this issue._

### Attachments (Optional)

_Attachments like images, videos, etc. (drag and drop in the text box)_

----
#### PR Post Submission Checklist for internal contributors (Optional)

 - [ ] Wire's Github Workflow has automatically linked the PR to a JIRA issue
----
#### PR Post Merge Checklist for internal contributors

 - [ ] If any soft of configuration variable was introduced by this PR, it has been added to the relevant documents and the CI jobs have been updated.
----
##### References
1. https://sparkbox.com/foundry/semantic_commit_messages
1. https://github.com/wireapp/.github#usage
1. E.g. `feat(conversation-list): Sort conversations by most emojis in the title #SQPIT-764`.



Diff:
diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationRepository.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationRepository.kt
index c59f5dc8316f..e61c4ad21495 100644
--- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationRepository.kt
+++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationRepository.kt
@@ -100,6 +100,8 @@ interface ConversationRepository {
     suspend fun getConversationById(conversationId: ConversationId): Either<StorageFailure, Conversation>
     suspend fun getConversationTypeById(conversationId: ConversationId): Either<StorageFailure, Conversation.Type>
 
+    // TODO: rename it to getChannelAddUserPermission
+    suspend fun getChannelAddPermission(conversationId: ConversationId): Either<StorageFailure, ChannelAddPermission>
     // endregion
 
     @DelicateKaliumApi("This function does not get values from cache")
@@ -1205,6 +1207,11 @@ internal class ConversationDataSource internal constructor(
         conversationDAO.updateChannelAddPermission(conversationId.toDao(), channelAddPermission.toDaoChannelPermission())
     }
 
+    override suspend fun getChannelAddPermission(conversationId: ConversationId): Either<StorageFailure, ChannelAddPermission> =
+        wrapStorageRequest {
+            conversationDAO.getChannelAddPermission(conversationId.toDao()).toModelChannelPermission()
+        }
+
     companion object {
         const val DEFAULT_MEMBER_ROLE = "wire_member"
         private const val CONVERSATIONS_TO_DELETE_KEY = "conversations_to_delete"
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 95aaed817b77..0632ee2bbba9 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
@@ -1855,6 +1855,7 @@ class UserSessionScope internal constructor(
 
     val channels: ChannelsScope by lazy {
         ChannelsScope(
+            { users.getSelfUser },
             { conversationRepository },
             { userStorage.database.metadataDAO },
             { userRepository }
diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/channels/ChannelsScope.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/channels/ChannelsScope.kt
index 7f7b71354f16..17e52e4e6f07 100644
--- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/channels/ChannelsScope.kt
+++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/channels/ChannelsScope.kt
@@ -20,11 +20,15 @@ package com.wire.kalium.logic.feature.channels
 import com.wire.kalium.logic.configuration.ChannelsConfigurationStorage
 import com.wire.kalium.logic.data.conversation.ConversationRepository
 import com.wire.kalium.logic.data.user.SelfUserObservationProvider
+import com.wire.kalium.logic.feature.conversation.channel.IsSelfEligibleToAddParticipantsToChannelUseCase
+import com.wire.kalium.logic.feature.conversation.channel.IsSelfEligibleToAddParticipantsToChannelUseCaseImpl
 import com.wire.kalium.logic.feature.conversation.channel.UpdateChannelAddPermissionUseCase
 import com.wire.kalium.logic.feature.conversation.channel.UpdateChannelAddPermissionUseCaseImpl
+import com.wire.kalium.logic.feature.user.GetSelfUserUseCase
 import com.wire.kalium.persistence.dao.MetadataDAO
 
 class ChannelsScope(
+    val selfUser: () -> GetSelfUserUseCase,
     val conversationRepository: () -> ConversationRepository,
     val metadataDaoProvider: () -> MetadataDAO,
     val selfUserObservationProvider: () -> SelfUserObservationProvider
@@ -40,4 +44,7 @@ class ChannelsScope(
 
     val updateChannelAddPermission: UpdateChannelAddPermissionUseCase
         get() = UpdateChannelAddPermissionUseCaseImpl(conversationRepository())
+
+    val isSelfEligibleToAddParticipantsToChannel: IsSelfEligibleToAddParticipantsToChannelUseCase
+        get() = IsSelfEligibleToAddParticipantsToChannelUseCaseImpl(selfUser(), conversationRepository())
 }
diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/conversation/channel/IsSelfEligibleToAddParticipantsToChannelUseCase.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/conversation/channel/IsSelfEligibleToAddParticipantsToChannelUseCase.kt
new file mode 100644
index 000000000000..9071039f0b33
--- /dev/null
+++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/conversation/channel/IsSelfEligibleToAddParticipantsToChannelUseCase.kt
@@ -0,0 +1,54 @@
+/*
+ * Wire
+ * Copyright (C) 2025 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.conversation.channel
+
+import com.wire.kalium.common.functional.fold
+import com.wire.kalium.logic.data.conversation.ConversationDetails
+import com.wire.kalium.logic.data.conversation.ConversationRepository
+import com.wire.kalium.logic.data.id.ConversationId
+import com.wire.kalium.logic.data.user.type.UserType
+import com.wire.kalium.logic.feature.user.GetSelfUserUseCase
+
+/**
+ * Use case to check if the self user is eligible to add participants to a channel.
+ */
+interface IsSelfEligibleToAddParticipantsToChannelUseCase {
+    suspend operator fun invoke(conversationId: ConversationId): Boolean
+}
+
+internal class IsSelfEligibleToAddParticipantsToChannelUseCaseImpl(
+    val selfUser: GetSelfUserUseCase,
+    val conversationRepository: ConversationRepository
+) : IsSelfEligibleToAddParticipantsToChannelUseCase {
+    override suspend operator fun invoke(conversationId: ConversationId): Boolean =
+        conversationRepository.getChannelAddPermission(conversationId).fold(
+            { false },
+            {
+                val eligibleUserTypes: Set<UserType> = when (it) {
+                    ConversationDetails.Group.Channel.ChannelAddPermission.ADMINS -> setOf(UserType.ADMIN, UserType.OWNER)
+                    ConversationDetails.Group.Channel.ChannelAddPermission.EVERYONE -> setOf(
+                        UserType.ADMIN,
+                        UserType.OWNER,
+                        UserType.INTERNAL
+                    )
+                }
+
+                selfUser()?.userType in eligibleUserTypes
+            }
+        )
+}
diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/conversation/channel/IsSelfEligibleToAddParticipantsToChannelUseCaseTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/conversation/channel/IsSelfEligibleToAddParticipantsToChannelUseCaseTest.kt
new file mode 100644
index 000000000000..7b178c31e0c3
--- /dev/null
+++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/conversation/channel/IsSelfEligibleToAddParticipantsToChannelUseCaseTest.kt
@@ -0,0 +1,145 @@
+/*
+ * Wire
+ * Copyright (C) 2025 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.conversation.channel
+
+import com.wire.kalium.common.error.StorageFailure
+import com.wire.kalium.common.functional.Either
+import com.wire.kalium.logic.data.conversation.ConversationDetails.Group.Channel.ChannelAddPermission
+import com.wire.kalium.logic.data.conversation.ConversationRepository
+import com.wire.kalium.logic.data.id.ConversationId
+import com.wire.kalium.logic.data.user.type.UserType
+import com.wire.kalium.logic.feature.user.GetSelfUserUseCase
+import com.wire.kalium.logic.framework.TestUser
+import io.mockative.Mock
+import io.mockative.any
+import io.mockative.coEvery
+import io.mockative.coVerify
+import io.mockative.mock
+import io.mockative.once
+import kotlinx.coroutines.test.runTest
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class IsSelfEligibleToAddParticipantsToChannelUseCaseTest {
+
+    @Test
+    fun givenErrorOccurs_whenRetrievingPermission_thenResultShouldBeFalse() = runTest {
+        val (arrangement, useCase) = Arrangement()
+            .withGetChannelAddPermissionReturning(Either.Left(StorageFailure.DataNotFound))
+            .arrange()
+
+        val result = useCase(conversationId)
+
+        assertEquals(false, result)
+        coVerify { arrangement.conversationRepository.getChannelAddPermission(any()) }.wasInvoked(exactly = once)
+    }
+
+    @Test
+    fun givenUserIsAdminAndPermissionIsADMINS_whenCheckingEligibility_thenResultShouldBeTrue() = runTest {
+        val (arrangement, useCase) = Arrangement()
+            .withGetChannelAddPermissionReturning(Either.Right(ChannelAddPermission.ADMINS))
+            .withGetUserTypeReturning(UserType.ADMIN)
+            .arrange()
+
+        val result = useCase(conversationId)
+
+        assertEquals(true, result)
+        coVerify { arrangement.conversationRepository.getChannelAddPermission(any()) }.wasInvoked(exactly = once)
+    }
+
+    @Test
+    fun givenUserIsMemberAndPermissionIsADMINS_whenCheckingEligibility_thenResultShouldBeFalse() = runTest {
+        val (arrangement, useCase) = Arrangement()
+            .withGetChannelAddPermissionReturning(Either.Right(ChannelAddPermission.ADMINS))
+            .withGetUserTypeReturning(UserType.INTERNAL)
+            .arrange()
+
+        val result = useCase(conversationId)
+
+        assertEquals(false, result)
+        coVerify { arrangement.conversationRepository.getChannelAddPermission(any()) }.wasInvoked(exactly = once)
+    }
+
+    @Test
+    fun givenUserIsMemberAndPermissionIsEVERYONE_whenCheckingEligibility_thenResultShouldBeTrue() = runTest {
+        val (arrangement, useCase) = Arrangement()
+            .withGetChannelAddPermissionReturning(Either.Right(ChannelAddPermission.EVERYONE))
+            .withGetUserTypeReturning(UserType.INTERNAL)
+            .arrange()
+
+        val result = useCase(conversationId)
+
+        assertEquals(true, result)
+        coVerify { arrangement.conversationRepository.getChannelAddPermission(any()) }.wasInvoked(exactly = once)
+    }
+
+    @Test
+    fun givenUserIsOwnerAndPermissionIsEVERYONE_whenCheckingEligibility_thenResultShouldBeTrue() = runTest {
+        val (arrangement, useCase) = Arrangement()
+            .withGetChannelAddPermissionReturning(Either.Right(ChannelAddPermission.EVERYONE))
+            .withGetUserTypeReturning(UserType.OWNER)
+            .arrange()
+
+        val result = useCase(conversationId)
+
+        assertEquals(true, result)
+        coVerify { arrangement.conversationRepository.getChannelAddPermission(any()) }.wasInvoked(exactly = once)
+    }
+
+    @Test
+    fun givenUserIsInternalAndPermissionIsADMINS_whenCheckingEligibility_thenResultShouldBeFalse() = runTest {
+        val (arrangement, useCase) = Arrangement()
+            .withGetChannelAddPermissionReturning(Either.Right(ChannelAddPermission.ADMINS))
+            .withGetUserTypeReturning(UserType.INTERNAL)
+            .arrange()
+
+        val result = useCase(conversationId)
+
+        assertEquals(false, result)
+        coVerify { arrangement.conversationRepository.getChannelAddPermission(any()) }.wasInvoked(exactly = once)
+    }
+
+    private class Arrangement {
+        @Mock
+        val getSelfUser = mock(GetSelfUserUseCase::class)
+
+        @Mock
+        val conversationRepository = mock(ConversationRepository::class)
+
+        private val isSelfEligibleToAddParticipantsToChannel =
+            IsSelfEligibleToAddParticipantsToChannelUseCaseImpl(getSelfUser, conversationRepository)
+
+        suspend fun withGetChannelAddPermissionReturning(result: Either<StorageFailure, ChannelAddPermission>) = apply {
+            coEvery {
+                conversationRepository.getChannelAddPermission(any())
+            }.returns(result)
+        }
+
+        suspend fun withGetUserTypeReturning(userType: UserType) = apply {
+            coEvery {
+                getSelfUser()
+            }.returns(TestUser.SELF.copy(userType = userType))
+        }
+
+        fun arrange(block: Arrangement.() -> Unit = { }) = apply(block).let { this to isSelfEligibleToAddParticipantsToChannel }
+    }
+
+    companion object {
+        private val conversationId = ConversationId("value", "domain")
+    }
+}
diff --git a/persistence/src/commonMain/db_user/com/wire/kalium/persistence/Conversations.sq b/persistence/src/commonMain/db_user/com/wire/kalium/persistence/Conversations.sq
index 368dd03e3a68..0e5122c04d6c 100644
--- a/persistence/src/commonMain/db_user/com/wire/kalium/persistence/Conversations.sq
+++ b/persistence/src/commonMain/db_user/com/wire/kalium/persistence/Conversations.sq
@@ -345,6 +345,9 @@ WHERE type IS NOT 'SELF' AND incomplete_metadata = 1;
 selectIsChannel:
 SELECT is_channel FROM Conversation WHERE qualified_id = ?;
 
+selectChannelAddPermission:
+SELECT channel_add_permission FROM Conversation WHERE qualified_id = ?;
+
 updateMlsVerificationStatus:
 UPDATE Conversation
 SET verification_status = :status
diff --git a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/conversation/ConversationDAO.kt b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/conversation/ConversationDAO.kt
index 23a698c6d7e4..86afde2cf075 100644
--- a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/conversation/ConversationDAO.kt
+++ b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/conversation/ConversationDAO.kt
@@ -39,6 +39,7 @@ interface ConversationDAO {
     suspend fun getConversationDetailsById(qualifiedID: QualifiedIDEntity): ConversationViewEntity?
     suspend fun observeConversationDetailsById(conversationId: QualifiedIDEntity): Flow<ConversationViewEntity?>
     suspend fun isAChannel(conversationId: QualifiedIDEntity): Boolean
+    suspend fun getChannelAddPermission(conversationId: QualifiedIDEntity): ConversationEntity.ChannelAddPermission
     //endregion
 
     suspend fun getSelfConversationId(protocol: ConversationEntity.Protocol): QualifiedIDEntity?
diff --git a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/conversation/ConversationDAOImpl.kt b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/conversation/ConversationDAOImpl.kt
index 567bd2bfe15b..493182310170 100644
--- a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/conversation/ConversationDAOImpl.kt
+++ b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/conversation/ConversationDAOImpl.kt
@@ -19,9 +19,9 @@
 package com.wire.kalium.persistence.dao.conversation
 
 import app.cash.sqldelight.coroutines.asFlow
-import com.wire.kalium.persistence.ConversationsQueries
 import com.wire.kalium.persistence.ConversationDetailsQueries
 import com.wire.kalium.persistence.ConversationDetailsWithEventsQueries
+import com.wire.kalium.persistence.ConversationsQueries
 import com.wire.kalium.persistence.MembersQueries
 import com.wire.kalium.persistence.UnreadEventsQueries
 import com.wire.kalium.persistence.cache.FlowCache
@@ -531,6 +531,13 @@ internal class ConversationDAOImpl internal constructor(
         conversationQueries.selectIsChannel(conversationId).executeAsOneOrNull() ?: false
     }
 
+    override suspend fun getChannelAddPermission(conversationId: QualifiedIDEntity): ConversationEntity.ChannelAddPermission {
+        return withContext(coroutineContext) {
+            conversationQueries.selectChannelAddPermission(conversationId).executeAsOneOrNull()?.channel_add_permission
+                ?: ConversationEntity.ChannelAddPermission.ADMINS
+        }
+    }
+
     override suspend fun updateChannelAddPermission(
         conversationId: QualifiedIDEntity,
         channelAddPermission: ConversationEntity.ChannelAddPermission


Reviews:
user: login: MohamadJaara
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-31T14:25:17Z
user: login: ohassine
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-01T12:55:05Z
user: login: vitorhugods
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T13:43:21Z
user: login: sbakhtiarov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T13:44:42Z
user: login: sbakhtiarov
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T13:44:53Z
user: login: ohassine
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-02T07:40:52Z
user: login: ohassine
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-02T08:49:15Z

Review comments:
diff_hunk: @@ -89,6 +89,8 @@ Conversation.verification_status AS mls_verification_status,
 Conversation.proteus_verification_status,
 Conversation.legal_hold_status,
 Conversation.is_channel,
+Conversation.channel_access,
+Conversation.channel_add_permission,
path: persistence/src/commonMain/db_user/com/wire/kalium/persistence/ConversationDetails.sq
user: login: MohamadJaara
body: is it really needed in the view ? since it is used via the use case i would assume it is not needed in the general view used mainly for the main list
created_at: 2025-03-31T14:25:17Z
author_association: MEMBER
diff_hunk: @@ -89,6 +89,8 @@ Conversation.verification_status AS mls_verification_status,
 Conversation.proteus_verification_status,
 Conversation.legal_hold_status,
 Conversation.is_channel,
+Conversation.channel_access,
+Conversation.channel_add_permission,
path: persistence/src/commonMain/db_user/com/wire/kalium/persistence/ConversationDetails.sq
user: login: ohassine
body: yes `channel_access` is needed in the conversation
created_at: 2025-04-01T10:14:58Z
author_association: MEMBER
diff_hunk: @@ -531,6 +531,13 @@ internal class ConversationDAOImpl internal constructor(
         conversationQueries.selectIsChannel(conversationId).executeAsOneOrNull() ?: false
     }
 
+    override suspend fun getChannelAddPermission(conversationId: QualifiedIDEntity): ConversationEntity.ChannelAddPermission {
path: persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/conversation/ConversationDAOImpl.kt
user: login: sbakhtiarov
body: This naming was confusing me in the other PR and dose not seem right here as well. 
"ChannelAddPermisson" - is it permission to add new channel?
"updateChannelAddPermission" - update or add? what permission?
Could we make more clear?
created_at: 2025-04-01T13:44:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -531,6 +531,13 @@ internal class ConversationDAOImpl internal constructor(
         conversationQueries.selectIsChannel(conversationId).executeAsOneOrNull() ?: false
     }
 
+    override suspend fun getChannelAddPermission(conversationId: QualifiedIDEntity): ConversationEntity.ChannelAddPermission {
path: persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/conversation/ConversationDAOImpl.kt
user: login: ohassine
body: it's the permission for adding users into a channel, renaming it for better clarity
created_at: 2025-04-02T07:40:52Z
author_association: MEMBER
diff_hunk: @@ -531,6 +531,13 @@ internal class ConversationDAOImpl internal constructor(
         conversationQueries.selectIsChannel(conversationId).executeAsOneOrNull() ?: false
     }
 
+    override suspend fun getChannelAddPermission(conversationId: QualifiedIDEntity): ConversationEntity.ChannelAddPermission {
path: persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/conversation/ConversationDAOImpl.kt
user: login: ohassine
body: I added a todo, for it I will adress that in a different PR
created_at: 2025-04-02T08:49:14Z
author_association: MEMBER

PR comments:
user: login: github-actions[bot]
created_at: 2025-03-27T11:57:02Z
body: ## Test Results
3 603 tests  +6   3 493 :white_check_mark: +6   6m 44s :stopwatch: +32s
  617 suites +1     110 :zzz: ±0 
  617 files   +1       0 :x: ±0 

Results for commit 9f133737. ± Comparison against base commit 4c8fc1b1.

[test-results]:data:application/gzip;base64,H4sIAKMH7WcC/13MSw6DIBSF4a0Yxh3c66U8uplGRBJSlQZhZLr3gn2RDs93kn9nzs/Txi6dQHnq2JZ9aqbNcUg+rAU48ALlTPUmAfSZ1y2PYzWuG7v5ezFE+JIb/FzoB1OMIb4l5rXp1vWXfVFbPaSJHrttjmFZfCqDaYdEkqSWpIzQRkjlVI/Gou2BI2iE8wRE7PEEpIDFzBEBAAA=

:recycle: This comment has been updated with latest results.
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-03-27T12:02:15Z
body: <h2><a href="https://bencher.dev/perf/kalium-hgtkc47o/reports/3057310f-1e7b-4c8e-985d-f1a128e38136?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=kalium-hgtkc47o"><img src="https://bencher.dev/favicon.svg" width="24" height="24" alt="🐰" /> Bencher Report</a></h2><table><tr><td>Branch</td><td><a href="https://bencher.dev/perf/kalium-hgtkc47o/branches/add-usecase-to-ckeck-if-user-is-allowed-to-add-participants?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=kalium-hgtkc47o">add-usecase-to-ckeck-if-user-is-allowed-to-add-participants</a></td></tr><tr><td>Testbed</td><td><a href="https://bencher.dev/perf/kalium-hgtkc47o/testbeds/ubuntu-latest?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=kalium-hgtkc47o">ubuntu-latest</a></td></tr></table><blockquote><h3>⚠️ WARNING: No Threshold found!</h3><p>Without a Threshold, no Alerts will ever be generated.</p><ul><li><a href="https://bencher.dev/perf/kalium-hgtkc47o/measures/latency?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=kalium-hgtkc47o">Latency (nanoseconds (ns))</a></li></ul><p><a href="https://bencher.dev/console/projects/kalium-hgtkc47o/thresholds/add?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kalium-hgtkc47o">Click here to create a new Threshold</a><br />For more information, see <a href="https://bencher.dev/docs/explanation/thresholds/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kalium-hgtkc47o">the Threshold documentation</a>.<br />To only post results if a Threshold exists, set <a href="https://bencher.dev/docs/explanation/bencher-run/#--ci-only-thresholds?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kalium-hgtkc47o">the <code lang="rust">--ci-only-thresholds</code> flag</a>.</p></blockquote><details><summary>Click to view all benchmark results</summary><br /><table><thead><tr><th>Benchmark</th><th><a href="https://bencher.dev/perf/kalium-hgtkc47o/measures/latency?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=kalium-hgtkc47o">Latency</a></th><th>microseconds (µs)</th></tr></thead><tbody><tr><td><a href="https://bencher.dev/perf/kalium-hgtkc47o/benchmarks/com-wire-kalium-benchmarks-logic-corelogicbenchmark-createobject?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=kalium-hgtkc47o">com.wire.kalium.benchmarks.logic.CoreLogicBenchmark.createObjectInFiles</a></td><td>📈 <a href="https://bencher.dev/perf/kalium-hgtkc47o?branches=418dbc00-5e30-47d7-acc0-9afcff2803af&heads=acf3f5a6-38b8-4748-962e-45f96e9a668d&testbeds=69638233-9d8a-46a4-bef0-7867546076c7&benchmarks=a9bea90f-75ef-43a3-8254-439f71a7b25d&measures=ad1d428c-7cc0-4006-a121-d6d4e41f1bde&start_time=1740994269000&end_time=1743586269000&report=3057310f-1e7b-4c8e-985d-f1a128e38136">view plot</a><br />⚠️ NO THRESHOLD</td><td>677.19 µs</td></tr><tr><td><a href="https://bencher.dev/perf/kalium-hgtkc47o/benchmarks/com-wire-kalium-benchmarks-logic-corelogicbenchmark-cre-ityj5bl2?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=kalium-hgtkc47o">com.wire.kalium.benchmarks.logic.CoreLogicBenchmark.createObjectInMemory</a></td><td>📈 <a href="https://bencher.dev/perf/kalium-hgtkc47o?branches=418dbc00-5e30-47d7-acc0-9afcff2803af&heads=acf3f5a6-38b8-4748-962e-45f96e9a668d&testbeds=69638233-9d8a-46a4-bef0-7867546076c7&benchmarks=406bead6-cc55-4f73-8b63-01409aab1375&measures=ad1d428c-7cc0-4006-a121-d6d4e41f1bde&start_time=1740994269000&end_time=1743586269000&report=3057310f-1e7b-4c8e-985d-f1a128e38136">view plot</a><br />⚠️ NO THRESHOLD</td><td>351,228.42 µs</td></tr><tr><td><a href="https://bencher.dev/perf/kalium-hgtkc47o/benchmarks/com-wire-kalium-benchmarks-persistence-messagesnopragmatunebench?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=kalium-hgtkc47o">com.wire.kalium.benchmarks.persistence.MessagesNoPragmaTuneBenchmark.messageInsertionBenchmark</a></td><td>📈 <a href="https://bencher.dev/perf/kalium-hgtkc47o?branches=418dbc00-5e30-47d7-acc0-9afcff2803af&heads=acf3f5a6-38b8-4748-962e-45f96e9a668d&testbeds=69638233-9d8a-46a4-bef0-7867546076c7&benchmarks=d6762c6c-9ba1-496c-a6ec-a9d47be72146&measures=ad1d428c-7cc0-4006-a121-d6d4e41f1bde&start_time=1740994269000&end_time=1743586269000&report=3057310f-1e7b-4c8e-985d-f1a128e38136">view plot</a><br />⚠️ NO THRESHOLD</td><td>1,343,318.33 µs</td></tr><tr><td><a href="https://bencher.dev/perf/kalium-hgtkc47o/benchmarks/com-wire-kalium-benchmarks-persistence-messagesnopragma-u4l3ncan?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=kalium-hgtkc47o">com.wire.kalium.benchmarks.persistence.MessagesNoPragmaTuneBenchmark.queryMessagesBenchmark</a></td><td>📈 <a href="https://bencher.dev/perf/kalium-hgtkc47o?branches=418dbc00-5e30-47d7-acc0-9afcff2803af&heads=acf3f5a6-38b8-4748-962e-45f96e9a668d&testbeds=69638233-9d8a-46a4-bef0-7867546076c7&benchmarks=4657376f-2ff8-4742-9ab6-c3a0c6fcf790&measures=ad1d428c-7cc0-4006-a121-d6d4e41f1bde&start_time=1740994269000&end_time=1743586269000&report=3057310f-1e7b-4c8e-985d-f1a128e38136">view plot</a><br />⚠️ NO THRESHOLD</td><td>27,440.13 µs</td></tr></tbody></table></details><a href="https://bencher.dev/perf/kalium-hgtkc47o/reports/3057310f-1e7b-4c8e-985d-f1a128e38136?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=kalium-hgtkc47o">🐰 View full continuous benchmarking report in Bencher</a><div id="bencher.dev/projects/kalium-hgtkc47o/id/add-usecase-to-ckeck-if-user-is-allowed-to-add-participants/ubuntu-latest/java_jmh"></div>
author_association: CONTRIBUTOR
user: login: datadog-wireapp[bot]
created_at: 2025-03-27T12:14:41Z
body: 
## Datadog Report

Branch report: [`add-usecase-to-ckeck-if-user-is-allowed-to-add-participants`](https://app.datadoghq.eu/ci/test-branch/github.com%2Fwireapp%2Fkalium/kalium-jvm/add-usecase-to-ckeck-if-user-is-allowed-to-add-participants?env=ci&index=citest&_user_click_origin_=github)
Commit report: [`fec2125`](https://app.datadoghq.eu/ci/test-commit/github.com%2Fwireapp%2Fkalium/kalium-jvm/add-usecase-to-ckeck-if-user-is-allowed-to-add-participants/fec21253d46b71c35aa06f78262337306ba51979?env=ci&index=citest&_user_click_origin_=github)
Test service: `kalium-jvm`

:white_check_mark: 0 Failed, 3493 Passed, 110 Skipped, 1m 5.04s Total Time




author_association: NONE
user: login: codecov-commenter
created_at: 2025-04-01T13:35:49Z
body: ## [Codecov](https://app.codecov.io/gh/wireapp/kalium/pull/3349?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wireapp) Report
:x: Patch coverage is `59.09091%` with `9 lines` in your changes missing coverage. Please review.
:white_check_mark: Project coverage is 53.71%. Comparing base ([`4c8fc1b`](https://app.codecov.io/gh/wireapp/kalium/commit/4c8fc1b1fbfd7535c19490273e4024c311a89300?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wireapp)) to head ([`9f13373`](https://app.codecov.io/gh/wireapp/kalium/commit/9f1337379738b69b678f821bd1d204109105e033?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wireapp)).
:warning: Report is 587 commits behind head on develop.

| [Files with missing lines](https://app.codecov.io/gh/wireapp/kalium/pull/3349?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wireapp) | Patch % | Lines |
|---|---|---|
| [.../logic/data/conversation/ConversationRepository.kt](https://app.codecov.io/gh/wireapp/kalium/pull/3349?src=pr&el=tree&filepath=logic%2Fsrc%2FcommonMain%2Fkotlin%2Fcom%2Fwire%2Fkalium%2Flogic%2Fdata%2Fconversation%2FConversationRepository.kt&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wireapp#diff-bG9naWMvc3JjL2NvbW1vbk1haW4va290bGluL2NvbS93aXJlL2thbGl1bS9sb2dpYy9kYXRhL2NvbnZlcnNhdGlvbi9Db252ZXJzYXRpb25SZXBvc2l0b3J5Lmt0) | 0.00% | [3 Missing :warning: ](https://app.codecov.io/gh/wireapp/kalium/pull/3349?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wireapp) |
| [...ersistence/dao/conversation/ConversationDAOImpl.kt](https://app.codecov.io/gh/wireapp/kalium/pull/3349?src=pr&el=tree&filepath=persistence%2Fsrc%2FcommonMain%2Fkotlin%2Fcom%2Fwire%2Fkalium%2Fpersistence%2Fdao%2Fconversation%2FConversationDAOImpl.kt&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wireapp#diff-cGVyc2lzdGVuY2Uvc3JjL2NvbW1vbk1haW4va290bGluL2NvbS93aXJlL2thbGl1bS9wZXJzaXN0ZW5jZS9kYW8vY29udmVyc2F0aW9uL0NvbnZlcnNhdGlvbkRBT0ltcGwua3Q=) | 0.00% | [3 Missing :warning: ](https://app.codecov.io/gh/wireapp/kalium/pull/3349?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wireapp) |
| [...ire/kalium/logic/feature/channels/ChannelsScope.kt](https://app.codecov.io/gh/wireapp/kalium/pull/3349?src=pr&el=tree&filepath=logic%2Fsrc%2FcommonMain%2Fkotlin%2Fcom%2Fwire%2Fkalium%2Flogic%2Ffeature%2Fchannels%2FChannelsScope.kt&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wireapp#diff-bG9naWMvc3JjL2NvbW1vbk1haW4va290bGluL2NvbS93aXJlL2thbGl1bS9sb2dpYy9mZWF0dXJlL2NoYW5uZWxzL0NoYW5uZWxzU2NvcGUua3Q=) | 0.00% | [2 Missing :warning: ](https://app.codecov.io/gh/wireapp/kalium/pull/3349?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wireapp) |
| [...IsSelfEligibleToAddParticipantsToChannelUseCase.kt](https://app.codecov.io/gh/wireapp/kalium/pull/3349?src=pr&el=tree&filepath=logic%2Fsrc%2FcommonMain%2Fkotlin%2Fcom%2Fwire%2Fkalium%2Flogic%2Ffeature%2Fconversation%2Fchannel%2FIsSelfEligibleToAddParticipantsToChannelUseCase.kt&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wireapp#diff-bG9naWMvc3JjL2NvbW1vbk1haW4va290bGluL2NvbS93aXJlL2thbGl1bS9sb2dpYy9mZWF0dXJlL2NvbnZlcnNhdGlvbi9jaGFubmVsL0lzU2VsZkVsaWdpYmxlVG9BZGRQYXJ0aWNpcGFudHNUb0NoYW5uZWxVc2VDYXNlLmt0) | 92.85% | [0 Missing and 1 partial :warning: ](https://app.codecov.io/gh/wireapp/kalium/pull/3349?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wireapp) |

<details><summary>Additional details and impacted files</summary>



```diff
@@           Coverage Diff            @@
##           develop    #3349   +/-   ##
========================================
  Coverage    53.70%   53.71%           
========================================
  Files         1337     1338    +1     
  Lines        38200    38222   +22     
  Branches      3882     3885    +3     
========================================
+ Hits         20517    20530   +13     
- Misses       16246    16254    +8     
- Partials      1437     1438    +1     
```

| [Files with missing lines](https://app.codecov.io/gh/wireapp/kalium/pull/3349?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wireapp) | Coverage Δ | |
|---|---|---|
| [...um/persistence/dao/conversation/ConversationDAO.kt](https://app.codecov.io/gh/wireapp/kalium/pull/3349?src=pr&el=tree&filepath=persistence%2Fsrc%2FcommonMain%2Fkotlin%2Fcom%2Fwire%2Fkalium%2Fpersistence%2Fdao%2Fconversation%2FConversationDAO.kt&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wireapp#diff-cGVyc2lzdGVuY2Uvc3JjL2NvbW1vbk1haW4va290bGluL2NvbS93aXJlL2thbGl1bS9wZXJzaXN0ZW5jZS9kYW8vY29udmVyc2F0aW9uL0NvbnZlcnNhdGlvbkRBTy5rdA==) | `100.00% <ø> (ø)` | |
| [...IsSelfEligibleToAddParticipantsToChannelUseCase.kt](https://app.codecov.io/gh/wireapp/kalium/pull/3349?src=pr&el=tree&filepath=logic%2Fsrc%2FcommonMain%2Fkotlin%2Fcom%2Fwire%2Fkalium%2Flogic%2Ffeature%2Fconversation%2Fchannel%2FIsSelfEligibleToAddParticipantsToChannelUseCase.kt&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wireapp#diff-bG9naWMvc3JjL2NvbW1vbk1haW4va290bGluL2NvbS93aXJlL2thbGl1bS9sb2dpYy9mZWF0dXJlL2NvbnZlcnNhdGlvbi9jaGFubmVsL0lzU2VsZkVsaWdpYmxlVG9BZGRQYXJ0aWNpcGFudHNUb0NoYW5uZWxVc2VDYXNlLmt0) | `92.85% <92.85%> (ø)` | |
| [...ire/kalium/logic/feature/channels/ChannelsScope.kt](https://app.codecov.io/gh/wireapp/kalium/pull/3349?src=pr&el=tree&filepath=logic%2Fsrc%2FcommonMain%2Fkotlin%2Fcom%2Fwire%2Fkalium%2Flogic%2Ffeature%2Fchannels%2FChannelsScope.kt&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wireapp#diff-bG9naWMvc3JjL2NvbW1vbk1haW4va290bGluL2NvbS93aXJlL2thbGl1bS9sb2dpYy9mZWF0dXJlL2NoYW5uZWxzL0NoYW5uZWxzU2NvcGUua3Q=) | `0.00% <0.00%> (ø)` | |
| [.../logic/data/conversation/ConversationRepository.kt](https://app.codecov.io/gh/wireapp/kalium/pull/3349?src=pr&el=tree&filepath=logic%2Fsrc%2FcommonMain%2Fkotlin%2Fcom%2Fwire%2Fkalium%2Flogic%2Fdata%2Fconversation%2FConversationRepository.kt&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wireapp#diff-bG9naWMvc3JjL2NvbW1vbk1haW4va290bGluL2NvbS93aXJlL2thbGl1bS9sb2dpYy9kYXRhL2NvbnZlcnNhdGlvbi9Db252ZXJzYXRpb25SZXBvc2l0b3J5Lmt0) | `58.96% <0.00%> (-0.33%)` | :arrow_down: |
| [...ersistence/dao/conversation/ConversationDAOImpl.kt](https://app.codecov.io/gh/wireapp/kalium/pull/3349?src=pr&el=tree&filepath=persistence%2Fsrc%2FcommonMain%2Fkotlin%2Fcom%2Fwire%2Fkalium%2Fpersistence%2Fdao%2Fconversation%2FConversationDAOImpl.kt&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wireapp#diff-cGVyc2lzdGVuY2Uvc3JjL2NvbW1vbk1haW4va290bGluL2NvbS93aXJlL2thbGl1bS9wZXJzaXN0ZW5jZS9kYW8vY29udmVyc2F0aW9uL0NvbnZlcnNhdGlvbkRBT0ltcGwua3Q=) | `70.09% <0.00%> (-0.71%)` | :arrow_down: |

------

[Continue to review full report in Codecov by Sentry](https://app.codecov.io/gh/wireapp/kalium/pull/3349?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wireapp).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wireapp)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://app.codecov.io/gh/wireapp/kalium/pull/3349?dropdown=coverage&src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wireapp). Last update [4c8fc1b...9f13373](https://app.codecov.io/gh/wireapp/kalium/pull/3349?dropdown=coverage&src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wireapp). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wireapp).
</details>
<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE
user: login: sonarqubecloud[bot]
created_at: 2025-04-02T09:16:01Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=wireapp_kalium&pullRequest=3349) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=wireapp_kalium&pullRequest=3349&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=wireapp_kalium&pullRequest=3349&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=wireapp_kalium&pullRequest=3349&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=wireapp_kalium&pullRequest=3349&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=wireapp_kalium&pullRequest=3349&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=wireapp_kalium&pullRequest=3349)


author_association: NONE