Skip to content

Commit 21eb6fc

Browse files
committed
refactor: adjust for server breaking change
1 parent 59079ab commit 21eb6fc

File tree

10 files changed

+121
-47
lines changed

10 files changed

+121
-47
lines changed

build.gradle.kts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ kotlin {
7575
useJUnitPlatform()
7676
}
7777
}
78-
js {
78+
js(IR) {
7979
nodejs()
8080
browser()
8181
}
@@ -97,6 +97,9 @@ kotlin {
9797
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
9898
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$serializationVersion")
9999
implementation("io.ktor:ktor-client-core:$ktorVersion")
100+
implementation("io.ktor:ktor-client-serialization:$ktorVersion")
101+
implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion")
102+
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
100103
}
101104
}
102105
val commonTest by getting {

gradle.properties

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,7 @@ kotlin.native.ignoreDisabledTargets=true
1414
kotlin.native.binary.memoryModel=experimental
1515

1616
org.gradle.daemon=false
17-
org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=512m
17+
org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=512m
18+
19+
org.gradle.unsafe.configuration-cache=true
20+
org.gradle.unsafe.configuration-cache-problems=warn

src/commonMain/kotlin/dev/zxilly/notify/sdk/Client.kt

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,40 @@
11
package dev.zxilly.notify.sdk
22

3-
import dev.zxilly.notify.sdk.entity.Message
4-
import dev.zxilly.notify.sdk.entity.MessageItem
3+
import dev.zxilly.notify.sdk.entity.*
54
import io.ktor.client.*
5+
import io.ktor.client.call.*
6+
import io.ktor.client.plugins.contentnegotiation.*
67
import io.ktor.client.request.*
78
import io.ktor.client.request.forms.*
89
import io.ktor.client.statement.*
910
import io.ktor.http.*
10-
import kotlinx.serialization.builtins.ListSerializer
11+
import io.ktor.serialization.kotlinx.json.*
1112
import kotlinx.serialization.json.Json
1213

1314
@Suppress("MemberVisibilityCanBePrivate", "unused")
1415
class Client private constructor(
1516
private val userID: String,
1617
private val endpoint: String
1718
) {
18-
private val client = HttpClient()
19+
private val client = HttpClient() {
20+
install(ContentNegotiation) {
21+
json(Json {
22+
ignoreUnknownKeys = true
23+
})
24+
}
25+
}
1926

20-
private suspend fun check() {
27+
private suspend inline fun check() {
2128
val resp = client.get("$endpoint/check") {
2229
parameter("user_id", userID)
2330
}
24-
if (resp.bodyAsText() != "true") {
25-
throw Error("User ID not valid")
31+
val ret: Response<Boolean> = resp.body()
32+
if (!ret.body) {
33+
throw Exception("User ID $userID not valid")
2634
}
2735
}
2836

29-
suspend fun send(msg: Message) = wrap {
37+
suspend fun send(msg: MessagePayload) = wrap {
3038
if (msg.content.isBlank()) {
3139
throw emptyContentError
3240
}
@@ -39,28 +47,29 @@ class Client private constructor(
3947
}
4048
)
4149
if (!resp.ok()) {
42-
throw Error("Send failed\n${resp.bodyAsText()}")
50+
val err: ErrorResponse = resp.body()
51+
throw Exception("Error code ${err.code}: ${err.body}")
4352
}
44-
Json.decodeFromString(MessageItem.serializer(), resp.bodyAsText())
53+
(resp.body() as Response<MessageItem>).body
4554
}
4655

4756
suspend fun send(content: String) = wrap {
48-
val msg = Message(content, null, null)
57+
val msg = MessagePayload(content, null, null)
4958
send(msg)
5059
}
5160

5261
suspend fun send(content: String, title: String) = wrap {
53-
val msg = Message(content, title, null)
62+
val msg = MessagePayload(content, title, null)
5463
send(msg)
5564
}
5665

5766
suspend fun send(content: String, title: String, long: String) = wrap {
58-
val msg = Message(content, title, long)
67+
val msg = MessagePayload(content, title, long)
5968
send(msg)
6069
}
6170

62-
suspend fun send(block: Message.() -> Unit) = wrap {
63-
val msg = Message("", null, null).apply(block)
71+
suspend fun send(block: MessagePayload.() -> Unit) = wrap {
72+
val msg = MessagePayload("", null, null).apply(block)
6473
send(msg)
6574
}
6675

@@ -71,22 +80,39 @@ class Client private constructor(
7180
}
7281
}
7382

74-
suspend fun reportFCMToken(token: String) = wrap {
75-
val resp = client.put("$endpoint/$userID/fcm/token") {
76-
setBody(token)
83+
suspend fun register(channel: Channel, token: String, deviceID: String) = wrap {
84+
if (!isUUID(deviceID)) {
85+
throw Error("Device ID is not a valid UUID")
86+
}
87+
88+
println(Parameters.build {
89+
append("channel", channel.value)
90+
append("token", token)
91+
}.formUrlEncode())
92+
val resp = client.put {
93+
url("$endpoint/$userID/token/$deviceID")
94+
setBody(
95+
Parameters.build {
96+
append("channel", channel.value)
97+
append("token", token)
98+
}.formUrlEncode()
99+
)
100+
header("Content-Type", "application/x-www-form-urlencoded")
77101
}
78102
if (!resp.ok()) {
79-
throw Error("Report failed\n${resp.bodyAsText()}")
103+
val err: ErrorResponse = resp.body()
104+
throw Exception("Error code ${err.code}: ${err.body}")
80105
}
106+
(resp.body() as Response<Boolean>).body
81107
}
82108

83109
suspend fun <T> fetchMessage(postProcessor: List<MessageItem>.() -> List<T>) = wrap {
84110
val resp = client.get("$endpoint/$userID/record")
85111
if (!resp.ok()) {
86112
throw Error("Fetch failed\n${resp.bodyAsText()}")
87113
}
88-
val messages = Json.decodeFromString(ListSerializer(MessageItem.serializer()), resp.bodyAsText())
89-
postProcessor(messages)
114+
val messages = resp.body() as Response<List<MessageItem>>
115+
postProcessor(messages.body)
90116
}
91117

92118
companion object {
Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package dev.zxilly.notify.sdk
22

3-
import dev.zxilly.notify.sdk.entity.Message
3+
import dev.zxilly.notify.sdk.entity.MessagePayload
44
import dev.zxilly.notify.sdk.entity.MessageItem
55

66
private val clients = LRUCache<String, Client>(8)
@@ -14,28 +14,28 @@ private suspend fun getClient(userID: String): Result<Client> {
1414
}
1515
}
1616

17-
suspend fun send(userID: String, message: Message): Result<MessageItem> {
17+
suspend fun send(userID: String, message: MessagePayload): Result<MessageItem> {
1818
return getClient(userID).getOrElse {
1919
return Result.failure(it)
2020
}.send(message)
2121
}
2222

2323
suspend fun send(userID: String, content: String): Result<MessageItem> {
24-
return send(userID, Message(content))
24+
return send(userID, MessagePayload(content))
2525
}
2626

2727
@Suppress("unused")
2828
suspend fun send(userID: String, content: String, title: String): Result<MessageItem> {
29-
return send(userID, Message(content, title))
29+
return send(userID, MessagePayload(content, title))
3030
}
3131

3232
@Suppress("unused")
3333
suspend fun send(userID: String, content: String, title: String, long: String): Result<MessageItem> {
34-
return send(userID, Message(content, title, long))
34+
return send(userID, MessagePayload(content, title, long))
3535
}
3636

3737
@Suppress("unused")
38-
suspend fun send(userID: String, block: Message.() -> Unit): Result<MessageItem> {
39-
val msg = Message("", null, null).apply(block)
38+
suspend fun send(userID: String, block: MessagePayload.() -> Unit): Result<MessageItem> {
39+
val msg = MessagePayload("", null, null).apply(block)
4040
return send(userID, msg)
4141
}

src/commonMain/kotlin/dev/zxilly/notify/sdk/Utils.kt

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
11
package dev.zxilly.notify.sdk
22

33
import io.ktor.client.statement.*
4+
import io.ktor.http.*
45

56
val ok: HttpResponse.() -> Boolean = {
6-
val codes = listOf(200, 201, 304)
7-
codes.contains(status.value)
7+
val codes = setOf(
8+
HttpStatusCode.OK,
9+
HttpStatusCode.Created,
10+
HttpStatusCode.Accepted,
11+
HttpStatusCode.NoContent,
12+
HttpStatusCode.NotModified
13+
)
14+
codes.contains(status)
815
}
916

1017
val emptyContentError = Error("Content is empty")
1118

12-
val isUnit by lazy {
19+
val isUnitTest by lazy {
1320
try {
1421
throw Error("test")
1522
} catch (e: Throwable) {
@@ -23,9 +30,13 @@ val isUnit by lazy {
2330

2431
val defaultEndpoint: String
2532
get() {
26-
return if (isUnit) {
33+
return if (isUnitTest) {
2734
"http://localhost:14444"
2835
} else {
2936
"https://push.learningman.top"
3037
}
3138
}
39+
40+
fun isUUID(str: String): Boolean {
41+
return str.matches(Regex("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"))
42+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package dev.zxilly.notify.sdk.entity
2+
3+
enum class Channel(val value: String) {
4+
FCM("FCM"),
5+
WebSocket("WebSocketHost"),
6+
}

src/commonMain/kotlin/dev/zxilly/notify/sdk/entity/Message.kt renamed to src/commonMain/kotlin/dev/zxilly/notify/sdk/entity/MessagePayload.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import kotlinx.serialization.Serializable
44

55

66
@Serializable
7-
data class Message(
7+
data class MessagePayload(
88
val content: String,
99
val title: String?,
1010
val long: String?
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package dev.zxilly.notify.sdk.entity
2+
3+
import kotlinx.serialization.Serializable
4+
5+
@Serializable
6+
data class Response<T>(
7+
val code: Int,
8+
9+
val body: T
10+
)
11+
12+
@Serializable
13+
data class ErrorResponse(
14+
val code: Int,
15+
val body: String
16+
)

src/commonTest/kotlin/ClientTest.kt

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import dev.zxilly.notify.sdk.Client
2-
import dev.zxilly.notify.sdk.entity.Message
2+
import dev.zxilly.notify.sdk.entity.Channel
3+
import dev.zxilly.notify.sdk.entity.MessagePayload
34
import dev.zxilly.notify.sdk.entity.MessageItem
45
import kotlinx.coroutines.ExperimentalCoroutinesApi
56
import kotlinx.coroutines.test.runTest
@@ -31,7 +32,7 @@ class ClientTest {
3132
val client = Client.create("test").getOrElse {
3233
fail(it.stackTraceToString())
3334
}
34-
val ret = client.send(Message("test"))
35+
val ret = client.send(MessagePayload("test"))
3536
assertTrue { ret.isSuccess }
3637
assertTrue { ret.getOrNull() is MessageItem }
3738
assertEquals(ret.getOrNull()!!.content, "test")
@@ -42,7 +43,7 @@ class ClientTest {
4243
val client = Client.create("test").getOrElse {
4344
fail(it.stackTraceToString())
4445
}
45-
val ret = client.send(Message("test", "test_title"))
46+
val ret = client.send(MessagePayload("test", "test_title"))
4647
assertTrue { ret.isSuccess }
4748
assertTrue { ret.getOrNull() is MessageItem }
4849
assertEquals(ret.getOrNull()!!.content, "test")
@@ -54,20 +55,28 @@ class ClientTest {
5455
val client = Client.create("test").getOrElse {
5556
fail(it.stackTraceToString())
5657
}
57-
val ret = client.send(Message(""))
58+
val ret = client.send(MessagePayload(""))
5859
assertTrue { ret.isFailure }
5960
assertTrue { ret.exceptionOrNull() is Throwable }
61+
6062
}
6163

6264
@Test
63-
fun checkReportFCMToken() = runTest {
65+
fun checkRegister() = runTest {
6466
val client = Client.create("test").getOrElse {
6567
fail(it.stackTraceToString())
6668
}
67-
val ret = client.reportFCMToken("test")
68-
assertTrue { ret.isSuccess }
69+
val ret = client.register(Channel.FCM, "test", "test")
70+
assertTrue { ret.isFailure }
71+
assertTrue { ret.exceptionOrNull() is Throwable }
72+
expect("Device ID is not a valid UUID") {
73+
ret.exceptionOrNull()!!.message
74+
}
6975

70-
val ret2 = client.reportFCMToken("test") // Can handle 304 correct
76+
val uuid = "00000000-0000-0000-0000-000000000000"
77+
val ret2 = client.register(Channel.WebSocket, "test", uuid)
7178
assertTrue { ret2.isSuccess }
79+
assertTrue { ret2.getOrNull() is Boolean }
80+
assertTrue { ret2.getOrNull()!! }
7281
}
7382
}

src/commonTest/kotlin/SendTest.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import dev.zxilly.notify.sdk.send
2-
import dev.zxilly.notify.sdk.entity.Message
2+
import dev.zxilly.notify.sdk.entity.MessagePayload
33
import dev.zxilly.notify.sdk.entity.MessageItem
44
import kotlinx.coroutines.ExperimentalCoroutinesApi
55
import kotlinx.coroutines.test.runTest
@@ -24,15 +24,15 @@ class SendTest {
2424

2525
@Test
2626
fun checkSendMessage() = runTest {
27-
val ret = send("test", Message("test"))
27+
val ret = send("test", MessagePayload("test"))
2828
assertTrue { ret.isSuccess }
2929
assertTrue { ret.getOrNull() is MessageItem }
3030
assertEquals(ret.getOrNull()!!.content, "test")
3131
}
3232

3333
@Test
3434
fun checkSendMessage2() = runTest {
35-
val ret = send("test", Message("test", "test_title"))
35+
val ret = send("test", MessagePayload("test", "test_title"))
3636
assertTrue { ret.isSuccess }
3737
assertTrue { ret.getOrNull() is MessageItem }
3838
assertEquals(ret.getOrNull()!!.content, "test")
@@ -41,7 +41,7 @@ class SendTest {
4141

4242
@Test
4343
fun checkSendMessageFailed() = runTest {
44-
val ret = send("test", Message(""))
44+
val ret = send("test", MessagePayload(""))
4545
assertTrue { ret.isFailure }
4646
assertTrue { ret.exceptionOrNull() is Throwable }
4747
}

0 commit comments

Comments
 (0)