From b5cf896c7bbaf93e600f8ae4fc3ad3835a9bc253 Mon Sep 17 00:00:00 2001 From: Jeehyun Kim Date: Mon, 19 Jun 2023 16:43:34 +0900 Subject: [PATCH] Add Presence for Client.Options (#108) * add Presence value class for Client.Options.presence * setup JDK 17 for the deploy pages action --- .github/workflows/gh-pages.yml | 5 ++ .../kotlin/dev/yorkie/JsonTestUtils.kt | 2 +- .../kotlin/dev/yorkie/core/ClientTest.kt | 55 +++++++++++++++++++ .../kotlin/dev/yorkie/core/TestUtils.kt | 3 +- .../src/main/kotlin/dev/yorkie/core/Client.kt | 4 +- .../src/main/kotlin/dev/yorkie/core/Peers.kt | 5 +- 6 files changed, 69 insertions(+), 5 deletions(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index cd70ab7b2..11698abc6 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -10,6 +10,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 + - uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + cache: gradle - run: ./gradlew dokkaHtml - uses: JamesIves/github-pages-deploy-action@v4.4.1 with: diff --git a/yorkie/src/androidTest/kotlin/dev/yorkie/JsonTestUtils.kt b/yorkie/src/androidTest/kotlin/dev/yorkie/JsonTestUtils.kt index 15dd0a368..e631b2c5a 100644 --- a/yorkie/src/androidTest/kotlin/dev/yorkie/JsonTestUtils.kt +++ b/yorkie/src/androidTest/kotlin/dev/yorkie/JsonTestUtils.kt @@ -6,7 +6,7 @@ import com.google.gson.Gson import com.google.gson.JsonObject import org.junit.Assert.assertEquals -private val gson = Gson() +internal val gson = Gson() fun assertJsonContentEquals(expected: String, actual: String) { val expectedJson = gson.fromJson(expected, JsonObject::class.java) diff --git a/yorkie/src/androidTest/kotlin/dev/yorkie/core/ClientTest.kt b/yorkie/src/androidTest/kotlin/dev/yorkie/core/ClientTest.kt index 9562eeeae..bb2ed880f 100644 --- a/yorkie/src/androidTest/kotlin/dev/yorkie/core/ClientTest.kt +++ b/yorkie/src/androidTest/kotlin/dev/yorkie/core/ClientTest.kt @@ -13,6 +13,7 @@ import dev.yorkie.document.change.CheckPoint import dev.yorkie.document.json.JsonCounter import dev.yorkie.document.json.JsonPrimitive import dev.yorkie.document.operation.OperationInfo +import dev.yorkie.gson import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.Job import kotlinx.coroutines.delay @@ -482,6 +483,60 @@ class ClientTest { } } + @Test + fun test_access_peer_presence() { + runBlocking { + data class Cursor(val x: Int, val y: Int) + + val serializedCursor = gson.toJson(Cursor(1, 1)) + + val client1 = createClient(presence = Presence(mapOf("name" to "a"))) + val client2 = createClient( + presence = Presence(mapOf("name" to "b", "cursor" to serializedCursor)), + ) + val client1Events = mutableListOf() + val client1EventsJob = launch(start = CoroutineStart.UNDISPATCHED) { + client1.events.collect(client1Events::add) + } + + client1.activateAsync().await() + client2.activateAsync().await() + + val documentKey = UUID.randomUUID().toString().toDocKey() + val document1 = Document(documentKey) + val document2 = Document(documentKey) + + client1.attachAsync(document1).await() + withTimeout(2_000) { + // initialized + while (client1Events.isEmpty()) { + delay(50) + } + } + assertEquals( + null, + client1.peerStatusByDoc(documentKey).first()[client2.requireClientId()]?.data, + ) + + client2.attachAsync(document2).await() + withTimeout(2_000) { + client1.peerStatusByDoc(documentKey).first { + it[client2.requireClientId()]?.data != null + } + } + assertEquals( + mapOf("name" to "b", "cursor" to serializedCursor), + client1.peerStatusByDoc(documentKey).first()[client2.requireClientId()]?.data, + ) + + client1EventsJob.cancel() + client1.detachAsync(document1).await() + client2.detachAsync(document2).await() + client1.deactivateAsync().await() + client2.deactivateAsync().await() + } + } + private fun Client.peerStatusByDoc(key: Document.Key) = peerStatus.mapNotNull { it[key] } diff --git a/yorkie/src/androidTest/kotlin/dev/yorkie/core/TestUtils.kt b/yorkie/src/androidTest/kotlin/dev/yorkie/core/TestUtils.kt index 413d54b5d..da1412488 100644 --- a/yorkie/src/androidTest/kotlin/dev/yorkie/core/TestUtils.kt +++ b/yorkie/src/androidTest/kotlin/dev/yorkie/core/TestUtils.kt @@ -6,11 +6,12 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.runBlocking import java.util.UUID -fun createClient() = Client( +fun createClient(presence: Presence? = null) = Client( InstrumentationRegistry.getInstrumentation().targetContext, "10.0.2.2", 8080, usePlainText = true, + options = Client.Options(presence = presence), ) fun String.toDocKey(): Document.Key { diff --git a/yorkie/src/main/kotlin/dev/yorkie/core/Client.kt b/yorkie/src/main/kotlin/dev/yorkie/core/Client.kt index 2f6c77922..173e21a77 100644 --- a/yorkie/src/main/kotlin/dev/yorkie/core/Client.kt +++ b/yorkie/src/main/kotlin/dev/yorkie/core/Client.kt @@ -93,7 +93,7 @@ public class Client @VisibleForTesting internal constructor( private val _peerStatus = MutableStateFlow(emptyMap()) public val peerStatus = _peerStatus.asStateFlow() - public var presenceInfo = options.presence ?: PresenceInfo(0, emptyMap()) + public var presenceInfo = PresenceInfo(0, options.presence?.value.orEmpty()) private set private val service by lazy { @@ -738,7 +738,7 @@ public class Client @VisibleForTesting internal constructor( * If the [Client] attaches a [Document], the [PresenceInfo] is sent to the other peers * attached to the [Document]. */ - public val presence: PresenceInfo? = null, + public val presence: Presence? = null, /** * API key of the project used to identify the project. */ diff --git a/yorkie/src/main/kotlin/dev/yorkie/core/Peers.kt b/yorkie/src/main/kotlin/dev/yorkie/core/Peers.kt index f58f7e04c..ff58f60ca 100644 --- a/yorkie/src/main/kotlin/dev/yorkie/core/Peers.kt +++ b/yorkie/src/main/kotlin/dev/yorkie/core/Peers.kt @@ -24,6 +24,9 @@ public class Peers private constructor( } public data class PresenceInfo( - public val clock: Int, + internal val clock: Int, public val data: Map, ) + +@JvmInline +public value class Presence(val value: Map)