From 6297fbe542b9ade72b7a7cdbd5dbb66b82501b64 Mon Sep 17 00:00:00 2001 From: Swordsteel Date: Tue, 12 Nov 2024 01:39:26 +0100 Subject: [PATCH] add MeasurementService --- .../ltd/hlaeja/service/MeasurementService.kt | 52 +++++++++++ .../hlaeja/service/MeasurementServiceTest.kt | 92 +++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 src/main/kotlin/ltd/hlaeja/service/MeasurementService.kt create mode 100644 src/test/kotlin/ltd/hlaeja/service/MeasurementServiceTest.kt diff --git a/src/main/kotlin/ltd/hlaeja/service/MeasurementService.kt b/src/main/kotlin/ltd/hlaeja/service/MeasurementService.kt new file mode 100644 index 0000000..f339bab --- /dev/null +++ b/src/main/kotlin/ltd/hlaeja/service/MeasurementService.kt @@ -0,0 +1,52 @@ +package ltd.hlaeja.service + +import com.influxdb.client.write.Point +import java.util.UUID +import ltd.hlaeja.library.deviceData.MeasurementData +import ltd.hlaeja.repository.MeasurementRepository +import org.springframework.http.HttpStatus.NOT_FOUND +import org.springframework.stereotype.Service +import org.springframework.web.server.ResponseStatusException + +@Service +class MeasurementService( + private val repository: MeasurementRepository, +) { + + suspend fun getNodeMeasurement( + client: UUID, + node: UUID, + ): MeasurementData.Response { + val result = repository.getByNode(client, node) + if (result.isEmpty()) { + throw ResponseStatusException(NOT_FOUND, "No data for client: $client, device: $node") + } + val latestData = mutableMapOf() + result.forEach { table -> + table.records.forEach { record -> + latestData[record.getValueByKey("_field") as String] = record.value as Number + } + } + return MeasurementData.Response(latestData) + } + + suspend fun addMeasurement( + client: UUID, + request: MeasurementData.Request, + ) = Point.measurement(client.toString()) + .also { point -> + addTags(request.tags, point) + addFields(request.fields, point) + } + .let { point -> repository.save(point) } + + private suspend fun addFields( + measurements: Map, + point: Point, + ) = measurements.forEach { (key, value) -> point.addField(key, value) } + + private suspend fun addTags( + measurements: Map, + point: Point, + ) = measurements.forEach { (key, value) -> point.addTag(key, value) } +} diff --git a/src/test/kotlin/ltd/hlaeja/service/MeasurementServiceTest.kt b/src/test/kotlin/ltd/hlaeja/service/MeasurementServiceTest.kt new file mode 100644 index 0000000..ea6df3e --- /dev/null +++ b/src/test/kotlin/ltd/hlaeja/service/MeasurementServiceTest.kt @@ -0,0 +1,92 @@ +package ltd.hlaeja.service + +import com.influxdb.client.write.Point +import com.influxdb.query.FluxRecord +import com.influxdb.query.FluxTable +import io.mockk.Runs +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.just +import io.mockk.mockk +import io.mockk.slot +import java.util.UUID +import kotlin.test.Test +import kotlin.test.assertFailsWith +import kotlinx.coroutines.test.runTest +import ltd.hlaeja.library.deviceData.MeasurementData +import ltd.hlaeja.repository.MeasurementRepository +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import org.springframework.http.HttpStatus.NOT_FOUND +import org.springframework.web.server.ResponseStatusException + +class MeasurementServiceTest { + + private val repository: MeasurementRepository = mockk() + val uuid: UUID = UUID.fromString("00000000-0000-0000-0000-000000000000") + + private lateinit var service: MeasurementService + + @BeforeEach + fun setUp() { + service = MeasurementService(repository) + } + + @Test + fun `get measurement from client and device - success`() = runTest { + // given + val fluxTable = mockk() + val fluxRecord = mockk() + + coEvery { repository.getByNode(any(), any()) } returns mutableListOf(fluxTable) + coEvery { fluxTable.records } returns mutableListOf(fluxRecord) + coEvery { fluxRecord.getValueByKey(any()) } returns "field" + coEvery { fluxRecord.value } returns 1 + + // when + val result = service.getNodeMeasurement(uuid, uuid) + + // then + assertEquals(1, result.fields.size) + assertEquals("field", result.fields.keys.first()) + assertEquals(1, result.fields.values.first()) + } + + @Test + fun `get measurement from client and device - fail no result`() = runTest { + // given + coEvery { repository.getByNode(any(), any()) } returns mutableListOf() + + // when exception + val exception = assertFailsWith( + block = { service.getNodeMeasurement(uuid, uuid) }, + ) + + // then + assertEquals(NOT_FOUND, exception.statusCode) + assertEquals( + "No data for client: 00000000-0000-0000-0000-000000000000, device: 00000000-0000-0000-0000-000000000000", + exception.reason, + ) + } + + @Test + fun `add measurement`() = runTest { + // given + val uuid = UUID.fromString("00000000-0000-0000-0000-000000000000") + val request = MeasurementData.Request(mapOf("tag" to "value"), mapOf("field" to 10)) + + val capturedPointSlot = slot() + coEvery { repository.save(capture(capturedPointSlot)) } just Runs + + // when + service.addMeasurement(uuid, request) + + // then + coVerify(exactly = 1) { repository.save(any()) } + assertEquals( + "00000000-0000-0000-0000-000000000000,tag=value field=10i", + capturedPointSlot.captured.toLineProtocol(), + ) + } +}