Skip to content

Commit

Permalink
Merge pull request #504 from xmtp/np/v3-consent
Browse files Browse the repository at this point in the history
Consent V3
  • Loading branch information
nplasterer authored Oct 1, 2024
2 parents 8ff06e5 + 26a8bd3 commit 831cae5
Show file tree
Hide file tree
Showing 12 changed files with 327 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import kotlinx.coroutines.withContext
import org.json.JSONObject
import org.xmtp.android.library.Client
import org.xmtp.android.library.ClientOptions
import org.xmtp.android.library.ConsentState
import org.xmtp.android.library.Conversation
import org.xmtp.android.library.Group
import org.xmtp.android.library.PreEventCallback
Expand Down Expand Up @@ -372,7 +373,7 @@ class XMTPModule : Module() {
}
}

AsyncFunction("createOrBuild") Coroutine { address: String, hasCreateIdentityCallback: Boolean?, hasEnableIdentityCallback: Boolean?, hasAuthInboxCallback: Boolean?, dbEncryptionKey: List<Int>?, authParams: String ->
AsyncFunction("createOrBuild") Coroutine { address: String, hasCreateIdentityCallback: Boolean?, hasEnableIdentityCallback: Boolean?, hasAuthInboxCallback: Boolean?, dbEncryptionKey: List<Int>?, authParams: String ->
withContext(Dispatchers.IO) {
logV("createOrBuild")
val reactSigner = ReactNativeSigner(module = this@XMTPModule, address = address)
Expand Down Expand Up @@ -1029,7 +1030,10 @@ class XMTPModule : Module() {
val client = clients[inboxId] ?: throw XMTPException("No client")
client.conversations.syncAllGroups()
// Expo Modules do not support UInt, so we need to convert to Int
val numGroupsSyncedInt: Int = client.conversations.syncAllGroups()?.toInt() ?: throw IllegalArgumentException("Value cannot be null")
val numGroupsSyncedInt: Int =
client.conversations.syncAllGroups()?.toInt() ?: throw IllegalArgumentException(
"Value cannot be null"
)
numGroupsSyncedInt
}
}
Expand Down Expand Up @@ -1610,7 +1614,7 @@ class XMTPModule : Module() {
withContext(Dispatchers.IO) {
val group = findGroup(inboxId, groupId)
?: throw XMTPException("no group found for $groupId")
consentStateToString(Conversation.Group(group).consentState())
consentStateToString(group.consentState())
}
}

Expand Down Expand Up @@ -1664,6 +1668,14 @@ class XMTPModule : Module() {
client.contacts.isGroupDenied(groupId)
}
}
AsyncFunction("updateGroupConsent") Coroutine { inboxId: String, groupId: String, state: String ->
withContext(Dispatchers.IO) {
logV("updateGroupConsent")
val group = findGroup(inboxId, groupId)

group?.updateConsentState(getConsentState(state))
}
}

AsyncFunction("exportNativeLogs") Coroutine { ->
withContext(Dispatchers.IO) {
Expand All @@ -1688,7 +1700,7 @@ class XMTPModule : Module() {
// Helpers
//

private suspend fun getPermissionOption(permissionString: String): PermissionOption {
private fun getPermissionOption(permissionString: String): PermissionOption {
return when (permissionString) {
"allow" -> PermissionOption.Allow
"deny" -> PermissionOption.Deny
Expand All @@ -1698,6 +1710,14 @@ class XMTPModule : Module() {
}
}

private fun getConsentState(stateString: String): ConsentState {
return when (stateString) {
"allowed" -> ConsentState.ALLOWED
"denied" -> ConsentState.DENIED
else -> ConsentState.UNKNOWN
}
}

private suspend fun findConversation(
inboxId: String,
topic: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package expo.modules.xmtpreactnativesdk.wrappers

import com.google.gson.GsonBuilder
import expo.modules.xmtpreactnativesdk.wrappers.ConsentWrapper.Companion.consentStateToString
import org.xmtp.android.library.Client
import org.xmtp.android.library.Group
import org.xmtp.android.library.toHex

class GroupWrapper {

Expand All @@ -21,7 +21,8 @@ class GroupWrapper {
"addedByInboxId" to group.addedByInboxId(),
"name" to group.name,
"imageUrlSquare" to group.imageUrlSquare,
"description" to group.description
"description" to group.description,
"consentState" to consentStateToString(group.consentState())
// "pinnedFrameUrl" to group.pinnedFrameUrl
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ class MemberWrapper {
return mapOf(
"inboxId" to member.inboxId,
"addresses" to member.addresses,
"permissionLevel" to permissionString
"permissionLevel" to permissionString,
"consentState" to ConsentWrapper.consentStateToString(member.consentState)
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,10 +294,12 @@
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-xmtpreactnativesdkexample/Pods-xmtpreactnativesdkexample-frameworks.sh",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenSSL-Universal/OpenSSL.framework/OpenSSL",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OpenSSL.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
Expand Down
170 changes: 121 additions & 49 deletions example/src/tests/groupTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1834,67 +1834,139 @@ test('creating a group should allow group', async () => {
return true
})

test('can allow a group', async () => {
test('can group consent', async () => {
const [alix, bo] = await createClients(2)
const alixGroup = await alix.conversations.newGroup([bo.address])
const startConsent = await bo.contacts.isGroupAllowed(alixGroup.id)
if (startConsent) {
throw Error('Group should not be allowed')
}
await bo.contacts.allowGroups([alixGroup.id])
const isAllowed = await bo.contacts.isGroupAllowed(alixGroup.id)
if (!isAllowed) {
throw Error('Group should be allowed')
}
const group = await bo.conversations.newGroup([alix.address])
let isAllowed = await alix.contacts.isGroupAllowed(group.id)
assert(
isAllowed === false,
`alix group should NOT be allowed but was ${isAllowed}`
)

return true
})
isAllowed = await bo.contacts.isGroupAllowed(group.id)
assert(
isAllowed === true,
`bo group should be allowed but was ${isAllowed}`
)
assert(
await group.state === 'allowed',
`the group should have a consent state of allowed but was ${await group.state}`
)

await bo.contacts.denyGroups([group.id])
let isDenied = await bo.contacts.isGroupDenied(group.id)
assert(
isDenied === true,
`bo group should be denied but was ${isDenied}`
)
assert(
await group.consentState() === 'denied',
`the group should have a consent state of denied but was ${await group.consentState()}`
)

test('can deny a group', async () => {
const [alix, bo] = await createClients(2)
const alixGroup = await alix.conversations.newGroup([bo.address])
const startConsent = await bo.contacts.isGroupDenied(alixGroup.id)
if (startConsent) {
throw Error('Group should be unknown')
}
await bo.contacts.denyGroups([alixGroup.id])
await bo.conversations.syncGroups()
const boGroups = await bo.conversations.listGroups()
const isDenied = await bo.contacts.isGroupDenied(alixGroup.id)
const isGroupDenied = await boGroups[0].isDenied()
if (!isDenied || !isGroupDenied) {
throw Error('Group should be denied')
}
await bo.contacts.allowGroups([alixGroup.id])
const isAllowed = await bo.contacts.isGroupAllowed(alixGroup.id)
if (!isAllowed) {
throw Error('Group should be allowed')
}
await group.updateConsent('allowed')
isAllowed = await bo.contacts.isGroupAllowed(group.id)
assert(
isAllowed === true,
`bo group should be allowed2 but was ${isAllowed}`
)
assert(
await group.consentState() === 'allowed',
`the group should have a consent state2 of allowed but was ${await group.consentState()}`
)

return true
})

test('can allow and deny a inbox id', async () => {
const [alix, bo] = await createClients(2)
const startConsent = await bo.contacts.isInboxAllowed(alix.inboxId)
if (startConsent) {
throw Error('inbox id should be unknown')
}
await bo.contacts.denyInboxes([alix.inboxId])
const isDenied = await bo.contacts.isInboxDenied(alix.inboxId)
if (!isDenied) {
throw Error('inbox id should be denied')
}
const boGroup = await bo.conversations.newGroup([alix.address])

let isInboxAllowed = await bo.contacts.isInboxAllowed(alix.inboxId)
let isInboxDenied = await bo.contacts.isInboxDenied(alix.inboxId)
assert(
isInboxAllowed === false,
`isInboxAllowed should be false but was ${isInboxAllowed}`
)
assert(
isInboxDenied === false,
`isInboxDenied should be false but was ${isInboxDenied}`
)

await bo.contacts.allowInboxes([alix.inboxId])
const isAllowed = await bo.contacts.isInboxAllowed(alix.inboxId)
if (!isAllowed) {
throw Error('inbox id should be allowed')
}

const consentList = await bo.contacts.consentList()
let alixMember = (await boGroup.membersList()).find(
(member) => member.inboxId === alix.inboxId
)
assert(
alixMember?.consentState === 'allowed',
`alixMember should be allowed but was ${alixMember?.consentState}`
)

isInboxAllowed = await bo.contacts.isInboxAllowed(alix.inboxId)
isInboxDenied = await bo.contacts.isInboxDenied(alix.inboxId)
assert(
isInboxAllowed === true,
`isInboxAllowed2 should be true but was ${isInboxAllowed}`
)
assert(
isInboxDenied === false,
`isInboxDenied2 should be false but was ${isInboxDenied}`
)

let isAddressAllowed = await bo.contacts.isAllowed(alix.address)
let isAddressDenied = await bo.contacts.isDenied(alix.address)
assert(
isAddressAllowed === true,
`isAddressAllowed should be true but was ${isAddressAllowed}`
)
assert(
isAddressDenied === false,
`isAddressDenied should be false but was ${isAddressDenied}`
)

await bo.contacts.denyInboxes([alix.inboxId])

alixMember = (await boGroup.membersList()).find(
(member) => member.inboxId === alix.inboxId
)
assert(
alixMember?.consentState === 'denied',
`alixMember should be denied but was ${alixMember?.consentState}`
)

isInboxAllowed = await bo.contacts.isInboxAllowed(alix.inboxId)
isInboxDenied = await bo.contacts.isInboxDenied(alix.inboxId)
assert(
isInboxAllowed === false,
`isInboxAllowed3 should be false but was ${isInboxAllowed}`
)
assert(
isInboxDenied === true,
`isInboxDenied3 should be true but was ${isInboxDenied}`
)

await bo.contacts.allow([alix.address])

isAddressAllowed = await bo.contacts.isAllowed(alix.address)
isAddressDenied = await bo.contacts.isDenied(alix.address)
assert(
isAddressAllowed === true,
`isAddressAllowed2 should be true but was ${isAddressAllowed}`
)
assert(
isAddressDenied === false,
`isAddressDenied2 should be false but was ${isAddressDenied}`
)
isInboxAllowed = await bo.contacts.isInboxAllowed(alix.inboxId)
isInboxDenied = await bo.contacts.isInboxDenied(alix.inboxId)
assert(
isInboxAllowed === true,
`isInboxAllowed4 should be false but was ${isInboxAllowed}`
)
assert(
consentList[0].entryType === 'inbox_id',
`the message should have a type of inbox_id but was ${consentList[0].entryType}`
isInboxDenied === false,
`isInboxDenied4 should be true but was ${isInboxDenied}`
)

return true
Expand Down
Loading

0 comments on commit 831cae5

Please sign in to comment.