Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,8 @@ 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"
/** MQTT topic string for the "ChangeZoneMetadataValueMapi" command. */
val CHANGE_ZONE_METADATA_VALUE = "xs3/1/cmd/ChangeZoneMetadataValueMapi"
/** MQTT topic string for the "ChangeInstallationPointMetadataValueMapi" command. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<MediumChanged> {
return sendCommandAsync<ChangeMediumMetadataValueMapi, MediumChanged>(
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
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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(
Expand All @@ -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<EntityMetadata>? = null,
) : Event
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -96,6 +98,7 @@ data class IdentificationMedium(
@Serializable(with = LocalDateTimeSerializer::class)
val registrationCodeValidUntil: LocalDateTime? = null,
val messageLanguage: String? = null,
val entityMetadata: List<EntityMetadata>? = null,
) : QueryListResource, QueryElementResource {

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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\":[{\"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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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\":\"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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Unit>()
val commandReceived = CompletableDeferred<String>()

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")
}
}
}
})
Loading
Loading