From c834029450215a8303a4875c243ce1b24297c7f0 Mon Sep 17 00:00:00 2001 From: paulwinter Date: Tue, 30 Sep 2025 14:11:13 +0200 Subject: [PATCH 1/2] feat: add ChangeMediumMetadataValue command and test (#162) --- .../com/open200/xesar/connect/Topics.kt | 3 + .../XesarConnectIdentificationMediumExt.kt | 29 +++++ .../command/ChangeMediumMetadataValueMapi.kt | 23 ++++ .../connect/messages/event/MediumChanged.kt | 3 + .../messages/query/IdentificationMedium.kt | 3 + .../query/IdentificationMediumElementTest.kt | 2 +- .../query/IdentificationMediumListTest.kt | 2 +- .../command/ChangeMediumMetadataValueTest.kt | 104 ++++++++++++++++++ .../fixture/IdentificationMediumFixture.kt | 1 + 9 files changed, 168 insertions(+), 2 deletions(-) create mode 100644 xesar-connect/src/main/kotlin/com/open200/xesar/connect/messages/command/ChangeMediumMetadataValueMapi.kt create mode 100644 xesar-connect/src/test/kotlin/com/open200/xesar/connect/it/command/ChangeMediumMetadataValueTest.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..651a0eb5 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,9 @@ 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 "ChangeMediumMetadataValueMapi" command. */ + val CHANGE_MEDIUM_METADATA_VALUE = "xs3/1/cmd/ChangeMediumMetadataValueMapi" } } diff --git a/xesar-connect/src/main/kotlin/com/open200/xesar/connect/extension/XesarConnectIdentificationMediumExt.kt b/xesar-connect/src/main/kotlin/com/open200/xesar/connect/extension/XesarConnectIdentificationMediumExt.kt index b1c1fdfb..c305ebb3 100644 --- a/xesar-connect/src/main/kotlin/com/open200/xesar/connect/extension/XesarConnectIdentificationMediumExt.kt +++ b/xesar-connect/src/main/kotlin/com/open200/xesar/connect/extension/XesarConnectIdentificationMediumExt.kt @@ -482,6 +482,35 @@ suspend fun XesarConnect.requestToAddMediumToInstallationAsync( ) } +/** + * Changes the value of custom data field of a medium asynchronously. + * + * @param id The ID of the medium. + * @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.changeMediumMetadataValueAsync( + id: UUID, + metadataId: UUID, + value: String, + requestConfig: XesarConnect.RequestConfig = buildRequestConfig(), +): SingleEventResult { + return sendCommandAsync( + Topics.Command.CHANGE_MEDIUM_METADATA_VALUE, + Topics.Event.MEDIUM_CHANGED, + true, + ChangeMediumMetadataValueMapi( + config.uuidGenerator.generateId(), + id, + metadataId, + value, + token, + ), + requestConfig, + ) +} + /** * Retrieves a cold stream of [IdentificationMedium] 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/ChangeMediumMetadataValueMapi.kt b/xesar-connect/src/main/kotlin/com/open200/xesar/connect/messages/command/ChangeMediumMetadataValueMapi.kt new file mode 100644 index 00000000..2db36415 --- /dev/null +++ b/xesar-connect/src/main/kotlin/com/open200/xesar/connect/messages/command/ChangeMediumMetadataValueMapi.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 a medium. + * + * @param commandId The id of the command. + * @param id The id of the medium. + * @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 ChangeMediumMetadataValueMapi( + 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/MediumChanged.kt b/xesar-connect/src/main/kotlin/com/open200/xesar/connect/messages/event/MediumChanged.kt index 8f3f09f2..cf144f18 100644 --- a/xesar-connect/src/main/kotlin/com/open200/xesar/connect/messages/event/MediumChanged.kt +++ b/xesar-connect/src/main/kotlin/com/open200/xesar/connect/messages/event/MediumChanged.kt @@ -1,6 +1,7 @@ package com.open200.xesar.connect.messages.event import com.open200.xesar.connect.messages.DisengagePeriod +import com.open200.xesar.connect.messages.EntityMetadata import com.open200.xesar.connect.utils.LocalDateTimeSerializer import com.open200.xesar.connect.utils.UUIDSerializer import java.time.LocalDateTime @@ -22,6 +23,7 @@ import kotlinx.serialization.Serializable * @param accessEndAt The access end date of the medium. * @param phoneNumber The phone number of the medium (smartphone). * @param messageLanguage The message language of the medium (smartphone). + * @param entityMetadata Contains the information for all defined custom data fields for the medium. */ @Serializable data class MediumChanged( @@ -37,4 +39,5 @@ data class MediumChanged( val accessEndAt: @Serializable(with = LocalDateTimeSerializer::class) LocalDateTime? = null, val phoneNumber: String? = null, val messageLanguage: String? = null, + val entityMetadata: List? = null, ) : Event diff --git a/xesar-connect/src/main/kotlin/com/open200/xesar/connect/messages/query/IdentificationMedium.kt b/xesar-connect/src/main/kotlin/com/open200/xesar/connect/messages/query/IdentificationMedium.kt index 647beebc..ca1e32e4 100644 --- a/xesar-connect/src/main/kotlin/com/open200/xesar/connect/messages/query/IdentificationMedium.kt +++ b/xesar-connect/src/main/kotlin/com/open200/xesar/connect/messages/query/IdentificationMedium.kt @@ -1,6 +1,7 @@ package com.open200.xesar.connect.messages.query import com.open200.xesar.connect.messages.DisengagePeriod +import com.open200.xesar.connect.messages.EntityMetadata import com.open200.xesar.connect.utils.LocalDateTimeSerializer import com.open200.xesar.connect.utils.UUIDSerializer import java.time.LocalDateTime @@ -54,6 +55,7 @@ import kotlinx.serialization.Serializable * @param registrationCodeValidUntil The expiration timestamp of the smartphone registration code * (optional). * @param messageLanguage The language for correspondence (optional). + * @param entityMetadata Contains the information for all defined custom data fields for the medium. */ @Serializable data class IdentificationMedium( @@ -96,6 +98,7 @@ data class IdentificationMedium( @Serializable(with = LocalDateTimeSerializer::class) val registrationCodeValidUntil: LocalDateTime? = null, val messageLanguage: String? = null, + val entityMetadata: List? = null, ) : QueryListResource, QueryElementResource { companion object { diff --git a/xesar-connect/src/test/kotlin/com/open200/xesar/connect/encodingDecoding/query/IdentificationMediumElementTest.kt b/xesar-connect/src/test/kotlin/com/open200/xesar/connect/encodingDecoding/query/IdentificationMediumElementTest.kt index faf32365..0b89415e 100644 --- a/xesar-connect/src/test/kotlin/com/open200/xesar/connect/encodingDecoding/query/IdentificationMediumElementTest.kt +++ b/xesar-connect/src/test/kotlin/com/open200/xesar/connect/encodingDecoding/query/IdentificationMediumElementTest.kt @@ -15,7 +15,7 @@ class IdentificationMediumElementTest : ) val identificationMediumString = - "{\"requestId\":\"00000000-1281-42c0-9a15-c5844850c748\",\"response\":{\"id\":\"8293e920-90ce-48da-851c-cff54a13e2c6\",\"label\":\"test door\",\"issuedAt\":\"2023-07-05T15:22:13.509825\",\"syncedAt\":\"2023-07-05T15:22:38.230076\",\"validityDuration\":151,\"authorizationProfileId\":\"d08fdd62-bc36-4e47-8bc9-62b603e75ed9\",\"authorizationProfileName\":\"Authorization Profile 1\",\"individualAuthorizationProfileIds\":[\"3dba6935-6904-4bc0-99d3-8115c9bbbedc\",\"66b9f5d9-5664-4bb9-9546-af315987752b\"],\"mediumState\":\"ACTIVE\",\"accessBeginAt\":\"2021-01-01T00:00:00\",\"accessEndAt\":\"2021-01-01T00:00:00\",\"validityBeginAt\":\"2021-01-01T00:00:00\",\"validityEndAt\":\"2021-01-01T00:00:00\",\"validityBeginAtInHardware\":\"2021-01-01T00:00:00\",\"validityEndAtInHardware\":\"2021-01-01T00:00:00\",\"external\":false,\"disengagePeriod\":\"SHORT\",\"mediumIdentifier\":1,\"outdated\":true,\"personId\":\"82a8a2cc-5d39-4cfa-b04e-49111a0bcdf7\",\"person\":\"test Person\",\"hardwareId\":\"f45ebc226706800e1cf942c995d0a43db78ec563173fb79a43eedbfbd8f29222\",\"nativeId\":\"046e0b8a967280\",\"secure\":true,\"softwareStatus\":\"ACTIVE\",\"hardwareStatus\":\"ACTIVE\",\"fitsOnHardware\":true,\"userId\":\"91781b22-ebdf-4ada-80cf-91f1fb9a4d96\",\"userName\":\"test User\",\"requiredAction\":\"UPDATE\",\"mediumType\":\"PASSIVE\",\"phoneNumber\":null,\"registrationState\":null,\"registrationCode\":null,\"registrationCodeValidUntil\":null,\"messageLanguage\":null}}" + "{\"requestId\":\"00000000-1281-42c0-9a15-c5844850c748\",\"response\":{\"id\":\"8293e920-90ce-48da-851c-cff54a13e2c6\",\"label\":\"test door\",\"issuedAt\":\"2023-07-05T15:22:13.509825\",\"syncedAt\":\"2023-07-05T15:22:38.230076\",\"validityDuration\":151,\"authorizationProfileId\":\"d08fdd62-bc36-4e47-8bc9-62b603e75ed9\",\"authorizationProfileName\":\"Authorization Profile 1\",\"individualAuthorizationProfileIds\":[\"3dba6935-6904-4bc0-99d3-8115c9bbbedc\",\"66b9f5d9-5664-4bb9-9546-af315987752b\"],\"mediumState\":\"ACTIVE\",\"accessBeginAt\":\"2021-01-01T00:00:00\",\"accessEndAt\":\"2021-01-01T00:00:00\",\"validityBeginAt\":\"2021-01-01T00:00:00\",\"validityEndAt\":\"2021-01-01T00:00:00\",\"validityBeginAtInHardware\":\"2021-01-01T00:00:00\",\"validityEndAtInHardware\":\"2021-01-01T00:00:00\",\"external\":false,\"disengagePeriod\":\"SHORT\",\"mediumIdentifier\":1,\"outdated\":true,\"personId\":\"82a8a2cc-5d39-4cfa-b04e-49111a0bcdf7\",\"person\":\"test Person\",\"hardwareId\":\"f45ebc226706800e1cf942c995d0a43db78ec563173fb79a43eedbfbd8f29222\",\"nativeId\":\"046e0b8a967280\",\"secure\":true,\"softwareStatus\":\"ACTIVE\",\"hardwareStatus\":\"ACTIVE\",\"fitsOnHardware\":true,\"userId\":\"91781b22-ebdf-4ada-80cf-91f1fb9a4d96\",\"userName\":\"test User\",\"requiredAction\":\"UPDATE\",\"mediumType\":\"PASSIVE\",\"phoneNumber\":null,\"registrationState\":null,\"registrationCode\":null,\"registrationCodeValidUntil\":null,\"messageLanguage\":null,\"entityMetadata\":[]}}" test("encoding QueryResponseElement for an identification medium") { val identificationMediumEncoded = encodeQueryElement(identificationMedium) diff --git a/xesar-connect/src/test/kotlin/com/open200/xesar/connect/encodingDecoding/query/IdentificationMediumListTest.kt b/xesar-connect/src/test/kotlin/com/open200/xesar/connect/encodingDecoding/query/IdentificationMediumListTest.kt index 4071aae4..7b967342 100644 --- a/xesar-connect/src/test/kotlin/com/open200/xesar/connect/encodingDecoding/query/IdentificationMediumListTest.kt +++ b/xesar-connect/src/test/kotlin/com/open200/xesar/connect/encodingDecoding/query/IdentificationMediumListTest.kt @@ -24,7 +24,7 @@ class IdentificationMediumListTest : ) val identificationMediumString = - "{\"requestId\":\"00000000-1281-42c0-9a15-c5844850c748\",\"response\":{\"data\":[{\"id\":\"8293e920-90ce-48da-851c-cff54a13e2c6\",\"label\":\"test door\",\"issuedAt\":\"2023-07-05T15:22:13.509825\",\"syncedAt\":\"2023-07-05T15:22:38.230076\",\"validityDuration\":151,\"authorizationProfileId\":\"d08fdd62-bc36-4e47-8bc9-62b603e75ed9\",\"authorizationProfileName\":\"Authorization Profile 1\",\"individualAuthorizationProfileIds\":[\"3dba6935-6904-4bc0-99d3-8115c9bbbedc\",\"66b9f5d9-5664-4bb9-9546-af315987752b\"],\"mediumState\":\"ACTIVE\",\"accessBeginAt\":\"2021-01-01T00:00:00\",\"accessEndAt\":\"2021-01-01T00:00:00\",\"validityBeginAt\":\"2021-01-01T00:00:00\",\"validityEndAt\":\"2021-01-01T00:00:00\",\"validityBeginAtInHardware\":\"2021-01-01T00:00:00\",\"validityEndAtInHardware\":\"2021-01-01T00:00:00\",\"external\":false,\"disengagePeriod\":\"SHORT\",\"mediumIdentifier\":1,\"outdated\":true,\"personId\":\"82a8a2cc-5d39-4cfa-b04e-49111a0bcdf7\",\"person\":\"test Person\",\"hardwareId\":\"f45ebc226706800e1cf942c995d0a43db78ec563173fb79a43eedbfbd8f29222\",\"nativeId\":\"046e0b8a967280\",\"secure\":true,\"softwareStatus\":\"ACTIVE\",\"hardwareStatus\":\"ACTIVE\",\"fitsOnHardware\":true,\"userId\":\"91781b22-ebdf-4ada-80cf-91f1fb9a4d96\",\"userName\":\"test User\",\"requiredAction\":\"UPDATE\",\"mediumType\":\"PASSIVE\",\"phoneNumber\":null,\"registrationState\":null,\"registrationCode\":null,\"registrationCodeValidUntil\":null,\"messageLanguage\":null},{\"id\":\"8293e920-90ce-48da-851c-cff54a13e2c6\",\"label\":\"test door\",\"issuedAt\":\"2023-07-05T15:22:13.509825\",\"syncedAt\":\"2023-07-05T15:22:38.230076\",\"validityDuration\":151,\"authorizationProfileId\":\"d08fdd62-bc36-4e47-8bc9-62b603e75ed9\",\"authorizationProfileName\":\"Authorization Profile 1\",\"individualAuthorizationProfileIds\":[\"3dba6935-6904-4bc0-99d3-8115c9bbbedc\",\"66b9f5d9-5664-4bb9-9546-af315987752b\"],\"mediumState\":\"ACTIVE\",\"accessBeginAt\":\"2021-01-01T00:00:00\",\"accessEndAt\":\"2021-01-01T00:00:00\",\"validityBeginAt\":\"2021-01-01T00:00:00\",\"validityEndAt\":\"2021-01-01T00:00:00\",\"validityBeginAtInHardware\":\"2021-01-01T00:00:00\",\"validityEndAtInHardware\":\"2021-01-01T00:00:00\",\"external\":false,\"disengagePeriod\":\"SHORT\",\"mediumIdentifier\":1,\"outdated\":true,\"personId\":\"82a8a2cc-5d39-4cfa-b04e-49111a0bcdf7\",\"person\":\"test Person\",\"hardwareId\":\"f45ebc226706800e1cf942c995d0a43db78ec563173fb79a43eedbfbd8f29222\",\"nativeId\":\"046e0b8a967280\",\"secure\":true,\"softwareStatus\":\"ACTIVE\",\"hardwareStatus\":\"ACTIVE\",\"fitsOnHardware\":true,\"userId\":\"91781b22-ebdf-4ada-80cf-91f1fb9a4d96\",\"userName\":\"test User\",\"requiredAction\":\"UPDATE\",\"mediumType\":\"PASSIVE\",\"phoneNumber\":null,\"registrationState\":null,\"registrationCode\":null,\"registrationCodeValidUntil\":null,\"messageLanguage\":null}],\"totalCount\":2,\"filterCount\":2}}" + "{\"requestId\":\"00000000-1281-42c0-9a15-c5844850c748\",\"response\":{\"data\":[{\"id\":\"8293e920-90ce-48da-851c-cff54a13e2c6\",\"label\":\"test door\",\"issuedAt\":\"2023-07-05T15:22:13.509825\",\"syncedAt\":\"2023-07-05T15:22:38.230076\",\"validityDuration\":151,\"authorizationProfileId\":\"d08fdd62-bc36-4e47-8bc9-62b603e75ed9\",\"authorizationProfileName\":\"Authorization Profile 1\",\"individualAuthorizationProfileIds\":[\"3dba6935-6904-4bc0-99d3-8115c9bbbedc\",\"66b9f5d9-5664-4bb9-9546-af315987752b\"],\"mediumState\":\"ACTIVE\",\"accessBeginAt\":\"2021-01-01T00:00:00\",\"accessEndAt\":\"2021-01-01T00:00:00\",\"validityBeginAt\":\"2021-01-01T00:00:00\",\"validityEndAt\":\"2021-01-01T00:00:00\",\"validityBeginAtInHardware\":\"2021-01-01T00:00:00\",\"validityEndAtInHardware\":\"2021-01-01T00:00:00\",\"external\":false,\"disengagePeriod\":\"SHORT\",\"mediumIdentifier\":1,\"outdated\":true,\"personId\":\"82a8a2cc-5d39-4cfa-b04e-49111a0bcdf7\",\"person\":\"test Person\",\"hardwareId\":\"f45ebc226706800e1cf942c995d0a43db78ec563173fb79a43eedbfbd8f29222\",\"nativeId\":\"046e0b8a967280\",\"secure\":true,\"softwareStatus\":\"ACTIVE\",\"hardwareStatus\":\"ACTIVE\",\"fitsOnHardware\":true,\"userId\":\"91781b22-ebdf-4ada-80cf-91f1fb9a4d96\",\"userName\":\"test User\",\"requiredAction\":\"UPDATE\",\"mediumType\":\"PASSIVE\",\"phoneNumber\":null,\"registrationState\":null,\"registrationCode\":null,\"registrationCodeValidUntil\":null,\"messageLanguage\":null,\"entityMetadata\":[]},{\"id\":\"8293e920-90ce-48da-851c-cff54a13e2c6\",\"label\":\"test door\",\"issuedAt\":\"2023-07-05T15:22:13.509825\",\"syncedAt\":\"2023-07-05T15:22:38.230076\",\"validityDuration\":151,\"authorizationProfileId\":\"d08fdd62-bc36-4e47-8bc9-62b603e75ed9\",\"authorizationProfileName\":\"Authorization Profile 1\",\"individualAuthorizationProfileIds\":[\"3dba6935-6904-4bc0-99d3-8115c9bbbedc\",\"66b9f5d9-5664-4bb9-9546-af315987752b\"],\"mediumState\":\"ACTIVE\",\"accessBeginAt\":\"2021-01-01T00:00:00\",\"accessEndAt\":\"2021-01-01T00:00:00\",\"validityBeginAt\":\"2021-01-01T00:00:00\",\"validityEndAt\":\"2021-01-01T00:00:00\",\"validityBeginAtInHardware\":\"2021-01-01T00:00:00\",\"validityEndAtInHardware\":\"2021-01-01T00:00:00\",\"external\":false,\"disengagePeriod\":\"SHORT\",\"mediumIdentifier\":1,\"outdated\":true,\"personId\":\"82a8a2cc-5d39-4cfa-b04e-49111a0bcdf7\",\"person\":\"test Person\",\"hardwareId\":\"f45ebc226706800e1cf942c995d0a43db78ec563173fb79a43eedbfbd8f29222\",\"nativeId\":\"046e0b8a967280\",\"secure\":true,\"softwareStatus\":\"ACTIVE\",\"hardwareStatus\":\"ACTIVE\",\"fitsOnHardware\":true,\"userId\":\"91781b22-ebdf-4ada-80cf-91f1fb9a4d96\",\"userName\":\"test User\",\"requiredAction\":\"UPDATE\",\"mediumType\":\"PASSIVE\",\"phoneNumber\":null,\"registrationState\":null,\"registrationCode\":null,\"registrationCodeValidUntil\":null,\"messageLanguage\":null,\"entityMetadata\":[]}],\"totalCount\":2,\"filterCount\":2}}" test("encoding QueryResponseElement for an identification medium") { val identificationMediumEncoded = encodeQueryList(identificationMediumList) diff --git a/xesar-connect/src/test/kotlin/com/open200/xesar/connect/it/command/ChangeMediumMetadataValueTest.kt b/xesar-connect/src/test/kotlin/com/open200/xesar/connect/it/command/ChangeMediumMetadataValueTest.kt new file mode 100644 index 00000000..9adf34b5 --- /dev/null +++ b/xesar-connect/src/test/kotlin/com/open200/xesar/connect/it/command/ChangeMediumMetadataValueTest.kt @@ -0,0 +1,104 @@ +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.changeMediumMetadataValueAsync +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.MediumChanged +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.time.LocalDateTime +import java.util.UUID +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.launch + +class ChangeMediumMetadataValueTest : + FunSpec({ + val container = MosquittoContainer.container() + val config = MosquittoContainer.config(container) + listener(container.perProject()) + + test("change medium metadata value") { + coEvery { config.uuidGenerator.generateId() } + .returns(UUID.fromString("00000000-1281-40ae-89d7-5c541d77a757")) + + val mediumId = 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_MEDIUM_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\":\"Vogons\",\"token\":\"JDJhJDEwJDFSNEljZ2FaRUNXUXBTQ25XN05KbE9qRzFHQ1VjMzkvWTBVcFpZb1M4Vmt0dnJYZ0tJVFBx\"}" + ) + + val apiEvent = + ApiEvent( + UUID.fromString("00000000-1281-40ae-89d7-5c541d77a757"), + MediumChanged( + id = mediumId, + changedAt = LocalDateTime.parse("2023-08-23T16:25:52.225991"), + entityMetadata = + listOf( + EntityMetadata( + id = metadataId, + name = "Group", + value = "Vogons", + ) + ), + ), + ) + + client + .publishAsync(Topics.Event.MEDIUM_CHANGED, encodeEvent(apiEvent)) + .await() + } + } + + launch { + simulatedBackendReady.await() + + val api = XesarConnect.connectAndLoginAsync(config).await() + api.subscribeAsync(Topics(Topics.Event.MEDIUM_CHANGED)).await() + + val result = + api.changeMediumMetadataValueAsync( + id = mediumId, + metadataId = metadataId, + value = "Vogons", + ) + .await() + + result.id.shouldBeEqual(mediumId) + result.entityMetadata!! + .single { it.id == metadataId } + .value + ?.shouldBeEqual("Vogons") + } + } + } + }) diff --git a/xesar-connect/src/test/kotlin/com/open200/xesar/connect/util/fixture/IdentificationMediumFixture.kt b/xesar-connect/src/test/kotlin/com/open200/xesar/connect/util/fixture/IdentificationMediumFixture.kt index 61c033d6..6212ee68 100644 --- a/xesar-connect/src/test/kotlin/com/open200/xesar/connect/util/fixture/IdentificationMediumFixture.kt +++ b/xesar-connect/src/test/kotlin/com/open200/xesar/connect/util/fixture/IdentificationMediumFixture.kt @@ -44,5 +44,6 @@ object IdentificationMediumFixture { userName = "test User", requiredAction = "UPDATE", mediumType = IdentificationMedium.MediumType.PASSIVE, + entityMetadata = emptyList(), ) } From 3e1a8e4b29d149d32cf2ff1a3523f4a1577c15ec Mon Sep 17 00:00:00 2001 From: paulwinter Date: Tue, 7 Oct 2025 12:52:13 +0200 Subject: [PATCH 2/2] test: add an actual list of entityMetadata to the fixture and query tests (#162) --- .../query/IdentificationMediumElementTest.kt | 2 +- .../query/IdentificationMediumListTest.kt | 2 +- .../util/fixture/IdentificationMediumFixture.kt | 15 ++++++++++++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/xesar-connect/src/test/kotlin/com/open200/xesar/connect/encodingDecoding/query/IdentificationMediumElementTest.kt b/xesar-connect/src/test/kotlin/com/open200/xesar/connect/encodingDecoding/query/IdentificationMediumElementTest.kt index 0b89415e..8ab9dd8c 100644 --- a/xesar-connect/src/test/kotlin/com/open200/xesar/connect/encodingDecoding/query/IdentificationMediumElementTest.kt +++ b/xesar-connect/src/test/kotlin/com/open200/xesar/connect/encodingDecoding/query/IdentificationMediumElementTest.kt @@ -15,7 +15,7 @@ class IdentificationMediumElementTest : ) val identificationMediumString = - "{\"requestId\":\"00000000-1281-42c0-9a15-c5844850c748\",\"response\":{\"id\":\"8293e920-90ce-48da-851c-cff54a13e2c6\",\"label\":\"test door\",\"issuedAt\":\"2023-07-05T15:22:13.509825\",\"syncedAt\":\"2023-07-05T15:22:38.230076\",\"validityDuration\":151,\"authorizationProfileId\":\"d08fdd62-bc36-4e47-8bc9-62b603e75ed9\",\"authorizationProfileName\":\"Authorization Profile 1\",\"individualAuthorizationProfileIds\":[\"3dba6935-6904-4bc0-99d3-8115c9bbbedc\",\"66b9f5d9-5664-4bb9-9546-af315987752b\"],\"mediumState\":\"ACTIVE\",\"accessBeginAt\":\"2021-01-01T00:00:00\",\"accessEndAt\":\"2021-01-01T00:00:00\",\"validityBeginAt\":\"2021-01-01T00:00:00\",\"validityEndAt\":\"2021-01-01T00:00:00\",\"validityBeginAtInHardware\":\"2021-01-01T00:00:00\",\"validityEndAtInHardware\":\"2021-01-01T00:00:00\",\"external\":false,\"disengagePeriod\":\"SHORT\",\"mediumIdentifier\":1,\"outdated\":true,\"personId\":\"82a8a2cc-5d39-4cfa-b04e-49111a0bcdf7\",\"person\":\"test Person\",\"hardwareId\":\"f45ebc226706800e1cf942c995d0a43db78ec563173fb79a43eedbfbd8f29222\",\"nativeId\":\"046e0b8a967280\",\"secure\":true,\"softwareStatus\":\"ACTIVE\",\"hardwareStatus\":\"ACTIVE\",\"fitsOnHardware\":true,\"userId\":\"91781b22-ebdf-4ada-80cf-91f1fb9a4d96\",\"userName\":\"test User\",\"requiredAction\":\"UPDATE\",\"mediumType\":\"PASSIVE\",\"phoneNumber\":null,\"registrationState\":null,\"registrationCode\":null,\"registrationCodeValidUntil\":null,\"messageLanguage\":null,\"entityMetadata\":[]}}" + "{\"requestId\":\"00000000-1281-42c0-9a15-c5844850c748\",\"response\":{\"id\":\"8293e920-90ce-48da-851c-cff54a13e2c6\",\"label\":\"test door\",\"issuedAt\":\"2023-07-05T15:22:13.509825\",\"syncedAt\":\"2023-07-05T15:22:38.230076\",\"validityDuration\":151,\"authorizationProfileId\":\"d08fdd62-bc36-4e47-8bc9-62b603e75ed9\",\"authorizationProfileName\":\"Authorization Profile 1\",\"individualAuthorizationProfileIds\":[\"3dba6935-6904-4bc0-99d3-8115c9bbbedc\",\"66b9f5d9-5664-4bb9-9546-af315987752b\"],\"mediumState\":\"ACTIVE\",\"accessBeginAt\":\"2021-01-01T00:00:00\",\"accessEndAt\":\"2021-01-01T00:00:00\",\"validityBeginAt\":\"2021-01-01T00:00:00\",\"validityEndAt\":\"2021-01-01T00:00:00\",\"validityBeginAtInHardware\":\"2021-01-01T00:00:00\",\"validityEndAtInHardware\":\"2021-01-01T00:00:00\",\"external\":false,\"disengagePeriod\":\"SHORT\",\"mediumIdentifier\":1,\"outdated\":true,\"personId\":\"82a8a2cc-5d39-4cfa-b04e-49111a0bcdf7\",\"person\":\"test Person\",\"hardwareId\":\"f45ebc226706800e1cf942c995d0a43db78ec563173fb79a43eedbfbd8f29222\",\"nativeId\":\"046e0b8a967280\",\"secure\":true,\"softwareStatus\":\"ACTIVE\",\"hardwareStatus\":\"ACTIVE\",\"fitsOnHardware\":true,\"userId\":\"91781b22-ebdf-4ada-80cf-91f1fb9a4d96\",\"userName\":\"test User\",\"requiredAction\":\"UPDATE\",\"mediumType\":\"PASSIVE\",\"phoneNumber\":null,\"registrationState\":null,\"registrationCode\":null,\"registrationCodeValidUntil\":null,\"messageLanguage\":null,\"entityMetadata\":[{\"id\":\"123e4567-e89b-12d3-a456-426614174000\",\"name\":\"type\",\"value\":\"card\"},{\"id\":\"0f8fad5b-d9cb-469f-a165-70867728950e\",\"name\":\"amount\",\"value\":null}]}}" test("encoding QueryResponseElement for an identification medium") { val identificationMediumEncoded = encodeQueryElement(identificationMedium) diff --git a/xesar-connect/src/test/kotlin/com/open200/xesar/connect/encodingDecoding/query/IdentificationMediumListTest.kt b/xesar-connect/src/test/kotlin/com/open200/xesar/connect/encodingDecoding/query/IdentificationMediumListTest.kt index 7b967342..ba0ba2ea 100644 --- a/xesar-connect/src/test/kotlin/com/open200/xesar/connect/encodingDecoding/query/IdentificationMediumListTest.kt +++ b/xesar-connect/src/test/kotlin/com/open200/xesar/connect/encodingDecoding/query/IdentificationMediumListTest.kt @@ -24,7 +24,7 @@ class IdentificationMediumListTest : ) val identificationMediumString = - "{\"requestId\":\"00000000-1281-42c0-9a15-c5844850c748\",\"response\":{\"data\":[{\"id\":\"8293e920-90ce-48da-851c-cff54a13e2c6\",\"label\":\"test door\",\"issuedAt\":\"2023-07-05T15:22:13.509825\",\"syncedAt\":\"2023-07-05T15:22:38.230076\",\"validityDuration\":151,\"authorizationProfileId\":\"d08fdd62-bc36-4e47-8bc9-62b603e75ed9\",\"authorizationProfileName\":\"Authorization Profile 1\",\"individualAuthorizationProfileIds\":[\"3dba6935-6904-4bc0-99d3-8115c9bbbedc\",\"66b9f5d9-5664-4bb9-9546-af315987752b\"],\"mediumState\":\"ACTIVE\",\"accessBeginAt\":\"2021-01-01T00:00:00\",\"accessEndAt\":\"2021-01-01T00:00:00\",\"validityBeginAt\":\"2021-01-01T00:00:00\",\"validityEndAt\":\"2021-01-01T00:00:00\",\"validityBeginAtInHardware\":\"2021-01-01T00:00:00\",\"validityEndAtInHardware\":\"2021-01-01T00:00:00\",\"external\":false,\"disengagePeriod\":\"SHORT\",\"mediumIdentifier\":1,\"outdated\":true,\"personId\":\"82a8a2cc-5d39-4cfa-b04e-49111a0bcdf7\",\"person\":\"test Person\",\"hardwareId\":\"f45ebc226706800e1cf942c995d0a43db78ec563173fb79a43eedbfbd8f29222\",\"nativeId\":\"046e0b8a967280\",\"secure\":true,\"softwareStatus\":\"ACTIVE\",\"hardwareStatus\":\"ACTIVE\",\"fitsOnHardware\":true,\"userId\":\"91781b22-ebdf-4ada-80cf-91f1fb9a4d96\",\"userName\":\"test User\",\"requiredAction\":\"UPDATE\",\"mediumType\":\"PASSIVE\",\"phoneNumber\":null,\"registrationState\":null,\"registrationCode\":null,\"registrationCodeValidUntil\":null,\"messageLanguage\":null,\"entityMetadata\":[]},{\"id\":\"8293e920-90ce-48da-851c-cff54a13e2c6\",\"label\":\"test door\",\"issuedAt\":\"2023-07-05T15:22:13.509825\",\"syncedAt\":\"2023-07-05T15:22:38.230076\",\"validityDuration\":151,\"authorizationProfileId\":\"d08fdd62-bc36-4e47-8bc9-62b603e75ed9\",\"authorizationProfileName\":\"Authorization Profile 1\",\"individualAuthorizationProfileIds\":[\"3dba6935-6904-4bc0-99d3-8115c9bbbedc\",\"66b9f5d9-5664-4bb9-9546-af315987752b\"],\"mediumState\":\"ACTIVE\",\"accessBeginAt\":\"2021-01-01T00:00:00\",\"accessEndAt\":\"2021-01-01T00:00:00\",\"validityBeginAt\":\"2021-01-01T00:00:00\",\"validityEndAt\":\"2021-01-01T00:00:00\",\"validityBeginAtInHardware\":\"2021-01-01T00:00:00\",\"validityEndAtInHardware\":\"2021-01-01T00:00:00\",\"external\":false,\"disengagePeriod\":\"SHORT\",\"mediumIdentifier\":1,\"outdated\":true,\"personId\":\"82a8a2cc-5d39-4cfa-b04e-49111a0bcdf7\",\"person\":\"test Person\",\"hardwareId\":\"f45ebc226706800e1cf942c995d0a43db78ec563173fb79a43eedbfbd8f29222\",\"nativeId\":\"046e0b8a967280\",\"secure\":true,\"softwareStatus\":\"ACTIVE\",\"hardwareStatus\":\"ACTIVE\",\"fitsOnHardware\":true,\"userId\":\"91781b22-ebdf-4ada-80cf-91f1fb9a4d96\",\"userName\":\"test User\",\"requiredAction\":\"UPDATE\",\"mediumType\":\"PASSIVE\",\"phoneNumber\":null,\"registrationState\":null,\"registrationCode\":null,\"registrationCodeValidUntil\":null,\"messageLanguage\":null,\"entityMetadata\":[]}],\"totalCount\":2,\"filterCount\":2}}" + "{\"requestId\":\"00000000-1281-42c0-9a15-c5844850c748\",\"response\":{\"data\":[{\"id\":\"8293e920-90ce-48da-851c-cff54a13e2c6\",\"label\":\"test door\",\"issuedAt\":\"2023-07-05T15:22:13.509825\",\"syncedAt\":\"2023-07-05T15:22:38.230076\",\"validityDuration\":151,\"authorizationProfileId\":\"d08fdd62-bc36-4e47-8bc9-62b603e75ed9\",\"authorizationProfileName\":\"Authorization Profile 1\",\"individualAuthorizationProfileIds\":[\"3dba6935-6904-4bc0-99d3-8115c9bbbedc\",\"66b9f5d9-5664-4bb9-9546-af315987752b\"],\"mediumState\":\"ACTIVE\",\"accessBeginAt\":\"2021-01-01T00:00:00\",\"accessEndAt\":\"2021-01-01T00:00:00\",\"validityBeginAt\":\"2021-01-01T00:00:00\",\"validityEndAt\":\"2021-01-01T00:00:00\",\"validityBeginAtInHardware\":\"2021-01-01T00:00:00\",\"validityEndAtInHardware\":\"2021-01-01T00:00:00\",\"external\":false,\"disengagePeriod\":\"SHORT\",\"mediumIdentifier\":1,\"outdated\":true,\"personId\":\"82a8a2cc-5d39-4cfa-b04e-49111a0bcdf7\",\"person\":\"test Person\",\"hardwareId\":\"f45ebc226706800e1cf942c995d0a43db78ec563173fb79a43eedbfbd8f29222\",\"nativeId\":\"046e0b8a967280\",\"secure\":true,\"softwareStatus\":\"ACTIVE\",\"hardwareStatus\":\"ACTIVE\",\"fitsOnHardware\":true,\"userId\":\"91781b22-ebdf-4ada-80cf-91f1fb9a4d96\",\"userName\":\"test User\",\"requiredAction\":\"UPDATE\",\"mediumType\":\"PASSIVE\",\"phoneNumber\":null,\"registrationState\":null,\"registrationCode\":null,\"registrationCodeValidUntil\":null,\"messageLanguage\":null,\"entityMetadata\":[{\"id\":\"123e4567-e89b-12d3-a456-426614174000\",\"name\":\"type\",\"value\":\"card\"},{\"id\":\"0f8fad5b-d9cb-469f-a165-70867728950e\",\"name\":\"amount\",\"value\":null}]},{\"id\":\"8293e920-90ce-48da-851c-cff54a13e2c6\",\"label\":\"test door\",\"issuedAt\":\"2023-07-05T15:22:13.509825\",\"syncedAt\":\"2023-07-05T15:22:38.230076\",\"validityDuration\":151,\"authorizationProfileId\":\"d08fdd62-bc36-4e47-8bc9-62b603e75ed9\",\"authorizationProfileName\":\"Authorization Profile 1\",\"individualAuthorizationProfileIds\":[\"3dba6935-6904-4bc0-99d3-8115c9bbbedc\",\"66b9f5d9-5664-4bb9-9546-af315987752b\"],\"mediumState\":\"ACTIVE\",\"accessBeginAt\":\"2021-01-01T00:00:00\",\"accessEndAt\":\"2021-01-01T00:00:00\",\"validityBeginAt\":\"2021-01-01T00:00:00\",\"validityEndAt\":\"2021-01-01T00:00:00\",\"validityBeginAtInHardware\":\"2021-01-01T00:00:00\",\"validityEndAtInHardware\":\"2021-01-01T00:00:00\",\"external\":false,\"disengagePeriod\":\"SHORT\",\"mediumIdentifier\":1,\"outdated\":true,\"personId\":\"82a8a2cc-5d39-4cfa-b04e-49111a0bcdf7\",\"person\":\"test Person\",\"hardwareId\":\"f45ebc226706800e1cf942c995d0a43db78ec563173fb79a43eedbfbd8f29222\",\"nativeId\":\"046e0b8a967280\",\"secure\":true,\"softwareStatus\":\"ACTIVE\",\"hardwareStatus\":\"ACTIVE\",\"fitsOnHardware\":true,\"userId\":\"91781b22-ebdf-4ada-80cf-91f1fb9a4d96\",\"userName\":\"test User\",\"requiredAction\":\"UPDATE\",\"mediumType\":\"PASSIVE\",\"phoneNumber\":null,\"registrationState\":null,\"registrationCode\":null,\"registrationCodeValidUntil\":null,\"messageLanguage\":null,\"entityMetadata\":[{\"id\":\"123e4567-e89b-12d3-a456-426614174000\",\"name\":\"type\",\"value\":\"card\"},{\"id\":\"0f8fad5b-d9cb-469f-a165-70867728950e\",\"name\":\"amount\",\"value\":null}]}],\"totalCount\":2,\"filterCount\":2}}" test("encoding QueryResponseElement for an identification medium") { val identificationMediumEncoded = encodeQueryList(identificationMediumList) diff --git a/xesar-connect/src/test/kotlin/com/open200/xesar/connect/util/fixture/IdentificationMediumFixture.kt b/xesar-connect/src/test/kotlin/com/open200/xesar/connect/util/fixture/IdentificationMediumFixture.kt index 6212ee68..609ca73c 100644 --- a/xesar-connect/src/test/kotlin/com/open200/xesar/connect/util/fixture/IdentificationMediumFixture.kt +++ b/xesar-connect/src/test/kotlin/com/open200/xesar/connect/util/fixture/IdentificationMediumFixture.kt @@ -1,6 +1,7 @@ package com.open200.xesar.connect.util.fixture import com.open200.xesar.connect.messages.DisengagePeriod +import com.open200.xesar.connect.messages.EntityMetadata import com.open200.xesar.connect.messages.query.IdentificationMedium import java.time.LocalDateTime import java.util.* @@ -44,6 +45,18 @@ object IdentificationMediumFixture { userName = "test User", requiredAction = "UPDATE", mediumType = IdentificationMedium.MediumType.PASSIVE, - entityMetadata = emptyList(), + entityMetadata = + listOf( + EntityMetadata( + id = UUID.fromString("123e4567-e89b-12d3-a456-426614174000"), + name = "type", + value = "card", + ), + EntityMetadata( + id = UUID.fromString("0f8fad5b-d9cb-469f-a165-70867728950e"), + name = "amount", + value = null, + ), + ), ) }