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

Fix deserialization of Heartbeat events #957

Merged
merged 1 commit into from
Jul 10, 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
13 changes: 9 additions & 4 deletions gateway/api/gateway.api
Original file line number Diff line number Diff line change
Expand Up @@ -1011,12 +1011,17 @@ public final class dev/kord/gateway/GuildUpdate : dev/kord/gateway/DispatchEvent
public final class dev/kord/gateway/Heartbeat : dev/kord/gateway/Event {
public static final field Companion Ldev/kord/gateway/Heartbeat$Companion;
public static final field NewCompanion Ldev/kord/gateway/Heartbeat$NewCompanion;
public fun <init> (J)V
public final fun component1 ()J
public final fun copy (J)Ldev/kord/gateway/Heartbeat;
public synthetic fun <init> (J)V
public fun <init> (Ljava/lang/Long;)V
public final synthetic fun component1 ()J
public final fun component1 ()Ljava/lang/Long;
public final synthetic fun copy (J)Ldev/kord/gateway/Heartbeat;
public final fun copy (Ljava/lang/Long;)Ldev/kord/gateway/Heartbeat;
public static synthetic fun copy$default (Ldev/kord/gateway/Heartbeat;JILjava/lang/Object;)Ldev/kord/gateway/Heartbeat;
public static synthetic fun copy$default (Ldev/kord/gateway/Heartbeat;Ljava/lang/Long;ILjava/lang/Object;)Ldev/kord/gateway/Heartbeat;
public fun equals (Ljava/lang/Object;)Z
public final fun getData ()J
public final synthetic fun getData ()J
public final fun getData ()Ljava/lang/Long;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
Expand Down
13 changes: 9 additions & 4 deletions gateway/api/gateway.klib.api
Original file line number Diff line number Diff line change
Expand Up @@ -1237,12 +1237,17 @@ final class dev.kord.gateway/GuildUpdate : dev.kord.gateway/DispatchEvent { // d

final class dev.kord.gateway/Heartbeat : dev.kord.gateway/Event { // dev.kord.gateway/Heartbeat|null[0]
constructor <init>(kotlin/Long) // dev.kord.gateway/Heartbeat.<init>|<init>(kotlin.Long){}[0]
constructor <init>(kotlin/Long?) // dev.kord.gateway/Heartbeat.<init>|<init>(kotlin.Long?){}[0]

final val data // dev.kord.gateway/Heartbeat.data|{}data[0]
final fun <get-data>(): kotlin/Long // dev.kord.gateway/Heartbeat.data.<get-data>|<get-data>(){}[0]

final fun component1(): kotlin/Long // dev.kord.gateway/Heartbeat.component1|component1(){}[0]
final fun copy(kotlin/Long = ...): dev.kord.gateway/Heartbeat // dev.kord.gateway/Heartbeat.copy|copy(kotlin.Long){}[0]
final fun <get-data>(): kotlin/Long? // dev.kord.gateway/Heartbeat.data.<get-data>|<get-data>(){}[0]
final val data_ // dev.kord.gateway/Heartbeat.data_|{}data_[0]
final fun <get-data_>(): kotlin/Long // dev.kord.gateway/Heartbeat.data_.<get-data_>|<get-data_>(){}[0]

final fun component1(): kotlin/Long? // dev.kord.gateway/Heartbeat.component1|component1(){}[0]
final fun component1_(): kotlin/Long // dev.kord.gateway/Heartbeat.component1_|component1_(){}[0]
final fun copy(kotlin/Long? = ...): dev.kord.gateway/Heartbeat // dev.kord.gateway/Heartbeat.copy|copy(kotlin.Long?){}[0]
final fun copy_(kotlin/Long = ...): dev.kord.gateway/Heartbeat // dev.kord.gateway/Heartbeat.copy_|copy_(kotlin.Long){}[0]
final fun equals(kotlin/Any?): kotlin/Boolean // dev.kord.gateway/Heartbeat.equals|equals(kotlin.Any?){}[0]
final fun hashCode(): kotlin/Int // dev.kord.gateway/Heartbeat.hashCode|hashCode(){}[0]
final fun toString(): kotlin/String // dev.kord.gateway/Heartbeat.toString|toString(){}[0]
Expand Down
37 changes: 33 additions & 4 deletions gateway/src/commonMain/kotlin/Event.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import dev.kord.common.serialization.DurationInSeconds
import io.github.oshai.kotlinlogging.KotlinLogging
import kotlinx.datetime.Instant
import kotlinx.serialization.*
import kotlinx.serialization.builtins.nullable
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.CompositeDecoder
Expand All @@ -16,6 +17,7 @@ import kotlinx.serialization.encoding.decodeStructure
import kotlinx.serialization.json.JsonDecoder
import kotlinx.serialization.json.JsonElement
import kotlin.jvm.JvmField
import kotlin.jvm.JvmName
import kotlinx.serialization.DeserializationStrategy as KDeserializationStrategy

private val jsonLogger = KotlinLogging.logger { }
Expand Down Expand Up @@ -288,13 +290,40 @@ public data class ReadyData(
)

@Serializable(with = Heartbeat.Serializer::class)
public data class Heartbeat(val data: Long) : Event() {
public data class Heartbeat(val data: Long?) : Event() {
internal object Serializer : KSerializer<Heartbeat> {
override val descriptor = PrimitiveSerialDescriptor("dev.kord.gateway.Heartbeat", PrimitiveKind.LONG)
override fun serialize(encoder: Encoder, value: Heartbeat) = encoder.encodeLong(value.data)
override fun deserialize(decoder: Decoder) = Heartbeat(decoder.decodeLong())
private val delegate = Long.serializer().nullable

override val descriptor = PrimitiveSerialDescriptor("dev.kord.gateway.Heartbeat", PrimitiveKind.LONG).nullable

override fun serialize(encoder: Encoder, value: Heartbeat) =
encoder.encodeSerializableValue(delegate, value.data)

override fun deserialize(decoder: Decoder) = Heartbeat(decoder.decodeSerializableValue(delegate))
}

@Deprecated("Binary compatibility, keep for some releases.", level = DeprecationLevel.HIDDEN)
public constructor(data: Long) : this(data as Long?)

@Suppress("PropertyName")
@Deprecated("Binary compatibility, keep for some releases.", level = DeprecationLevel.HIDDEN)
@get:JvmName("getData")
public val data_: Long
get() = data ?: throw NullPointerException("This heartbeat request contains a null sequence number")

@Suppress("FunctionName")
@Deprecated("Binary compatibility, keep for some releases.", level = DeprecationLevel.HIDDEN)
@JvmName("component1")
public fun component1_(): Long =
component1() ?: throw NullPointerException("This heartbeat request contains a null sequence number")

@Suppress("FunctionName")
@Deprecated("Binary compatibility, keep for some releases.", level = DeprecationLevel.HIDDEN)
@JvmName("copy")
public fun copy_(
data: Long = this.data ?: throw NullPointerException("This heartbeat request contains a null sequence number"),
): Heartbeat = Heartbeat(data as Long?)

public companion object {
@Suppress("DEPRECATION_ERROR")
@Deprecated(
Expand Down
18 changes: 18 additions & 0 deletions gateway/src/commonTest/kotlin/json/SerializationTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import kotlinx.serialization.MissingFieldException
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonNull
import kotlin.js.JsName
import kotlin.random.Random
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
Expand Down Expand Up @@ -167,6 +168,23 @@ class SerializationTest {
}
}

@Test
fun test_Heartbeat_Event_serialization() {
val sequenceNumbers = listOf(null, 0, -1, 1, Random.nextLong(), Long.MIN_VALUE, Long.MAX_VALUE)
sequenceNumbers.forEach { sequenceNumber ->
val heartbeat = Heartbeat(sequenceNumber)
val permutations = listOf(
jsonObjectPermutations("op" to "1", "d" to "$sequenceNumber"),
jsonObjectPermutations("op" to "1", "t" to "null", "d" to "$sequenceNumber"),
jsonObjectPermutations("op" to "1", "s" to "null", "d" to "$sequenceNumber"),
jsonObjectPermutations("op" to "1", "t" to "null", "s" to "null", "d" to "$sequenceNumber"),
).flatten()
permutations.forEach { perm ->
assertEquals(heartbeat, Json.decodeFromString(Event.DeserializationStrategy, perm))
}
}
}

@Test
fun field_order_doesnt_matter() {
val data = listOf(
Expand Down