Skip to content

Commit

Permalink
Add custom event handling
Browse files Browse the repository at this point in the history
  • Loading branch information
wkal-pubnub committed Aug 22, 2024
1 parent 2f4ec6c commit 0bd1e4b
Show file tree
Hide file tree
Showing 13 changed files with 139 additions and 72 deletions.
25 changes: 6 additions & 19 deletions pubnub-chat-api/api/pubnub-chat-api.api
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,8 @@ public final class com/pubnub/chat/config/ChatConfigurationKt {
public static synthetic fun ChatConfiguration-QkTvx9o$default (Lcom/pubnub/chat/config/LogLevel;JJZLcom/pubnub/chat/config/PushNotificationsConfig;ILjava/util/Map;Lcom/pubnub/chat/config/CustomPayloads;ILjava/lang/Object;)Lcom/pubnub/chat/config/ChatConfiguration;
public static final fun RateLimitPerChannel-InTURus (JJJJ)Ljava/util/Map;
public static synthetic fun RateLimitPerChannel-InTURus$default (JJJJILjava/lang/Object;)Ljava/util/Map;
public static final fun main ()V
public static synthetic fun main ([Ljava/lang/String;)V
}

public final class com/pubnub/chat/config/CustomPayloads {
Expand All @@ -298,6 +300,7 @@ public final class com/pubnub/chat/config/LogLevel : java/lang/Enum {

public final class com/pubnub/chat/config/PushNotificationsConfig {
public fun <init> (ZLjava/lang/String;Lcom/pubnub/api/enums/PNPushType;Ljava/lang/String;Lcom/pubnub/api/enums/PNPushEnvironment;)V
public synthetic fun <init> (ZLjava/lang/String;Lcom/pubnub/api/enums/PNPushType;Ljava/lang/String;Lcom/pubnub/api/enums/PNPushEnvironment;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getApnsEnvironment ()Lcom/pubnub/api/enums/PNPushEnvironment;
public final fun getApnsTopic ()Ljava/lang/String;
public final fun getDeviceGateway ()Lcom/pubnub/api/enums/PNPushType;
Expand Down Expand Up @@ -439,28 +442,12 @@ public final class com/pubnub/chat/types/EventContent$Companion {
}

public final class com/pubnub/chat/types/EventContent$Custom : com/pubnub/chat/types/EventContent {
public static final field Companion Lcom/pubnub/chat/types/EventContent$Custom$Companion;
public fun <init> (Ljava/lang/Object;Lcom/pubnub/chat/types/EmitEventMethod;)V
public synthetic fun <init> (Ljava/lang/Object;Lcom/pubnub/chat/types/EmitEventMethod;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getData ()Ljava/lang/Object;
public fun <init> (Ljava/util/Map;Lcom/pubnub/chat/types/EmitEventMethod;)V
public synthetic fun <init> (Ljava/util/Map;Lcom/pubnub/chat/types/EmitEventMethod;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getData ()Ljava/util/Map;
public final fun getMethod ()Lcom/pubnub/chat/types/EmitEventMethod;
}

public synthetic class com/pubnub/chat/types/EventContent$Custom$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
public static final field INSTANCE Lcom/pubnub/chat/types/EventContent$Custom$$serializer;
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/pubnub/chat/types/EventContent$Custom;
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/pubnub/chat/types/EventContent$Custom;)V
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
}

public final class com/pubnub/chat/types/EventContent$Custom$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}

public final class com/pubnub/chat/types/EventContent$Invite : com/pubnub/chat/types/EventContent {
public static final field Companion Lcom/pubnub/chat/types/EventContent$Invite$Companion;
public fun <init> (Lcom/pubnub/chat/types/ChannelType;Ljava/lang/String;)V
Expand Down
4 changes: 2 additions & 2 deletions pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/Chat.kt
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ interface Chat {
fun <T : EventContent> listenForEvents(
type: KClass<T>,
channelId: String,
customMethod: EmitEventMethod? = null,
customMethod: EmitEventMethod = EmitEventMethod.PUBLISH,
callback: (event: Event<T>) -> Unit
): AutoCloseable

Expand Down Expand Up @@ -194,7 +194,7 @@ interface Chat {

inline fun <reified T : EventContent> Chat.listenForEvents(
channelId: String,
customMethod: EmitEventMethod? = null,
customMethod: EmitEventMethod = EmitEventMethod.PUBLISH,
noinline callback: (event: Event<T>) -> Unit
): AutoCloseable {
return listenForEvents(T::class, channelId, customMethod, callback)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
package com.pubnub.chat

class MessageDraft

// sealed class TextElement(val string: String) {
//
// abstract fun insert(text: String)
//
// class Whitespace(string: String) : TextElement(string)
// class Text(string: String) : TextElement(string)
// sealed class Mention(string: String, val marker: Char) : TextElement(string) {
// class UserMention(string: String) : Mention(string, '@')
// class ChannelMention(string: String) : Mention(string, '#')
// }
// }
//
// fun main() {
// val list = mutableListOf<TextElement>()
//
// }
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import com.pubnub.chat.types.ChannelType
import kotlin.time.Duration
import kotlin.time.Duration.Companion.ZERO
import kotlin.time.Duration.Companion.seconds
import kotlin.time.Duration.Companion.milliseconds

interface ChatConfiguration {
val logLevel: LogLevel
Expand Down Expand Up @@ -62,4 +61,4 @@ fun RateLimitPerChannel(

fun main() {
ChatConfiguration(rateLimitPerChannel = RateLimitPerChannel(direct = 1.seconds, group = 2.seconds))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package com.pubnub.chat.types

import com.pubnub.api.JsonElement
import com.pubnub.chat.restrictions.RestrictionType
import kotlinx.serialization.Contextual
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
Expand Down Expand Up @@ -44,10 +43,8 @@ sealed class EventContent {
@SerialName("invite")
class Invite(val channelType: ChannelType, val channelId: String) : EventContent()

@Serializable
@SerialName("custom")
class Custom(
@Contextual val data: Any,
val data: Map<String, Any?>,
@Transient val method: EmitEventMethod = EmitEventMethod.PUBLISH
) : EventContent()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package com.pubnub.chat.internal
import com.benasher44.uuid.uuid4
import com.pubnub.api.PubNub
import com.pubnub.api.PubNubException
import com.pubnub.api.asMap
import com.pubnub.api.asString
import com.pubnub.api.decode
import com.pubnub.api.enums.PNPushType
import com.pubnub.api.models.consumer.PNBoundedPage
import com.pubnub.api.models.consumer.PNPublishResult
Expand Down Expand Up @@ -580,15 +583,23 @@ class ChatImpl(
override fun <T : EventContent> listenForEvents(
type: KClass<T>,
channelId: String,
customMethod: EmitEventMethod?,
customMethod: EmitEventMethod,
callback: (event: Event<T>) -> Unit
): AutoCloseable {
val handler = fun(_: PubNub, pnEvent: PNEvent) {
if (pnEvent.channel != channelId) {
return
}
val message = (pnEvent as? MessageResult)?.message ?: return
val eventContent: EventContent = PNDataEncoder.decode<EventContent>(message)
val eventContent: EventContent = try {
PNDataEncoder.decode<EventContent>(message)
} catch (e: Exception) {
if (message.asMap()?.get("type")?.asString() == "custom") {
EventContent.Custom((message.decode() as Map<String, Any?>) - "type")
} else {
throw e
}
}

@Suppress("UNCHECKED_CAST")
val payload = eventContent as? T ?: return
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.pubnub.chat.internal

import com.pubnub.api.asMap
import com.pubnub.api.asString
import com.pubnub.api.decode
import com.pubnub.api.models.consumer.history.PNFetchMessageItem
import com.pubnub.chat.Chat
import com.pubnub.chat.Event
Expand All @@ -19,10 +22,19 @@ class EventImpl<T : EventContent>(
channelId: String,
pnFetchMessageItem: PNFetchMessageItem
): Event<EventContent> {
val eventContent: EventContent = try {
PNDataEncoder.decode<EventContent>(pnFetchMessageItem.message)
} catch (e: Exception) {
if (pnFetchMessageItem.message.asMap()?.get("type")?.asString() == "custom") {
EventContent.Custom((pnFetchMessageItem.message.decode() as Map<String, Any?>) - "type")
} else {
throw e
}
}
return EventImpl(
chat = chat,
timetoken = pnFetchMessageItem.timetoken ?: 0,
payload = PNDataEncoder.decode<EventContent>(pnFetchMessageItem.message),
payload = eventContent,
channelId = channelId,
userId = pnFetchMessageItem.uuid ?: "unknown-user"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,14 @@ data class MessageImpl(

companion object {
internal fun fromDTO(chat: ChatInternal, pnMessageResult: PNMessageResult): Message {
val content = chat.config.customPayloads?.getMessageResponseBody?.invoke(pnMessageResult.message) ?: defaultGetMessageResponseBody(pnMessageResult.message)
val content =
chat.config.customPayloads?.getMessageResponseBody?.invoke(pnMessageResult.message)
?: defaultGetMessageResponseBody(pnMessageResult.message)
?: EventContent.UnknownMessageFormat(pnMessageResult.message)
return MessageImpl(
chat,
pnMessageResult.timetoken!!,
content!!, // todo handle malformed content (then this is null)
content,
pnMessageResult.channel,
pnMessageResult.publisher!!,
meta = pnMessageResult.userMetadata?.decode() as? Map<String, Any>,
Expand All @@ -54,12 +57,15 @@ data class MessageImpl(
}

internal fun fromDTO(chat: ChatInternal, messageItem: PNFetchMessageItem, channelId: String): Message {
val content = chat.config.customPayloads?.getMessageResponseBody?.invoke(messageItem.message) ?: defaultGetMessageResponseBody(messageItem.message)
val content =
chat.config.customPayloads?.getMessageResponseBody?.invoke(messageItem.message)
?: defaultGetMessageResponseBody(messageItem.message)
?: EventContent.UnknownMessageFormat(messageItem.message)

return MessageImpl(
chat,
messageItem.timetoken!!,
content!!,
content,
channelId,
messageItem.uuid!!,
messageItem.actions,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.pubnub.chat.internal.message

import com.pubnub.api.asString
import com.pubnub.api.decode
import com.pubnub.api.models.consumer.history.PNFetchMessageItem
import com.pubnub.api.models.consumer.history.PNFetchMessageItem.Action
Expand All @@ -10,6 +9,7 @@ import com.pubnub.chat.ThreadMessage
import com.pubnub.chat.internal.ChatImpl
import com.pubnub.chat.internal.ChatInternal
import com.pubnub.chat.internal.channel.ChannelImpl
import com.pubnub.chat.internal.defaultGetMessageResponseBody
import com.pubnub.chat.internal.error.PubNubErrorMessage.PARENT_CHANNEL_DOES_NOT_EXISTS
import com.pubnub.chat.internal.serialization.PNDataEncoder
import com.pubnub.chat.internal.util.pnError
Expand Down Expand Up @@ -53,11 +53,15 @@ data class ThreadMessageImpl(
private val log = logging()

internal fun fromDTO(chat: ChatImpl, pnMessageResult: PNMessageResult, parentChannelId: String): ThreadMessage {
val content =
chat.config.customPayloads?.getMessageResponseBody?.invoke(pnMessageResult.message)
?: defaultGetMessageResponseBody(pnMessageResult.message)
?: EventContent.UnknownMessageFormat(pnMessageResult.message)
return ThreadMessageImpl(
chat,
parentChannelId,
pnMessageResult.timetoken!!,
PNDataEncoder.decode<EventContent>(pnMessageResult.message) as EventContent.TextMessageContent,
content,
pnMessageResult.channel,
pnMessageResult.publisher!!,
meta = pnMessageResult.userMetadata?.decode() as? Map<String, Any>,
Expand All @@ -68,19 +72,16 @@ data class ThreadMessageImpl(
}

internal fun fromDTO(chat: ChatInternal, messageItem: PNFetchMessageItem, channelId: String, parentChannelId: String): ThreadMessage {
val eventContent = try {
messageItem.message.asString()?.let { text ->
EventContent.TextMessageContent(text, null)
} ?: PNDataEncoder.decode(messageItem.message)
} catch (e: Exception) {
EventContent.UnknownMessageFormat(messageItem.message)
}
val content =
chat.config.customPayloads?.getMessageResponseBody?.invoke(messageItem.message)
?: defaultGetMessageResponseBody(messageItem.message)
?: EventContent.UnknownMessageFormat(messageItem.message)

return ThreadMessageImpl(
chat = chat,
parentChannelId = parentChannelId,
timetoken = messageItem.timetoken!!,
content = eventContent,
content = content,
channelId = channelId,
userId = messageItem.uuid!!,
actions = messageItem.actions,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import com.pubnub.chat.internal.defaultGetMessagePublishBody
import com.pubnub.chat.internal.serialization.PNDataEncoder
import com.pubnub.chat.types.EventContent
import kotlinx.serialization.InternalSerializationApi

internal fun Any?.tryLong(): Long? {
return when (this) {
Expand Down Expand Up @@ -41,10 +42,19 @@ internal fun EventContent.TextMessageContent.encodeForSending(
return finalMessage
}

@OptIn(InternalSerializationApi::class)
internal fun EventContent.encodeForSending(
mergeMessageWith: Map<String, Any>? = null,
): Map<String, Any> {
var finalMessage = PNDataEncoder.encode(this) as Map<String, Any>
): Map<String, Any?> {
var finalMessage = if (this is EventContent.Custom) {
buildMap<String, Any?> {
putAll(data)
put("type", "custom")
}
} else {
PNDataEncoder.encode(this) as Map<String, Any?>
}

if (mergeMessageWith != null) {
finalMessage = buildMap {
putAll(finalMessage)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,35 +21,37 @@ import kotlin.test.assertFalse
class ChatConfigurationIntegrationTest : BaseChatIntegrationTest() {
@Test
fun custom_payloads_send_receive_msgs() = runTest {
val chat = ChatImpl(ChatConfiguration(
customPayloads = CustomPayloads(
getMessagePublishBody = { content, channelId ->
mapOf(
val chat = ChatImpl(
ChatConfiguration(
customPayloads = CustomPayloads(
getMessagePublishBody = { content, channelId ->
mapOf(
"custom" to mapOf(
"payload" to mapOf(
"text" to content.text
)
"text" to content.text
)
,
"files" to content.files,
),
"files" to content.files,
// "type" to "text"
)
},
getMessageResponseBody = { json: JsonElement ->
EventContent.TextMessageContent(
json.asMap()?.get("custom")?.asMap()?.get("payload")?.asMap()?.get("text")?.asString()!!,
json.asList()?.map {
File(
it.asMap()?.get("name")?.asString()!!,
it.asMap()?.get("id")?.asString()!!,
it.asMap()?.get("url")?.asString()!!,
it.asMap()?.get("type")?.asString(),
)
},
getMessageResponseBody = { json: JsonElement ->
EventContent.TextMessageContent(
json.asMap()?.get("custom")?.asMap()?.get("payload")?.asMap()?.get("text")?.asString()!!,
json.asList()?.map {
File(
it.asMap()?.get("name")?.asString()!!,
it.asMap()?.get("id")?.asString()!!,
it.asMap()?.get("url")?.asString()!!,
it.asMap()?.get("type")?.asString(),
)
}
)
}
)
), pubnub).initialize().await()
}
)
}
)
),
pubnub
).initialize().await()
val channel = chat.createChannel(randomString()).await()
val messageText = randomString()
val message = CompletableDeferred<Message>()
Expand All @@ -67,4 +69,4 @@ class ChatConfigurationIntegrationTest : BaseChatIntegrationTest() {
unsubscribe?.close()
}
}
}
}
Loading

0 comments on commit 0bd1e4b

Please sign in to comment.