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

Use partial channel metadata from PubNub SDK #53

Merged
merged 5 commits into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions pubnub-chat-api/api/pubnub-chat-api.api
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public abstract interface class com/pubnub/chat/Channel {
public abstract fun join (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Lcom/pubnub/kmp/PNFuture;
public abstract fun leave ()Lcom/pubnub/kmp/PNFuture;
public abstract fun pinMessage (Lcom/pubnub/chat/Message;)Lcom/pubnub/kmp/PNFuture;
public abstract fun plus (Lcom/pubnub/api/models/consumer/objects/channel/PNChannelMetadata;)Lcom/pubnub/chat/Channel;
public abstract fun registerForPush ()Lcom/pubnub/kmp/PNFuture;
public abstract fun sendText (Ljava/lang/String;Ljava/util/Map;ZZLjava/lang/Integer;Ljava/util/Map;Ljava/util/Map;Ljava/util/List;Lcom/pubnub/chat/Message;Ljava/util/List;)Lcom/pubnub/kmp/PNFuture;
public abstract fun setRestrictions (Lcom/pubnub/chat/User;ZZLjava/lang/String;)Lcom/pubnub/kmp/PNFuture;
Expand Down Expand Up @@ -233,6 +234,7 @@ public abstract interface class com/pubnub/chat/User {
public abstract fun getType ()Ljava/lang/String;
public abstract fun getUpdated ()Ljava/lang/String;
public abstract fun isPresentOn (Ljava/lang/String;)Lcom/pubnub/kmp/PNFuture;
public abstract fun plus (Lcom/pubnub/api/models/consumer/objects/uuid/PNUUIDMetadata;)Lcom/pubnub/chat/User;
public abstract fun report (Ljava/lang/String;)Lcom/pubnub/kmp/PNFuture;
public abstract fun setRestrictions (Lcom/pubnub/chat/Channel;ZZLjava/lang/String;)Lcom/pubnub/kmp/PNFuture;
public abstract fun streamUpdates (Lkotlin/jvm/functions/Function1;)Ljava/lang/AutoCloseable;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.pubnub.api.models.consumer.files.PNDeleteFileResult
import com.pubnub.api.models.consumer.objects.PNMemberKey
import com.pubnub.api.models.consumer.objects.PNPage
import com.pubnub.api.models.consumer.objects.PNSortKey
import com.pubnub.api.models.consumer.objects.channel.PNChannelMetadata
import com.pubnub.api.models.consumer.push.PNPushAddChannelResult
import com.pubnub.api.models.consumer.push.PNPushRemoveChannelResult
import com.pubnub.chat.membership.MembersResponse
Expand Down Expand Up @@ -128,7 +129,11 @@ interface Channel {

fun getUserSuggestions(text: String, limit: Int = 10): PNFuture<Set<Membership>>

/**
* Get a new `Channel` instance that is a copy of this `Channel` with its properties updated with information coming from `update`.
*/
operator fun plus(update: PNChannelMetadata): Channel

// Companion object required for extending this class elsewhere
// toDo Is this needed? Where do we extend this?
companion object
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ interface ThreadChannel : Channel {
val parentMessage: Message
val parentChannelId: String

// TODO change parameter to ThreadMessage
override fun pinMessage(message: Message): PNFuture<ThreadChannel>

override fun unpinMessage(): PNFuture<ThreadChannel>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.pubnub.api.models.consumer.PNPublishResult
import com.pubnub.api.models.consumer.objects.PNMembershipKey
import com.pubnub.api.models.consumer.objects.PNPage
import com.pubnub.api.models.consumer.objects.PNSortKey
import com.pubnub.api.models.consumer.objects.uuid.PNUUIDMetadata
import com.pubnub.chat.membership.MembershipsResponse
import com.pubnub.chat.restrictions.GetRestrictionsResponse
import com.pubnub.chat.restrictions.Restriction
Expand Down Expand Up @@ -67,5 +68,10 @@ interface User {

fun report(reason: String): PNFuture<PNPublishResult>

/**
* Get a new `User` instance that is a copy of this `User` with its properties updated with information coming from `update`.
*/
operator fun plus(update: PNUUIDMetadata): User

companion object
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.pubnub.api.models.consumer.objects.membership.PNChannelMembershipArra
import com.pubnub.api.models.consumer.objects.uuid.PNUUIDMetadata
import com.pubnub.api.models.consumer.pubsub.objects.PNDeleteUUIDMetadataEventMessage
import com.pubnub.api.models.consumer.pubsub.objects.PNSetUUIDMetadataEventMessage
import com.pubnub.api.utils.PatchValue
import com.pubnub.api.v2.callbacks.Result
import com.pubnub.chat.Channel
import com.pubnub.chat.Membership
Expand Down Expand Up @@ -189,6 +190,10 @@ data class UserImpl(
return chat.emitEvent(channelId = INTERNAL_ADMIN_CHANNEL, payload = payload)
}

override operator fun plus(update: PNUUIDMetadata): User {
return fromDTO(chat, toUUIDMetadata() + update)
}

internal fun getRestrictions(
channel: Channel?,
limit: Int? = null,
Expand Down Expand Up @@ -234,21 +239,36 @@ data class UserImpl(
return memberships
}

private fun toUUIDMetadata(): PNUUIDMetadata {
return PNUUIDMetadata(
id = id,
name = name?.let { PatchValue.of(it) },
externalId = externalId?.let { PatchValue.of(it) },
profileUrl = profileUrl?.let { PatchValue.of(it) },
email = email?.let { PatchValue.of(it) },
custom = custom?.let { PatchValue.of(it) },
updated = updated?.let { PatchValue.of(it) },
eTag = null,
type = type?.let { PatchValue.of(it) },
status = status?.let { PatchValue.of(it) },
)
}

companion object {
private val log = logging()

internal fun fromDTO(chat: ChatInternal, user: PNUUIDMetadata): User = UserImpl(
chat,
id = user.id,
name = user.name,
externalId = user.externalId,
profileUrl = user.profileUrl,
email = user.email,
custom = user.custom,
updated = user.updated,
status = user.status,
type = user.type,
lastActiveTimestamp = user.custom?.get("lastActiveTimestamp")?.tryLong()
name = user.name?.value,
externalId = user.externalId?.value,
profileUrl = user.profileUrl?.value,
email = user.email?.value,
custom = user.custom?.value,
updated = user.updated?.value,
status = user.status?.value,
type = user.type?.value,
lastActiveTimestamp = user.custom?.value?.get("lastActiveTimestamp")?.tryLong()
)

fun streamUpdatesOn(users: Collection<User>, callback: (users: Collection<User>) -> Unit): AutoCloseable {
Expand All @@ -259,14 +279,25 @@ data class UserImpl(
val chat = users.first().chat as ChatInternal
val listener = createEventListener(chat.pubNub, onObjects = { pubNub, event ->
val (newUser, newUserId) = when (val message = event.extractedMessage) {
is PNSetUUIDMetadataEventMessage -> fromDTO(chat, message.data) to message.data.id
is PNSetUUIDMetadataEventMessage -> {
val newUserId = message.data.id
val previousUser = latestUsers.firstOrNull { it.id == newUserId }
val newUser = previousUser?.plus(message.data) ?: fromDTO(chat, message.data)
newUser to newUserId
}
is PNDeleteUUIDMetadataEventMessage -> null to message.uuid
else -> return@createEventListener
}

latestUsers = latestUsers.asSequence().filter {
it.id != newUserId
}.run { newUser?.let { plus(it) } ?: this }.toList()
latestUsers = latestUsers.asSequence().filter { user ->
user.id != newUserId
}.let { sequence ->
if (newUser != null) {
sequence + newUser
} else {
sequence
}
}.toList()
callback(latestUsers)
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import com.pubnub.api.models.consumer.objects.membership.PNChannelMembershipArra
import com.pubnub.api.models.consumer.pubsub.objects.PNDeleteChannelMetadataEventMessage
import com.pubnub.api.models.consumer.pubsub.objects.PNSetChannelMetadataEventMessage
import com.pubnub.api.models.consumer.push.payload.PushPayloadHelper
import com.pubnub.api.utils.PatchValue
import com.pubnub.api.v2.callbacks.Result
import com.pubnub.api.v2.subscriptions.SubscriptionOptions
import com.pubnub.chat.Channel
Expand All @@ -40,7 +41,6 @@ import com.pubnub.chat.internal.METADATA_REFERENCED_CHANNELS
import com.pubnub.chat.internal.METADATA_TEXT_LINKS
import com.pubnub.chat.internal.MINIMAL_TYPING_INDICATOR_TIMEOUT
import com.pubnub.chat.internal.MembershipImpl
import com.pubnub.chat.internal.channel.ChannelImpl.Companion.fromDTO
import com.pubnub.chat.internal.defaultGetMessageResponseBody
import com.pubnub.chat.internal.error.PubNubErrorMessage.CAN_NOT_STREAM_CHANNEL_UPDATES_ON_EMPTY_LIST
import com.pubnub.chat.internal.error.PubNubErrorMessage.CHANNEL_INVITES_ARE_NOT_SUPPORTED_IN_PUBLIC_CHATS
Expand Down Expand Up @@ -671,6 +671,10 @@ abstract class BaseChannel<C : Channel, M : Message>(
)
}

override operator fun plus(update: PNChannelMetadata): Channel {
return channelFactory(chat, toPNChannelMetadata() + update)
}

private fun sendTypingSignal(value: Boolean): PNFuture<Unit> {
return chat.emitEvent(
channelId = this.id,
Expand All @@ -684,6 +688,19 @@ abstract class BaseChannel<C : Channel, M : Message>(

internal abstract fun copyWithStatusDeleted(): C

private fun toPNChannelMetadata(): PNChannelMetadata {
return PNChannelMetadata(
id = id,
name = name?.let { PatchValue.of(it) },
description = description?.let { PatchValue.of(it) },
custom = custom?.let { PatchValue.of(custom) },
updated = updated?.let { PatchValue.of(it) },
eTag = null,
type = type?.let { PatchValue.of(it.stringValue) },
status = status?.let { PatchValue.of(it) }
)
}

companion object {
private val log = logging()

Expand Down Expand Up @@ -737,14 +754,25 @@ abstract class BaseChannel<C : Channel, M : Message>(
val chat = channels.first().chat as ChatInternal
val listener = createEventListener(chat.pubNub, onObjects = { _, event ->
val (newChannel, newChannelId) = when (val message = event.extractedMessage) {
is PNSetChannelMetadataEventMessage -> fromDTO(chat, message.data) to message.data.id
is PNSetChannelMetadataEventMessage -> {
val newChannelId = message.data.id
val previousChannel = latestChannels.firstOrNull { it.id == newChannelId }
val newChannel = previousChannel?.plus(message.data) ?: ChannelImpl.fromDTO(chat, message.data)
newChannel to newChannelId
}
is PNDeleteChannelMetadataEventMessage -> null to message.channel
else -> return@createEventListener
}

latestChannels = latestChannels.asSequence().filter {
it.id != newChannelId
}.run { newChannel?.let { plus(it) } ?: this }.toList()
latestChannels = latestChannels.asSequence().filter { channel ->
channel.id != newChannelId
}.let { sequence ->
if (newChannel != null) {
sequence + newChannel
} else {
sequence
}
}.toList()
callback(latestChannels)
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ data class ChannelImpl(
return ChannelImpl(
chat,
id = channel.id,
name = channel.name,
custom = channel.custom,
description = channel.description,
updated = channel.updated,
status = channel.status,
type = ChannelType.from(channel.type)
name = channel.name?.value,
custom = channel.custom?.value,
description = channel.description?.value,
updated = channel.updated?.value,
status = channel.status?.value,
type = ChannelType.from(channel.type?.value)
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,12 +137,12 @@ data class ThreadChannelImpl(
parentMessage,
chat,
id = channel.id,
name = channel.name,
custom = channel.custom,
description = channel.description,
updated = channel.updated,
status = channel.status,
type = ChannelType.from(channel.type)
name = channel.name?.value,
custom = channel.custom?.value,
description = channel.description?.value,
updated = channel.updated?.value,
status = channel.status?.value,
type = ChannelType.from(channel.type?.value)
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import com.pubnub.chat.internal.UserImpl
import com.pubnub.chat.internal.channel.BaseChannel
import com.pubnub.chat.internal.channel.ChannelImpl
import com.pubnub.chat.restrictions.GetRestrictionsResponse
import com.pubnub.chat.types.ChannelType
import com.pubnub.chat.types.JoinResult
import com.pubnub.kmp.createCustomObject
import com.pubnub.test.await
Expand Down Expand Up @@ -289,51 +288,21 @@ class ChannelIntegrationTest : BaseChatIntegrationTest() {
).await()
delayInMillis(1000)

// todo there are big problems with update handling in PN SDK that prevent this for working in a sane way
// e.g. getting an update to one property (e.g. description) will return an object with all other properties set to null
val expectedUpdates = listOf<List<Channel>>(
listOf(
channel01.asImpl().copy(
name = newName,
custom = null,
description = null,
type = ChannelType.UNKNOWN,
status = null,
updated = null
),
channel02.asImpl().copy(updated = null)
channel01.asImpl().copy(name = newName),
channel02.asImpl()
).sortedBy {
it.id
},
listOf(
channel01.asImpl().copy(
name = newName,
custom = null,
description = null,
type = ChannelType.UNKNOWN,
status = null,
updated = null
),
channel02.asImpl().copy(
custom = null,
name = null,
description = newName,
type = ChannelType.UNKNOWN,
status = null,
updated = null
)
channel01.asImpl().copy(name = newName),
channel02.asImpl().copy(description = newName)
).sortedBy {
it.id
},
listOf(
channel02.asImpl().copy(
custom = null,
name = null,
description = newName,
type = ChannelType.UNKNOWN,
status = null,
updated = null
)
channel02.asImpl().copy(description = newName)
).sortedBy {
it.id
},
Expand Down Expand Up @@ -361,7 +330,7 @@ class ChannelIntegrationTest : BaseChatIntegrationTest() {
dispose?.close()
}

assertEquals(expectedUpdates, actualUpdates)
assertEquals(expectedUpdates.map { it.map { it.asImpl().copy(updated = null) as Channel } }, actualUpdates)
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ class UserIntegrationTest : BaseChatIntegrationTest() {
val expectedUpdates = listOf(
listOf(someUser),
listOf(someUser.asImpl().copy(name = newName)),
listOf(someUser.asImpl().copy(name = newName, externalId = newName)),
emptyList()
)
val actualUpdates = mutableListOf<List<User>>()
Expand All @@ -138,6 +139,8 @@ class UserIntegrationTest : BaseChatIntegrationTest() {

someUser.update(name = newName).await()

someUser.update(externalId = newName).await()

someUser.delete().await()

delayInMillis(2000)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import com.pubnub.api.models.consumer.history.PNFetchMessagesResult
import com.pubnub.api.models.consumer.objects.PNMemberKey
import com.pubnub.api.models.consumer.objects.PNPage
import com.pubnub.api.models.consumer.objects.PNSortKey
import com.pubnub.api.models.consumer.objects.channel.PNChannelMetadata
import com.pubnub.api.models.consumer.objects.member.PNUUIDDetailsLevel
import com.pubnub.api.utils.PatchValue
import com.pubnub.api.v2.callbacks.Consumer
import com.pubnub.api.v2.callbacks.Result
import com.pubnub.api.v2.createPNConfiguration
Expand All @@ -35,6 +37,7 @@ import com.pubnub.chat.types.MessageMentionedUser
import com.pubnub.chat.types.MessageReferencedChannel
import com.pubnub.kmp.utils.BaseTest
import com.pubnub.test.await
import com.pubnub.test.randomString
import dev.mokkery.MockMode
import dev.mokkery.answering.calls
import dev.mokkery.answering.returns
Expand Down Expand Up @@ -765,6 +768,16 @@ class ChannelTest : BaseTest() {

verify { chat.updateChannel(channelId, name, custom, description, status, type) }
}

@Test
fun plus() {
val channel = createChannel(ChannelType.PUBLIC)
val expectedChannel = channel.copy(name = randomString(), description = randomString())

val newChannel = channel + PNChannelMetadata(expectedChannel.id, name = PatchValue.of(expectedChannel.name), description = PatchValue.of(expectedChannel.description))

assertEquals(expectedChannel, newChannel)
}
}

private operator fun Any?.get(s: String): Any? {
Expand Down
Loading
Loading