Skip to content

Commit

Permalink
added update permissions functions and updated tests
Browse files Browse the repository at this point in the history
  • Loading branch information
cameronvoell committed Jul 2, 2024
1 parent bcfac48 commit 3c6b741
Show file tree
Hide file tree
Showing 6 changed files with 439 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import expo.modules.xmtpreactnativesdk.wrappers.DecryptedLocalAttachment
import expo.modules.xmtpreactnativesdk.wrappers.EncryptedLocalAttachment
import expo.modules.xmtpreactnativesdk.wrappers.GroupWrapper
import expo.modules.xmtpreactnativesdk.wrappers.MemberWrapper
import expo.modules.xmtpreactnativesdk.wrappers.PermissionPolicySetWrapper
import expo.modules.xmtpreactnativesdk.wrappers.PreparedLocalMessage
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope
Expand Down Expand Up @@ -1165,6 +1166,21 @@ class XMTPModule : Module() {
}
}

AsyncFunction("permissionPolicySet") Coroutine { inboxId: String, id: String ->
withContext(Dispatchers.IO) {
logV("groupImageUrlSquare")
val client = clients[inboxId] ?: throw XMTPException("No client")
val group = findGroup(inboxId, id)

val permissionPolicySet = group?.permissionPolicySet()
if (permissionPolicySet != null) {
PermissionPolicySetWrapper.encodeToJsonString(permissionPolicySet)
} else {
throw XMTPException("Permission policy set not found for group: $id")
}
}
}

AsyncFunction("processGroupMessage") Coroutine { inboxId: String, id: String, encryptedMessage: String ->
withContext(Dispatchers.IO) {
logV("processGroupMessage")
Expand Down
95 changes: 83 additions & 12 deletions example/src/tests/groupPermissionsTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,8 @@ test('new group has expected admin list and super admin list', async () => {
const superAdminList = await alixGroup.listSuperAdmins()

assert(
adminList.length === 1,
`adminList.length should be 1 but was ${adminList.length}`
)
assert(
adminList[0] === alix.inboxId,
`adminList[0] should be ${alix.address} but was ${adminList[0]}`
adminList.length === 0,
`adminList.length should be 0 but was ${adminList.length}`
)
assert(
superAdminList.length === 1,
Expand Down Expand Up @@ -88,9 +84,9 @@ test('in admin only group, members can not update group name unless they are an
{ permissionLevel: 'admin_only' }
)

if (alixGroup.permissionLevel !== 'admin_only') {
if ((await alixGroup.permissionPolicySet()).addMemberPolicy !== 'admin') {
throw Error(
`Group permission level should be admin_only but was ${alixGroup.permissionLevel}`
`Group add member policy should be admin but was ${(await alixGroup.permissionPolicySet()).addMemberPolicy}`
)
}

Expand Down Expand Up @@ -123,9 +119,11 @@ test('in admin only group, members can update group name once they are an admin'
{ permissionLevel: 'admin_only' }
)

if (alixGroup.permissionLevel !== 'admin_only') {
if (
(await alixGroup.permissionPolicySet()).updateGroupNamePolicy !== 'admin'
) {
throw Error(
`Group permission level should be admin_only but was ${alixGroup.permissionLevel}`
`Group update name policy should be admin but was ${(await alixGroup.permissionPolicySet()).updateGroupNamePolicy}`
)
}

Expand Down Expand Up @@ -174,9 +172,11 @@ test('in admin only group, members can not update group name after admin status
{ permissionLevel: 'admin_only' }
)

if (alixGroup.permissionLevel !== 'admin_only') {
if (
(await alixGroup.permissionPolicySet()).updateGroupNamePolicy !== 'admin'
) {
throw Error(
`Group permission level should be admin_only but was ${alixGroup.permissionLevel}`
`Group update name policy should be admin but was ${(await alixGroup.permissionPolicySet()).updateGroupNamePolicy}`
)
}

Expand Down Expand Up @@ -392,3 +392,74 @@ test('group with All Members policy has remove function that is admin only', asy

return true
})

test('can update group permissions', async () => {
// Create clients
const [alix, bo, caro] = await createClients(3)

// Bo creates a group with Alix and Caro
const boGroup = await bo.conversations.newGroup(
[alix.address, caro.address],
{ permissionLevel: 'admin_only' }
)

// Verify that bo is a super admin
assert(
(await boGroup.isSuperAdmin(bo.inboxId)) === true,
`bo should be a super admin`
)

// Verify that group has the expected group description permission
assert(
(await boGroup.permissionPolicySet()).updateGroupDescriptionPolicy ===
'admin',
`boGroup.permissionPolicySet.updateGroupDescriptionPolicy should be admin but was ${(await boGroup.permissionPolicySet()).updateGroupDescriptionPolicy}`
)

// Verify that Bo can update the group description
await boGroup.updateGroupDescription('new description')
await boGroup.sync()
assert(
(await boGroup.groupDescription()) === 'new description',
`boGroup.groupDescription should be "new description" but was ${boGroup.groupDescription}`
)

// Verify that alix can not update the group description
await alix.conversations.syncGroups()
const alixGroup = (await alix.conversations.listGroups())[0]
try {
await alixGroup.updateGroupDescription('new description')
assert(false, 'Alix should not be able to update the group description')
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (error) {
// expected
}

// Verify that alix can not update permissions
try {
await alixGroup.updateGroupDescriptionPermission('allow')
assert(false, 'Alix should not be able to update the group name permission')
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (error) {
// expected
}

// Verify that bo can update permissions
await boGroup.updateGroupDescriptionPermission('allow')
await boGroup.sync()
assert(
(await boGroup.permissionPolicySet()).updateGroupDescriptionPolicy ===
'allow',
`boGroup.permissionPolicySet.updateGroupDescriptionPolicy should be allow but was ${(await boGroup.permissionPolicySet()).updateGroupDescriptionPolicy}`
)

// Verify that alix can now update the group description
await alixGroup.updateGroupDescription('new description 2')
await alixGroup.sync()
assert(
(await alixGroup.groupDescription()) === 'new description 2',
`alixGroup.groupDescription should be "new description 2" but was ${alixGroup.groupDescription}`
)

return true
})
4 changes: 2 additions & 2 deletions example/src/tests/groupTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1137,9 +1137,9 @@ test('can make a group with admin permissions', async () => {
{ permissionLevel: 'admin_only' }
)

if (group.permissionPolicySet.addMemberPolicy !== 'admin') {
if ((await group.permissionPolicySet()).addMemberPolicy !== 'admin') {
throw Error(
`Group permission level should be admin but was ${group.permissionPolicySet.addMemberPolicy}`
`Group permission level should be admin but was ${(await group.permissionPolicySet()).addMemberPolicy}`
)
}

Expand Down
118 changes: 110 additions & 8 deletions ios/XMTPModule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public class XMTPModule: Module {
}

enum Error: Swift.Error {
case noClient, conversationNotFound(String), noMessage, invalidKeyBundle, invalidDigest, badPreparation(String), mlsNotEnabled(String), invalidString
case noClient, conversationNotFound(String), noMessage, invalidKeyBundle, invalidDigest, badPreparation(String), mlsNotEnabled(String), invalidString, invalidPermissionOption
}

public func definition() -> ModuleDefinition {
Expand Down Expand Up @@ -987,13 +987,100 @@ public class XMTPModule: Module {
}
try await group.removeSuperAdmin(inboxId: inboxId)
}

AsyncFunction("processGroupMessage") { (inboxId: String, id: String, encryptedMessage: String) -> String in
guard let client = await clientsManager.getClient(key: inboxId) else {
throw Error.noClient
}

guard let group = try await findGroup(inboxId: inboxId, id: id) else {

AsyncFunction("updateAddMemberPermission") { (clientInboxId: String, id: String, newPermission: String) in
guard let client = await clientsManager.getClient(key: clientInboxId) else {
throw Error.noClient
}
guard let group = try await findGroup(inboxId: clientInboxId, id: id) else {
throw Error.conversationNotFound("no group found for \(id)")
}
try await group.updateAddMemberPermission(newPermissionOption: getPermissionOption(permission: newPermission))
}

AsyncFunction("updateRemoveMemberPermission") { (clientInboxId: String, id: String, newPermission: String) in
guard let client = await clientsManager.getClient(key: clientInboxId) else {
throw Error.noClient
}
guard let group = try await findGroup(inboxId: clientInboxId, id: id) else {
throw Error.conversationNotFound("no group found for \(id)")
}
try await group.updateRemoveMemberPermission(newPermissionOption: getPermissionOption(permission: newPermission))
}

AsyncFunction("updateAddAdminPermission") { (clientInboxId: String, id: String, newPermission: String) in
guard let client = await clientsManager.getClient(key: clientInboxId) else {
throw Error.noClient
}
guard let group = try await findGroup(inboxId: clientInboxId, id: id) else {
throw Error.conversationNotFound("no group found for \(id)")
}
try await group.updateAddAdminPermission(newPermissionOption: getPermissionOption(permission: newPermission))
}

AsyncFunction("updateRemoveAdminPermission") { (clientInboxId: String, id: String, newPermission: String) in
guard let client = await clientsManager.getClient(key: clientInboxId) else {
throw Error.noClient
}
guard let group = try await findGroup(inboxId: clientInboxId, id: id) else {
throw Error.conversationNotFound("no group found for \(id)")
}
try await group.updateRemoveAdminPermission(newPermissionOption: getPermissionOption(permission: newPermission))
}

AsyncFunction("updateGroupNamePermission") { (clientInboxId: String, id: String, newPermission: String) in
guard let client = await clientsManager.getClient(key: clientInboxId) else {
throw Error.noClient
}
guard let group = try await findGroup(inboxId: clientInboxId, id: id) else {
throw Error.conversationNotFound("no group found for \(id)")
}
try await group.updateGroupNamePermission(newPermissionOption: getPermissionOption(permission: newPermission))
}

AsyncFunction("updateGroupImageUrlSquarePermission") { (clientInboxId: String, id: String, newPermission: String) in
guard let client = await clientsManager.getClient(key: clientInboxId) else {
throw Error.noClient
}
guard let group = try await findGroup(inboxId: clientInboxId, id: id) else {
throw Error.conversationNotFound("no group found for \(id)")
}
try await group.updateGroupImageUrlSquarePermission(newPermissionOption: getPermissionOption(permission: newPermission))
}

AsyncFunction("updateGroupDescriptionPermission") { (clientInboxId: String, id: String, newPermission: String) in
guard let client = await clientsManager.getClient(key: clientInboxId) else {
throw Error.noClient
}
guard let group = try await findGroup(inboxId: clientInboxId, id: id) else {
throw Error.conversationNotFound("no group found for \(id)")
}
try await group.updateGroupDescriptionPermission(newPermissionOption: getPermissionOption(permission: newPermission))
}

AsyncFunction("permissionPolicySet") { (inboxId: String, id: String) async throws -> String in

guard let client = await clientsManager.getClient(key: inboxId) else {
throw Error.noClient
}

guard let group = try await findGroup(inboxId: inboxId, id: id) else {
throw Error.conversationNotFound("Permission policy set not found for group: \(id)")
}

let permissionPolicySet = try group.permissionPolicySet()

return try PermissionPolicySetWrapper.encodeToJsonString(permissionPolicySet)
}



AsyncFunction("processGroupMessage") { (inboxId: String, id: String, encryptedMessage: String) -> String in
guard let client = await clientsManager.getClient(key: inboxId) else {
throw Error.noClient
}

guard let group = try await findGroup(inboxId: inboxId, id: id) else {
throw Error.conversationNotFound("no group found for \(id)")
}

Expand Down Expand Up @@ -1275,6 +1362,21 @@ public class XMTPModule: Module {
//
// Helpers
//

private func getPermissionOption(permission: String) async throws -> PermissionOption {
switch permission {
case "allow":
return .allow
case "deny":
return .deny
case "admin":
return .admin
case "super_admin":
return .superAdmin
default:
throw Error.invalidPermissionOption
}
}

func createClientConfig(env: String, appVersion: String?, preEnableIdentityCallback: PreEventCallback? = nil, preCreateIdentityCallback: PreEventCallback? = nil, enableV3: Bool = false, dbEncryptionKey: Data? = nil, dbDirectory: String? = nil, historySyncUrl: String? = nil) -> XMTP.ClientOptions {
// Ensure that all codecs have been registered.
Expand Down
Loading

0 comments on commit 3c6b741

Please sign in to comment.