From d40d71dc53626246da2d4f2cd0dd111d5d41ae25 Mon Sep 17 00:00:00 2001 From: paulwinter Date: Wed, 1 Oct 2025 13:02:38 +0200 Subject: [PATCH 1/2] feat: add ChangeAuthorizationProfileMetadataValue command and test (#165) --- .../com/open200/xesar/connect/Topics.kt | 4 + .../XesarConnectAuthorizationProfileExt.kt | 32 ++++++ ...geAuthorizationProfileMetadataValueMapi.kt | 23 ++++ .../event/AuthorizationProfileInfoChanged.kt | 4 + .../messages/query/AuthorizationProfile.kt | 4 + .../query/AuthorizationProfileElementTest.kt | 3 +- .../query/AuthorizationProfileListTest.kt | 6 +- ...geAuthorizationProfileMetadataValueTest.kt | 106 ++++++++++++++++++ .../fixture/AuthorizationProfileFixture.kt | 1 + 9 files changed, 180 insertions(+), 3 deletions(-) create mode 100644 xesar-connect/src/main/kotlin/com/open200/xesar/connect/messages/command/ChangeAuthorizationProfileMetadataValueMapi.kt create mode 100644 xesar-connect/src/test/kotlin/com/open200/xesar/connect/it/command/ChangeAuthorizationProfileMetadataValueTest.kt diff --git a/xesar-connect/src/main/kotlin/com/open200/xesar/connect/Topics.kt b/xesar-connect/src/main/kotlin/com/open200/xesar/connect/Topics.kt index 49f5958d..763ee31b 100644 --- a/xesar-connect/src/main/kotlin/com/open200/xesar/connect/Topics.kt +++ b/xesar-connect/src/main/kotlin/com/open200/xesar/connect/Topics.kt @@ -480,6 +480,10 @@ class Topics(vararg val topics: String) { /** MQTT topic string for the "ChangePersonMetadataValueMapi" command. */ val CHANGE_PERSON_METADATA_VALUE = "xs3/1/cmd/ChangePersonMetadataValueMapi" + + /** MQTT topic string for the "ChangeAuthorizationProfileMetadataValueMapi" command. */ + val CHANGE_AUTHORIZATION_PROFILE_METADATA_VALUE = + "xs3/1/cmd/ChangeAuthorizationProfileMetadataValueMapi" } } diff --git a/xesar-connect/src/main/kotlin/com/open200/xesar/connect/extension/XesarConnectAuthorizationProfileExt.kt b/xesar-connect/src/main/kotlin/com/open200/xesar/connect/extension/XesarConnectAuthorizationProfileExt.kt index fa92df0e..8553adc7 100644 --- a/xesar-connect/src/main/kotlin/com/open200/xesar/connect/extension/XesarConnectAuthorizationProfileExt.kt +++ b/xesar-connect/src/main/kotlin/com/open200/xesar/connect/extension/XesarConnectAuthorizationProfileExt.kt @@ -162,6 +162,38 @@ suspend fun XesarConnect.changeAuthorizationProfileAsync( ) } +/** + * Changes the value of custom data field of an authorization profile asynchronously. + * + * @param id The ID of the authorization profile + * @param metadataId The metadataID of the data field. + * @param value The new value of the field. + * @param requestConfig The request configuration (optional). + */ +suspend fun XesarConnect.changeAuthorizationProfileMetadataValueAsync( + id: UUID, + metadataId: UUID, + value: String, + requestConfig: XesarConnect.RequestConfig = buildRequestConfig(), +): SingleEventResult { + return sendCommandAsync< + ChangeAuthorizationProfileMetadataValueMapi, + AuthorizationProfileInfoChanged, + >( + Topics.Command.CHANGE_AUTHORIZATION_PROFILE_METADATA_VALUE, + Topics.Event.AUTHORIZATION_PROFILE_INFO_CHANGED, + true, + ChangeAuthorizationProfileMetadataValueMapi( + config.uuidGenerator.generateId(), + id, + metadataId, + value, + token, + ), + requestConfig, + ) +} + /** * Retrieves a cold stream of [AuthorizationProfile] objects, fetching them incrementally in * smaller,more manageable chunks rather than retrieving the entire dataset at once. Use diff --git a/xesar-connect/src/main/kotlin/com/open200/xesar/connect/messages/command/ChangeAuthorizationProfileMetadataValueMapi.kt b/xesar-connect/src/main/kotlin/com/open200/xesar/connect/messages/command/ChangeAuthorizationProfileMetadataValueMapi.kt new file mode 100644 index 00000000..a45b9696 --- /dev/null +++ b/xesar-connect/src/main/kotlin/com/open200/xesar/connect/messages/command/ChangeAuthorizationProfileMetadataValueMapi.kt @@ -0,0 +1,23 @@ +package com.open200.xesar.connect.messages.command + +import com.open200.xesar.connect.utils.UUIDSerializer +import java.util.* +import kotlinx.serialization.Serializable + +/** + * Represents a command POJO to change a custom data field value for an authorization profile. + * + * @param commandId The id of the command. + * @param id The id of the authorization profile. + * @param metadataId The id of the custom data field. + * @param value The new value of the custom data field. + * @param token The token of the command. + */ +@Serializable +data class ChangeAuthorizationProfileMetadataValueMapi( + override val commandId: @Serializable(with = UUIDSerializer::class) UUID, + val id: @Serializable(with = UUIDSerializer::class) UUID? = null, + val metadataId: @Serializable(with = UUIDSerializer::class) UUID, + val value: String, + val token: String, +) : Command diff --git a/xesar-connect/src/main/kotlin/com/open200/xesar/connect/messages/event/AuthorizationProfileInfoChanged.kt b/xesar-connect/src/main/kotlin/com/open200/xesar/connect/messages/event/AuthorizationProfileInfoChanged.kt index aa00ea9e..b6ddc322 100644 --- a/xesar-connect/src/main/kotlin/com/open200/xesar/connect/messages/event/AuthorizationProfileInfoChanged.kt +++ b/xesar-connect/src/main/kotlin/com/open200/xesar/connect/messages/event/AuthorizationProfileInfoChanged.kt @@ -1,5 +1,6 @@ package com.open200.xesar.connect.messages.event +import com.open200.xesar.connect.messages.EntityMetadata import com.open200.xesar.connect.utils.UUIDSerializer import java.util.* import kotlinx.serialization.Serializable @@ -11,10 +12,13 @@ import kotlinx.serialization.Serializable * @param name The name of the authorization profile. * @param description The description of the authorization profile. * @param id The id of the authorization profile. + * @param entityMetadata Contains the information for all defined custom data fields for the + * authorization profile. */ @Serializable data class AuthorizationProfileInfoChanged( val name: String? = null, val description: String? = null, val id: @Serializable(with = UUIDSerializer::class) UUID? = null, + val entityMetadata: List? = null, ) : Event diff --git a/xesar-connect/src/main/kotlin/com/open200/xesar/connect/messages/query/AuthorizationProfile.kt b/xesar-connect/src/main/kotlin/com/open200/xesar/connect/messages/query/AuthorizationProfile.kt index 23558550..34c5ceca 100644 --- a/xesar-connect/src/main/kotlin/com/open200/xesar/connect/messages/query/AuthorizationProfile.kt +++ b/xesar-connect/src/main/kotlin/com/open200/xesar/connect/messages/query/AuthorizationProfile.kt @@ -1,5 +1,6 @@ package com.open200.xesar.connect.messages.query +import com.open200.xesar.connect.messages.EntityMetadata import com.open200.xesar.connect.utils.UUIDSerializer import java.util.* import kotlinx.serialization.Serializable @@ -17,6 +18,8 @@ import kotlinx.serialization.Serializable * @param anyAuthorizations Indicates if the authorization profile allows any authorizations. * @param standardTimeProfile The used standard time profile id or null for an all-day time profile. * (optional). + * @param entityMetadata Contains the information for all defined custom data fields for the + * authorization profile. */ @Serializable data class AuthorizationProfile( @@ -28,6 +31,7 @@ data class AuthorizationProfile( val manualOfficeMode: Boolean, val anyAuthorizations: Boolean, @Serializable(with = UUIDSerializer::class) val standardTimeProfile: UUID? = null, + val entityMetadata: List? = null, ) : QueryListResource, QueryElementResource { /** diff --git a/xesar-connect/src/test/kotlin/com/open200/xesar/connect/encodingDecoding/query/AuthorizationProfileElementTest.kt b/xesar-connect/src/test/kotlin/com/open200/xesar/connect/encodingDecoding/query/AuthorizationProfileElementTest.kt index 59f26206..bc5535a9 100644 --- a/xesar-connect/src/test/kotlin/com/open200/xesar/connect/encodingDecoding/query/AuthorizationProfileElementTest.kt +++ b/xesar-connect/src/test/kotlin/com/open200/xesar/connect/encodingDecoding/query/AuthorizationProfileElementTest.kt @@ -32,7 +32,8 @@ class AuthorizationProfileElementTest : "\"timeProfileId\":\"532534ef-d5aa-4cca-acfb-e558c623b00a\"}]," + "\"manualOfficeMode\":true," + "\"anyAuthorizations\":true," + - "\"standardTimeProfile\":\"a58e45f8-7bff-4b3a-bd0e-a831b3fa8053\"}}" + "\"standardTimeProfile\":\"a58e45f8-7bff-4b3a-bd0e-a831b3fa8053\"," + + "\"entityMetadata\":[]}}" test("encoding QueryListElement for an authorization profile") { val authorizationProfileEncoded = encodeQueryElement(authorizationProfileTest) diff --git a/xesar-connect/src/test/kotlin/com/open200/xesar/connect/encodingDecoding/query/AuthorizationProfileListTest.kt b/xesar-connect/src/test/kotlin/com/open200/xesar/connect/encodingDecoding/query/AuthorizationProfileListTest.kt index c159809f..1d101070 100644 --- a/xesar-connect/src/test/kotlin/com/open200/xesar/connect/encodingDecoding/query/AuthorizationProfileListTest.kt +++ b/xesar-connect/src/test/kotlin/com/open200/xesar/connect/encodingDecoding/query/AuthorizationProfileListTest.kt @@ -45,7 +45,8 @@ class AuthorizationProfileListTest : "\"timeProfileId\":\"532534ef-d5aa-4cca-acfb-e558c623b00a\"}]," + "\"manualOfficeMode\":true," + "\"anyAuthorizations\":true," + - "\"standardTimeProfile\":\"a58e45f8-7bff-4b3a-bd0e-a831b3fa8053\"}," + + "\"standardTimeProfile\":\"a58e45f8-7bff-4b3a-bd0e-a831b3fa8053\"," + + "\"entityMetadata\":[]}," + "{\"id\":\"555e7d1a-54f1-432a-ade7-80d20a63ee2d\"," + "\"name\":\"authorization profile 2 String\"," + "\"description\":\"description profile 2 String\"," + @@ -53,7 +54,8 @@ class AuthorizationProfileListTest : "\"zones\":[]," + "\"manualOfficeMode\":true," + "\"anyAuthorizations\":true," + - "\"standardTimeProfile\":\"a58e45f8-7bff-4b3a-bd0e-a831b3fa8053\"}]," + + "\"standardTimeProfile\":\"a58e45f8-7bff-4b3a-bd0e-a831b3fa8053\"," + + "\"entityMetadata\":[]}]," + "\"totalCount\":2," + "\"filterCount\":2}}" diff --git a/xesar-connect/src/test/kotlin/com/open200/xesar/connect/it/command/ChangeAuthorizationProfileMetadataValueTest.kt b/xesar-connect/src/test/kotlin/com/open200/xesar/connect/it/command/ChangeAuthorizationProfileMetadataValueTest.kt new file mode 100644 index 00000000..cc413a01 --- /dev/null +++ b/xesar-connect/src/test/kotlin/com/open200/xesar/connect/it/command/ChangeAuthorizationProfileMetadataValueTest.kt @@ -0,0 +1,106 @@ +package com.open200.xesar.connect.it.command + +import com.open200.xesar.connect.Topics +import com.open200.xesar.connect.XesarConnect +import com.open200.xesar.connect.XesarMqttClient +import com.open200.xesar.connect.extension.changeAuthorizationProfileMetadataValueAsync +import com.open200.xesar.connect.it.MosquittoContainer +import com.open200.xesar.connect.messages.EntityMetadata +import com.open200.xesar.connect.messages.event.ApiEvent +import com.open200.xesar.connect.messages.event.AuthorizationProfileInfoChanged +import com.open200.xesar.connect.messages.event.encodeEvent +import io.kotest.common.runBlocking +import io.kotest.core.spec.style.FunSpec +import io.kotest.extensions.testcontainers.perProject +import io.kotest.matchers.equals.shouldBeEqual +import io.mockk.coEvery +import java.util.* +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.launch + +class ChangeAuthorizationProfileMetadataValueTest : + FunSpec({ + val container = MosquittoContainer.container() + val config = MosquittoContainer.config(container) + listener(container.perProject()) + + test("change authorization profile metadata value") { + coEvery { config.uuidGenerator.generateId() } + .returns(UUID.fromString("00000000-1281-40ae-89d7-5c541d77a757")) + + val authorizationProfileId = UUID.fromString("11111111-2222-3333-4444-555555555555") + val metadataId = UUID.fromString("aaaaaaaa-0000-0000-0000-000000000001") + + runBlocking { + val simulatedBackendReady = CompletableDeferred() + val commandReceived = CompletableDeferred() + + launch { + XesarMqttClient.connectAsync(config).await().use { client -> + client.subscribeAsync(arrayOf(Topics.ALL_TOPICS)).await() + + client.onMessage = { topic, payload -> + when (topic) { + Topics.Command.CHANGE_AUTHORIZATION_PROFILE_METADATA_VALUE -> { + commandReceived.complete(payload.decodeToString()) + } + } + } + + simulatedBackendReady.complete(Unit) + + val commandContent = commandReceived.await() + + commandContent.shouldBeEqual( + "{\"commandId\":\"00000000-1281-40ae-89d7-5c541d77a757\",\"id\":\"11111111-2222-3333-4444-555555555555\",\"metadataId\":\"aaaaaaaa-0000-0000-0000-000000000001\",\"value\":\"Test Value\",\"token\":\"JDJhJDEwJDFSNEljZ2FaRUNXUXBTQ25XN05KbE9qRzFHQ1VjMzkvWTBVcFpZb1M4Vmt0dnJYZ0tJVFBx\"}" + ) + + val apiEvent = + ApiEvent( + UUID.fromString("00000000-1281-40ae-89d7-5c541d77a757"), + AuthorizationProfileInfoChanged( + id = authorizationProfileId, + entityMetadata = + listOf( + EntityMetadata( + id = metadataId, + name = "test", + value = "Test Value", + ) + ), + ), + ) + + client + .publishAsync( + Topics.Event.AUTHORIZATION_PROFILE_INFO_CHANGED, + encodeEvent(apiEvent), + ) + .await() + } + } + + launch { + simulatedBackendReady.await() + + val api = XesarConnect.connectAndLoginAsync(config).await() + api.subscribeAsync(Topics(Topics.Event.AUTHORIZATION_PROFILE_INFO_CHANGED)) + .await() + + val result = + api.changeAuthorizationProfileMetadataValueAsync( + id = authorizationProfileId, + metadataId = metadataId, + value = "Test Value", + ) + .await() + + result.id?.shouldBeEqual(authorizationProfileId) + result.entityMetadata!! + .single { it.id == metadataId } + .value + ?.shouldBeEqual("Test Value") + } + } + } + }) diff --git a/xesar-connect/src/test/kotlin/com/open200/xesar/connect/util/fixture/AuthorizationProfileFixture.kt b/xesar-connect/src/test/kotlin/com/open200/xesar/connect/util/fixture/AuthorizationProfileFixture.kt index 8ab63a46..339a629f 100644 --- a/xesar-connect/src/test/kotlin/com/open200/xesar/connect/util/fixture/AuthorizationProfileFixture.kt +++ b/xesar-connect/src/test/kotlin/com/open200/xesar/connect/util/fixture/AuthorizationProfileFixture.kt @@ -27,5 +27,6 @@ object AuthorizationProfileFixture { manualOfficeMode = true, anyAuthorizations = true, standardTimeProfile = UUID.fromString("a58e45f8-7bff-4b3a-bd0e-a831b3fa8053"), + entityMetadata = emptyList(), ) } From 4ce38770fd4896428d712cea1476aa559936b2d8 Mon Sep 17 00:00:00 2001 From: paulwinter Date: Fri, 10 Oct 2025 15:22:07 +0200 Subject: [PATCH 2/2] test: add an actual list of entityMetadata to the fixture and query tests (#165) --- .../query/AuthorizationProfileElementTest.kt | 2 +- .../query/AuthorizationProfileListTest.kt | 4 ++-- .../util/fixture/AuthorizationProfileFixture.kt | 15 ++++++++++++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/xesar-connect/src/test/kotlin/com/open200/xesar/connect/encodingDecoding/query/AuthorizationProfileElementTest.kt b/xesar-connect/src/test/kotlin/com/open200/xesar/connect/encodingDecoding/query/AuthorizationProfileElementTest.kt index bc5535a9..53bba639 100644 --- a/xesar-connect/src/test/kotlin/com/open200/xesar/connect/encodingDecoding/query/AuthorizationProfileElementTest.kt +++ b/xesar-connect/src/test/kotlin/com/open200/xesar/connect/encodingDecoding/query/AuthorizationProfileElementTest.kt @@ -33,7 +33,7 @@ class AuthorizationProfileElementTest : "\"manualOfficeMode\":true," + "\"anyAuthorizations\":true," + "\"standardTimeProfile\":\"a58e45f8-7bff-4b3a-bd0e-a831b3fa8053\"," + - "\"entityMetadata\":[]}}" + "\"entityMetadata\":[{\"id\":\"123e4567-e89b-12d3-a456-426614174000\",\"name\":\"type\",\"value\":\"authorization type 1\"},{\"id\":\"0f8fad5b-d9cb-469f-a165-70867728950e\",\"name\":\"number\",\"value\":null}]}}" test("encoding QueryListElement for an authorization profile") { val authorizationProfileEncoded = encodeQueryElement(authorizationProfileTest) diff --git a/xesar-connect/src/test/kotlin/com/open200/xesar/connect/encodingDecoding/query/AuthorizationProfileListTest.kt b/xesar-connect/src/test/kotlin/com/open200/xesar/connect/encodingDecoding/query/AuthorizationProfileListTest.kt index 1d101070..1f6dd502 100644 --- a/xesar-connect/src/test/kotlin/com/open200/xesar/connect/encodingDecoding/query/AuthorizationProfileListTest.kt +++ b/xesar-connect/src/test/kotlin/com/open200/xesar/connect/encodingDecoding/query/AuthorizationProfileListTest.kt @@ -46,7 +46,7 @@ class AuthorizationProfileListTest : "\"manualOfficeMode\":true," + "\"anyAuthorizations\":true," + "\"standardTimeProfile\":\"a58e45f8-7bff-4b3a-bd0e-a831b3fa8053\"," + - "\"entityMetadata\":[]}," + + "\"entityMetadata\":[{\"id\":\"123e4567-e89b-12d3-a456-426614174000\",\"name\":\"type\",\"value\":\"authorization type 1\"},{\"id\":\"0f8fad5b-d9cb-469f-a165-70867728950e\",\"name\":\"number\",\"value\":null}]}," + "{\"id\":\"555e7d1a-54f1-432a-ade7-80d20a63ee2d\"," + "\"name\":\"authorization profile 2 String\"," + "\"description\":\"description profile 2 String\"," + @@ -55,7 +55,7 @@ class AuthorizationProfileListTest : "\"manualOfficeMode\":true," + "\"anyAuthorizations\":true," + "\"standardTimeProfile\":\"a58e45f8-7bff-4b3a-bd0e-a831b3fa8053\"," + - "\"entityMetadata\":[]}]," + + "\"entityMetadata\":[{\"id\":\"123e4567-e89b-12d3-a456-426614174000\",\"name\":\"type\",\"value\":\"authorization type 1\"},{\"id\":\"0f8fad5b-d9cb-469f-a165-70867728950e\",\"name\":\"number\",\"value\":null}]}]," + "\"totalCount\":2," + "\"filterCount\":2}}" diff --git a/xesar-connect/src/test/kotlin/com/open200/xesar/connect/util/fixture/AuthorizationProfileFixture.kt b/xesar-connect/src/test/kotlin/com/open200/xesar/connect/util/fixture/AuthorizationProfileFixture.kt index 339a629f..fc75df45 100644 --- a/xesar-connect/src/test/kotlin/com/open200/xesar/connect/util/fixture/AuthorizationProfileFixture.kt +++ b/xesar-connect/src/test/kotlin/com/open200/xesar/connect/util/fixture/AuthorizationProfileFixture.kt @@ -1,5 +1,6 @@ package com.open200.xesar.connect.util.fixture +import com.open200.xesar.connect.messages.EntityMetadata import com.open200.xesar.connect.messages.query.AuthorizationProfile import java.util.* @@ -27,6 +28,18 @@ object AuthorizationProfileFixture { manualOfficeMode = true, anyAuthorizations = true, standardTimeProfile = UUID.fromString("a58e45f8-7bff-4b3a-bd0e-a831b3fa8053"), - entityMetadata = emptyList(), + entityMetadata = + listOf( + EntityMetadata( + id = UUID.fromString("123e4567-e89b-12d3-a456-426614174000"), + name = "type", + value = "authorization type 1", + ), + EntityMetadata( + id = UUID.fromString("0f8fad5b-d9cb-469f-a165-70867728950e"), + name = "number", + value = null, + ), + ), ) }