Skip to content

Commit

Permalink
Latest libxmtp - permissions and description field updates (#267)
Browse files Browse the repository at this point in the history
* update libxmtp bindings

* format bindings file

* update bindings fields

* lint fix

* Add group description functions, fix import

* Added functions for reading and updating group permissions

* fixed tests

* lint fix

* Added test for updating permissions and description

* fix lint error

* remove unused enum

---------

Co-authored-by: cameronvoell <[email protected]>
  • Loading branch information
cameronvoell and cameronvoell authored Jul 2, 2024
1 parent 2f763bd commit 2d1bbac
Show file tree
Hide file tree
Showing 11 changed files with 587 additions and 113 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import org.xmtp.android.library.libxmtp.PermissionLevel
import org.xmtp.android.library.messages.PrivateKey
import org.xmtp.android.library.messages.PrivateKeyBuilder
import org.xmtp.android.library.messages.walletAddress
import uniffi.xmtpv3.GroupPermissions
import uniffi.xmtpv3.org.xmtp.android.library.libxmtp.GroupPermissionPreconfiguration
import uniffi.xmtpv3.org.xmtp.android.library.libxmtp.PermissionOption

@RunWith(AndroidJUnit4::class)
class GroupPermissionsTest {
Expand Down Expand Up @@ -56,7 +57,7 @@ class GroupPermissionsTest {
runBlocking { alixClient.conversations.syncGroups() }
val alixGroup = runBlocking { alixClient.conversations.listGroups().first() }

assert(boGroup.isAdmin(boClient.inboxId))
assert(!boGroup.isAdmin(boClient.inboxId))
assert(boGroup.isSuperAdmin(boClient.inboxId))
assert(!alixGroup.isCreator())
assert(!alixGroup.isAdmin(alixClient.inboxId))
Expand All @@ -68,19 +69,19 @@ class GroupPermissionsTest {
val superAdminList = runBlocking {
boGroup.listSuperAdmins()
}
assert(adminList.size == 1)
assert(adminList.contains(boClient.inboxId))
assert(adminList.isEmpty())
assert(!adminList.contains(boClient.inboxId))
assert(superAdminList.size == 1)
assert(superAdminList.contains(boClient.inboxId))
}

@Test
fun testGroupCanUpdateAdminList() {
val boGroup = runBlocking { boClient.conversations.newGroup(listOf(alix.walletAddress, caro.walletAddress), GroupPermissions.ADMIN_ONLY) }
val boGroup = runBlocking { boClient.conversations.newGroup(listOf(alix.walletAddress, caro.walletAddress), GroupPermissionPreconfiguration.ADMIN_ONLY) }
runBlocking { alixClient.conversations.syncGroups() }
val alixGroup = runBlocking { alixClient.conversations.listGroups().first() }

assert(boGroup.isAdmin(boClient.inboxId))
assert(!boGroup.isAdmin(boClient.inboxId))
assert(boGroup.isSuperAdmin(boClient.inboxId))
assert(!alixGroup.isCreator())
assert(!alixGroup.isAdmin(alixClient.inboxId))
Expand All @@ -92,8 +93,8 @@ class GroupPermissionsTest {
var superAdminList = runBlocking {
boGroup.listSuperAdmins()
}
assert(adminList.size == 1)
assert(adminList.contains(boClient.inboxId))
assert(adminList.size == 0)
assert(!adminList.contains(boClient.inboxId))
assert(superAdminList.size == 1)
assert(superAdminList.contains(boClient.inboxId))

Expand Down Expand Up @@ -126,7 +127,7 @@ class GroupPermissionsTest {
}

assert(alixGroup.isAdmin(alixClient.inboxId))
assert(adminList.size == 2)
assert(adminList.size == 1)
assert(adminList.contains(alixClient.inboxId))
assert(superAdminList.size == 1)

Expand Down Expand Up @@ -155,7 +156,7 @@ class GroupPermissionsTest {
}

assert(!alixGroup.isAdmin(alixClient.inboxId))
assert(adminList.size == 1)
assert(adminList.size == 0)
assert(!adminList.contains(alixClient.inboxId))
assert(superAdminList.size == 1)

Expand All @@ -170,7 +171,7 @@ class GroupPermissionsTest {

@Test
fun testGroupCanUpdateSuperAdminList() {
val boGroup = runBlocking { boClient.conversations.newGroup(listOf(alix.walletAddress, caro.walletAddress), GroupPermissions.ADMIN_ONLY) }
val boGroup = runBlocking { boClient.conversations.newGroup(listOf(alix.walletAddress, caro.walletAddress), GroupPermissionPreconfiguration.ADMIN_ONLY) }
runBlocking { alixClient.conversations.syncGroups() }
val alixGroup = runBlocking { alixClient.conversations.listGroups().first() }

Expand Down Expand Up @@ -211,7 +212,7 @@ class GroupPermissionsTest {

@Test
fun testGroupMembersAndPermissionLevel() {
val group = runBlocking { boClient.conversations.newGroup(listOf(alix.walletAddress, caro.walletAddress), GroupPermissions.ADMIN_ONLY) }
val group = runBlocking { boClient.conversations.newGroup(listOf(alix.walletAddress, caro.walletAddress), GroupPermissionPreconfiguration.ADMIN_ONLY) }
runBlocking { alixClient.conversations.syncGroups() }
val alixGroup = runBlocking { alixClient.conversations.listGroups().first() }

Expand Down Expand Up @@ -260,7 +261,7 @@ class GroupPermissionsTest {

@Test
fun testCanCommitAfterInvalidPermissionsCommit() {
val boGroup = runBlocking { boClient.conversations.newGroup(listOf(alix.walletAddress, caro.walletAddress), GroupPermissions.ALL_MEMBERS) }
val boGroup = runBlocking { boClient.conversations.newGroup(listOf(alix.walletAddress, caro.walletAddress), GroupPermissionPreconfiguration.ALL_MEMBERS) }
runBlocking { alixClient.conversations.syncGroups() }
val alixGroup = runBlocking { alixClient.conversations.listGroups().first() }

Expand Down Expand Up @@ -288,4 +289,42 @@ class GroupPermissionsTest {
assert(boGroup.name == "Alix group name")
assert(alixGroup.name == "Alix group name")
}

@Test
fun testCanUpdatePermissions() {
val boGroup = runBlocking { boClient.conversations.newGroup(listOf(alix.walletAddress, caro.walletAddress), GroupPermissionPreconfiguration.ADMIN_ONLY) }
runBlocking { alixClient.conversations.syncGroups() }
val alixGroup = runBlocking { alixClient.conversations.listGroups().first() }

// Verify that alix can NOT update group name
assert(boGroup.name == "")
val exception = assertThrows(XMTPException::class.java) {
runBlocking {
alixGroup.updateGroupDescription("new group description")
}
}
assertEquals(exception.message, "Permission denied: Unable to update group description")
runBlocking {
alixGroup.sync()
boGroup.sync()
}
assertEquals(boGroup.permissionPolicySet().updateGroupDescriptionPolicy, PermissionOption.Admin)

// Update group name permissions so Alix can update
runBlocking {
boGroup.updateGroupDescriptionPermission(PermissionOption.Allow)
boGroup.sync()
alixGroup.sync()
}
assertEquals(boGroup.permissionPolicySet().updateGroupDescriptionPolicy, PermissionOption.Allow)

// Verify that alix can now update group name
runBlocking {
alixGroup.updateGroupDescription("Alix group description")
alixGroup.sync()
boGroup.sync()
}
assert(boGroup.description == "Alix group description")
assert(alixGroup.description == "Alix group description")
}
}
29 changes: 15 additions & 14 deletions library/src/androidTest/java/org/xmtp/android/library/GroupTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ import org.xmtp.android.library.messages.PrivateKey
import org.xmtp.android.library.messages.PrivateKeyBuilder
import org.xmtp.android.library.messages.walletAddress
import org.xmtp.proto.mls.message.contents.TranscriptMessages
import uniffi.xmtpv3.GroupPermissions
import uniffi.xmtpv3.org.xmtp.android.library.libxmtp.GroupPermissionPreconfiguration
import uniffi.xmtpv3.org.xmtp.android.library.libxmtp.PermissionOption

@RunWith(AndroidJUnit4::class)
class GroupTest {
Expand Down Expand Up @@ -94,12 +95,12 @@ class GroupTest {
assertEquals(alixGroup.members().size, 3)
assertEquals(boGroup.members().size, 3)

assertEquals(boGroup.permissionLevel(), GroupPermissions.ALL_MEMBERS)
assertEquals(alixGroup.permissionLevel(), GroupPermissions.ALL_MEMBERS)
assertEquals(boGroup.isAdmin(boClient.inboxId), true)
assertEquals(boGroup.isAdmin(alixClient.inboxId), false)
assertEquals(alixGroup.isAdmin(boClient.inboxId), true)
assertEquals(alixGroup.isAdmin(alixClient.inboxId), false)
assertEquals(boGroup.permissionPolicySet().addMemberPolicy, PermissionOption.Allow)
assertEquals(alixGroup.permissionPolicySet().addMemberPolicy, PermissionOption.Allow)
assertEquals(boGroup.isSuperAdmin(boClient.inboxId), true)
assertEquals(boGroup.isSuperAdmin(alixClient.inboxId), false)
assertEquals(alixGroup.isSuperAdmin(boClient.inboxId), true)
assertEquals(alixGroup.isSuperAdmin(alixClient.inboxId), false)
// can not fetch creator ID. See https://github.com/xmtp/libxmtp/issues/788
// assert(boGroup.isCreator())
assert(!alixGroup.isCreator())
Expand All @@ -110,7 +111,7 @@ class GroupTest {
val boGroup = runBlocking {
boClient.conversations.newGroup(
listOf(alix.walletAddress),
permissions = GroupPermissions.ADMIN_ONLY
permissions = GroupPermissionPreconfiguration.ADMIN_ONLY
)
}
runBlocking { alixClient.conversations.syncGroups() }
Expand Down Expand Up @@ -152,12 +153,12 @@ class GroupTest {
assertEquals(alixGroup.members().size, 2)
assertEquals(boGroup.members().size, 2)

assertEquals(boGroup.permissionLevel(), GroupPermissions.ADMIN_ONLY)
assertEquals(alixGroup.permissionLevel(), GroupPermissions.ADMIN_ONLY)
assertEquals(boGroup.isAdmin(boClient.inboxId), true)
assertEquals(boGroup.isAdmin(alixClient.inboxId), false)
assertEquals(alixGroup.isAdmin(boClient.inboxId), true)
assertEquals(alixGroup.isAdmin(alixClient.inboxId), false)
assertEquals(boGroup.permissionPolicySet().addMemberPolicy, PermissionOption.Admin)
assertEquals(alixGroup.permissionPolicySet().addMemberPolicy, PermissionOption.Admin)
assertEquals(boGroup.isSuperAdmin(boClient.inboxId), true)
assertEquals(boGroup.isSuperAdmin(alixClient.inboxId), false)
assertEquals(alixGroup.isSuperAdmin(boClient.inboxId), true)
assertEquals(alixGroup.isSuperAdmin(alixClient.inboxId), false)
// can not fetch creator ID. See https://github.com/xmtp/libxmtp/issues/788
// assert(boGroup.isCreator())
assert(!alixGroup.isCreator())
Expand Down
4 changes: 2 additions & 2 deletions library/src/main/java/libxmtp-version.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
Version: feb48641
Version: e4cfade7
Branch: main
Date: 2024-06-26 22:31:21 +0000
Date: 2024-07-01 19:23:57 +0000
10 changes: 6 additions & 4 deletions library/src/main/java/org/xmtp/android/library/Conversations.kt
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ import uniffi.xmtpv3.FfiMessageCallback
import uniffi.xmtpv3.FfiV2SubscribeRequest
import uniffi.xmtpv3.FfiV2Subscription
import uniffi.xmtpv3.FfiV2SubscriptionCallback
import uniffi.xmtpv3.GroupPermissions
import uniffi.xmtpv3.NoPointer
import uniffi.xmtpv3.org.xmtp.android.library.libxmtp.GroupPermissionPreconfiguration
import java.util.Date
import kotlin.time.Duration.Companion.nanoseconds
import kotlin.time.DurationUnit
Expand Down Expand Up @@ -109,9 +109,10 @@ data class Conversations(

suspend fun newGroup(
accountAddresses: List<String>,
permissions: GroupPermissions = GroupPermissions.ALL_MEMBERS,
permissions: GroupPermissionPreconfiguration = GroupPermissionPreconfiguration.ALL_MEMBERS,
groupName: String = "",
groupImageUrlSquare: String = "",
groupDescription: String = "",
): Group {
if (accountAddresses.size == 1 &&
accountAddresses.first().lowercase() == client.address.lowercase()
Expand All @@ -129,9 +130,10 @@ data class Conversations(
libXMTPConversations?.createGroup(
accountAddresses,
opts = FfiCreateGroupOptions(
permissions = permissions,
permissions = GroupPermissionPreconfiguration.toFfiGroupPermissionOptions(permissions),
groupName = groupName,
groupImageUrlSquare = groupImageUrlSquare
groupImageUrlSquare = groupImageUrlSquare,
groupDescription = groupDescription
)
) ?: throw XMTPException("Client does not support Groups")
client.contacts.allowGroups(groupIds = listOf(group.id()))
Expand Down
60 changes: 55 additions & 5 deletions library/src/main/java/org/xmtp/android/library/Group.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ import uniffi.xmtpv3.FfiGroupPermissions
import uniffi.xmtpv3.FfiListMessagesOptions
import uniffi.xmtpv3.FfiMessage
import uniffi.xmtpv3.FfiMessageCallback
import uniffi.xmtpv3.GroupPermissions
import uniffi.xmtpv3.FfiMetadataField
import uniffi.xmtpv3.FfiPermissionUpdateType
import uniffi.xmtpv3.org.xmtp.android.library.libxmtp.PermissionOption
import uniffi.xmtpv3.org.xmtp.android.library.libxmtp.PermissionPolicySet
import java.util.Date
import kotlin.time.Duration.Companion.nanoseconds
import kotlin.time.DurationUnit
Expand All @@ -47,6 +50,9 @@ class Group(val client: Client, private val libXMTPGroup: FfiGroup) {
val imageUrlSquare: String
get() = libXMTPGroup.groupImageUrlSquare()

val description: String
get() = libXMTPGroup.groupDescription()

suspend fun send(text: String): String {
return send(prepareMessage(content = text, options = null))
}
Expand Down Expand Up @@ -165,8 +171,8 @@ class Group(val client: Client, private val libXMTPGroup: FfiGroup) {
return libXMTPGroup.addedByInboxId()
}

fun permissionLevel(): GroupPermissions {
return permissions.policyType()
fun permissionPolicySet(): PermissionPolicySet {
return PermissionPolicySet(permissions.policySet())
}

fun creatorInboxId(): String {
Expand Down Expand Up @@ -220,11 +226,55 @@ class Group(val client: Client, private val libXMTPGroup: FfiGroup) {
}

suspend fun updateGroupName(name: String) {
return libXMTPGroup.updateGroupName(name)
try {
return libXMTPGroup.updateGroupName(name)
} catch (e: Exception) {
throw XMTPException("Permission denied: Unable to update group name", e)
}
}

suspend fun updateGroupImageUrlSquare(imageUrl: String) {
return libXMTPGroup.updateGroupImageUrlSquare(imageUrl)
try {
return libXMTPGroup.updateGroupImageUrlSquare(imageUrl)
} catch (e: Exception) {
throw XMTPException("Permission denied: Unable to update image url", e)
}
}

suspend fun updateGroupDescription(description: String) {
try {
return libXMTPGroup.updateGroupDescription(description)
} catch (e: Exception) {
throw XMTPException("Permission denied: Unable to update group description", e)
}
}

suspend fun updateAddMemberPermission(newPermissionOption: PermissionOption) {
return libXMTPGroup.updatePermissionPolicy(FfiPermissionUpdateType.ADD_MEMBER, PermissionOption.toFfiPermissionPolicy(newPermissionOption), null)
}

suspend fun updateRemoveMemberPermission(newPermissionOption: PermissionOption) {
return libXMTPGroup.updatePermissionPolicy(FfiPermissionUpdateType.REMOVE_MEMBER, PermissionOption.toFfiPermissionPolicy(newPermissionOption), null)
}

suspend fun updateAddAdminPermission(newPermissionOption: PermissionOption) {
return libXMTPGroup.updatePermissionPolicy(FfiPermissionUpdateType.ADD_ADMIN, PermissionOption.toFfiPermissionPolicy(newPermissionOption), null)
}

suspend fun updateRemoveAdminPermission(newPermissionOption: PermissionOption) {
return libXMTPGroup.updatePermissionPolicy(FfiPermissionUpdateType.REMOVE_ADMIN, PermissionOption.toFfiPermissionPolicy(newPermissionOption), null)
}

suspend fun updateGroupNamePermission(newPermissionOption: PermissionOption) {
return libXMTPGroup.updatePermissionPolicy(FfiPermissionUpdateType.UPDATE_METADATA, PermissionOption.toFfiPermissionPolicy(newPermissionOption), FfiMetadataField.GROUP_NAME)
}

suspend fun updateGroupDescriptionPermission(newPermissionOption: PermissionOption) {
return libXMTPGroup.updatePermissionPolicy(FfiPermissionUpdateType.UPDATE_METADATA, PermissionOption.toFfiPermissionPolicy(newPermissionOption), FfiMetadataField.DESCRIPTION)
}

suspend fun updateGroupImageUrlSquarePermission(newPermissionOption: PermissionOption) {
return libXMTPGroup.updatePermissionPolicy(FfiPermissionUpdateType.UPDATE_METADATA, PermissionOption.toFfiPermissionPolicy(newPermissionOption), FfiMetadataField.IMAGE_URL_SQUARE)
}

fun isAdmin(inboxId: String): Boolean {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package uniffi.xmtpv3.org.xmtp.android.library.libxmtp

import uniffi.xmtpv3.FfiGroupPermissionsOptions
import uniffi.xmtpv3.FfiPermissionPolicy
import uniffi.xmtpv3.FfiPermissionPolicySet
enum class PermissionOption {
Allow,
Deny,
Admin,
SuperAdmin,
Unknown;
companion object {
fun toFfiPermissionPolicy(option: PermissionOption): FfiPermissionPolicy {
return when (option) {
Allow -> FfiPermissionPolicy.ALLOW
Deny -> FfiPermissionPolicy.DENY
Admin -> FfiPermissionPolicy.ADMIN
SuperAdmin -> FfiPermissionPolicy.SUPER_ADMIN
Unknown -> FfiPermissionPolicy.OTHER
}
}
fun fromFfiPermissionPolicy(ffiPolicy: FfiPermissionPolicy): PermissionOption {
return when (ffiPolicy) {
FfiPermissionPolicy.ALLOW -> Allow
FfiPermissionPolicy.DENY -> Deny
FfiPermissionPolicy.ADMIN -> Admin
FfiPermissionPolicy.SUPER_ADMIN -> SuperAdmin
FfiPermissionPolicy.DOES_NOT_EXIST -> Unknown
FfiPermissionPolicy.OTHER -> Unknown
}
}
}
}

enum class GroupPermissionPreconfiguration {
ALL_MEMBERS,
ADMIN_ONLY;

companion object {
fun toFfiGroupPermissionOptions(option: GroupPermissionPreconfiguration): FfiGroupPermissionsOptions {
return when (option) {
ALL_MEMBERS -> FfiGroupPermissionsOptions.ALL_MEMBERS
ADMIN_ONLY -> FfiGroupPermissionsOptions.ADMIN_ONLY
}
}
}
}

class PermissionPolicySet(private val ffiPermissionPolicySet: FfiPermissionPolicySet) {
val addMemberPolicy: PermissionOption
get() = PermissionOption.fromFfiPermissionPolicy(ffiPermissionPolicySet.addMemberPolicy)
val removeMemberPolicy: PermissionOption
get() = PermissionOption.fromFfiPermissionPolicy(ffiPermissionPolicySet.removeMemberPolicy)
val addAdminPolicy: PermissionOption
get() = PermissionOption.fromFfiPermissionPolicy(ffiPermissionPolicySet.addAdminPolicy)
val removeAdminPolicy: PermissionOption
get() = PermissionOption.fromFfiPermissionPolicy(ffiPermissionPolicySet.removeAdminPolicy)
val updateGroupNamePolicy: PermissionOption
get() = PermissionOption.fromFfiPermissionPolicy(ffiPermissionPolicySet.updateGroupNamePolicy)
val updateGroupDescriptionPolicy: PermissionOption
get() = PermissionOption.fromFfiPermissionPolicy(ffiPermissionPolicySet.updateGroupDescriptionPolicy)
val updateGroupImagePolicy: PermissionOption
get() = PermissionOption.fromFfiPermissionPolicy(ffiPermissionPolicySet.updateGroupImageUrlSquarePolicy)
}
Loading

0 comments on commit 2d1bbac

Please sign in to comment.