From 14d36f21db21acbf0ab88d4167ec4cd60dac5236 Mon Sep 17 00:00:00 2001 From: Swordsteel Date: Sun, 17 Aug 2025 19:16:26 +0200 Subject: [PATCH] add devices by type endpoint - update DevicesController - update DevicesEndpoint - update DevicesControllerTest - add getDevicesByType to DevicesController - update devices.http - add getDevicesByType to DeviceService - add findAllByType to DeviceRepository --- http/devices.http | 9 + .../hlaeja/controller/DevicesController.kt | 13 + .../ltd/hlaeja/repository/DeviceRepository.kt | 7 + .../ltd/hlaeja/service/DeviceService.kt | 6 + .../ltd/hlaeja/controller/DevicesEndpoint.kt | 224 +++++++++++++----- .../controller/DevicesControllerTest.kt | 18 ++ 6 files changed, 213 insertions(+), 64 deletions(-) diff --git a/http/devices.http b/http/devices.http index ee9a648..773b9de 100644 --- a/http/devices.http +++ b/http/devices.http @@ -6,3 +6,12 @@ GET {{hostname}}/devices/page-1 ### get all types GET {{hostname}}/devices/page-1/show-2 + +### get all types +GET {{hostname}}/devices/type-00000000-0000-0000-0000-000000000000 + +### get all types +GET {{hostname}}/devices/type-00000000-0000-0000-0000-000000000000/page-1 + +### get all types +GET {{hostname}}/devices/type-00000000-0000-0000-0000-000000000000/page-1/show-2 diff --git a/src/main/kotlin/ltd/hlaeja/controller/DevicesController.kt b/src/main/kotlin/ltd/hlaeja/controller/DevicesController.kt index ed50e36..e460655 100644 --- a/src/main/kotlin/ltd/hlaeja/controller/DevicesController.kt +++ b/src/main/kotlin/ltd/hlaeja/controller/DevicesController.kt @@ -1,6 +1,7 @@ package ltd.hlaeja.controller import jakarta.validation.constraints.Min +import java.util.UUID import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import ltd.hlaeja.library.deviceRegistry.Devices @@ -30,4 +31,16 @@ class DevicesController( @PathVariable(required = false) @Min(1) show: Int = DEFAULT_SIZE, ): Flow = deviceService.getDevices((page - 1) * show, show) .map { it.toDevicesResponse() } + + @GetMapping( + "/devices/type-{type}", + "/devices/type-{type}/page-{page}", + "/devices/type-{type}/page-{page}/show-{show}", + ) + suspend fun getDevicesByType( + @PathVariable type: UUID, + @PathVariable(required = false) @Min(1) page: Int = DEFAULT_PAGE, + @PathVariable(required = false) @Min(1) show: Int = DEFAULT_SIZE, + ): Flow = deviceService.getDevicesByType(type, (page - 1) * show, show) + .map { it.toDevicesResponse() } } diff --git a/src/main/kotlin/ltd/hlaeja/repository/DeviceRepository.kt b/src/main/kotlin/ltd/hlaeja/repository/DeviceRepository.kt index ed7eef3..4e6422c 100644 --- a/src/main/kotlin/ltd/hlaeja/repository/DeviceRepository.kt +++ b/src/main/kotlin/ltd/hlaeja/repository/DeviceRepository.kt @@ -16,4 +16,11 @@ interface DeviceRepository : CoroutineCrudRepository { @Param("offset") offset: Int, @Param("limit") limit: Int, ): Flow + + @Query("SELECT * FROM devices WHERE type = :type LIMIT :limit OFFSET :offset") + fun findAllByType( + @Param("type") type: UUID, + @Param("offset") offset: Int, + @Param("limit") limit: Int, + ): Flow } diff --git a/src/main/kotlin/ltd/hlaeja/service/DeviceService.kt b/src/main/kotlin/ltd/hlaeja/service/DeviceService.kt index f1e21c5..8603337 100644 --- a/src/main/kotlin/ltd/hlaeja/service/DeviceService.kt +++ b/src/main/kotlin/ltd/hlaeja/service/DeviceService.kt @@ -37,4 +37,10 @@ class DeviceService( page: Int, show: Int, ): Flow = deviceRepository.findAll(page, show) + + suspend fun getDevicesByType( + type: UUID, + page: Int, + show: Int, + ): Flow = deviceRepository.findAllByType(type, page, show) } diff --git a/src/test-integration/kotlin/ltd/hlaeja/controller/DevicesEndpoint.kt b/src/test-integration/kotlin/ltd/hlaeja/controller/DevicesEndpoint.kt index 8c156e1..79d3f40 100644 --- a/src/test-integration/kotlin/ltd/hlaeja/controller/DevicesEndpoint.kt +++ b/src/test-integration/kotlin/ltd/hlaeja/controller/DevicesEndpoint.kt @@ -1,9 +1,11 @@ package ltd.hlaeja.controller +import java.util.UUID import ltd.hlaeja.library.deviceRegistry.Devices import ltd.hlaeja.test.container.PostgresTestContainer import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.CsvSource @@ -27,79 +29,173 @@ class DevicesEndpoint { webClient = WebTestClient.bindToServer().baseUrl("http://localhost:$port").build() } - @Test - fun `get devices default - success`() { - // when - val result = webClient.get().uri("/devices").exchange() + @Nested + inner class GetDevices { - // then - result.expectStatus().isOk() - .expectBody>() - .consumeWith { - assertThat(it.responseBody?.size).isEqualTo(4) - } + @Test + fun `get devices default - success`() { + // when + val result = webClient.get().uri("/devices").exchange() + + // then + result.expectStatus().isOk() + .expectBody>() + .consumeWith { + assertThat(it.responseBody?.size).isEqualTo(4) + } + } + + @ParameterizedTest + @CsvSource( + value = [ + "1,4", + "2,0", + ], + ) + fun `get devices by page - success`(page: Int, expected: Int) { + // when + val result = webClient.get().uri("/devices/page-$page").exchange() + + // then + result.expectStatus().isOk() + .expectBody>() + .consumeWith { + assertThat(it.responseBody?.size).isEqualTo(expected) + } + } + + @Test + fun `get devices by pages - fail`() { + // when + val result = webClient.get().uri("/devices/page-0").exchange() + + // then + result.expectStatus().isBadRequest + } + + @ParameterizedTest + @CsvSource( + value = [ + "1,2,2", + "2,2,2", + "3,2,0", + ], + ) + fun `get devices by page and show - success`(page: Int, show: Int, expected: Int) { + // when + val result = webClient.get().uri("/devices/page-$page/show-$show").exchange() + + // then + result.expectStatus().isOk() + .expectBody>() + .consumeWith { + assertThat(it.responseBody?.size).isEqualTo(expected) + } + } + + @ParameterizedTest + @CsvSource( + value = [ + "0,1", + "1,0", + ], + ) + fun `get devices by page and show - fail`(page: Int, show: Int) { + // when + val result = webClient.get().uri("/devices/page-$page/show-$show").exchange() + + // then + result.expectStatus().isBadRequest + } } - @ParameterizedTest - @CsvSource( - value = [ - "1,4", - "2,0", - ], - ) - fun `get devices by page - success`(page: Int, expected: Int) { - // when - val result = webClient.get().uri("/devices/page-$page").exchange() + @Nested + inner class GetDevicesByType { - // then - result.expectStatus().isOk() - .expectBody>() - .consumeWith { - assertThat(it.responseBody?.size).isEqualTo(expected) - } - } + @Test + fun `get devices for type default - success`() { + // when + val result = webClient.get() + .uri("/devices/type-00000000-0000-0000-0001-000000000001") + .exchange() - @Test - fun `get devices by pages - fail`() { - // when - val result = webClient.get().uri("/devices/page-0").exchange() + // then + result.expectStatus().isOk() + .expectBody>() + .consumeWith { + assertThat(it.responseBody?.size).isEqualTo(2) + } + } - // then - result.expectStatus().isBadRequest - } + @ParameterizedTest + @CsvSource( + value = [ + "00000000-0000-0000-0001-000000000001,1,2", + "00000000-0000-0000-0001-000000000001,2,0", + ], + ) + fun `get devices for type by page - success`(type: UUID, page: Int, expected: Int) { + // when + val result = webClient.get() + .uri("/devices/type-$type/page-$page") + .exchange() - @ParameterizedTest - @CsvSource( - value = [ - "1,2,2", - "2,2,2", - "3,2,0", - ], - ) - fun `get devices by page and show - success`(page: Int, show: Int, expected: Int) { - // when - val result = webClient.get().uri("/devices/page-$page/show-$show").exchange() + // then + result.expectStatus().isOk() + .expectBody>() + .consumeWith { + assertThat(it.responseBody?.size).isEqualTo(expected) + } + } - // then - result.expectStatus().isOk() - .expectBody>() - .consumeWith { - assertThat(it.responseBody?.size).isEqualTo(expected) - } - } + @Test + fun `get devices for type by pages - fail`() { + // when + val result = webClient.get() + .uri("/devices/type-00000000-0000-0000-0001-000000000001/page-0") + .exchange() - @ParameterizedTest - @CsvSource( - value = [ - "0,1", - "1,0", - ], - ) - fun `get devices by page and show - fail`(page: Int, show: Int) { - // when - val result = webClient.get().uri("/devices/page-$page/show-$show").exchange() + // then + result.expectStatus().isBadRequest + } - // then - result.expectStatus().isBadRequest + @ParameterizedTest + @CsvSource( + value = [ + "00000000-0000-0000-0001-000000000001,1,1,1", + "00000000-0000-0000-0001-000000000001,2,1,1", + "00000000-0000-0000-0001-000000000001,3,1,0", + ], + ) + fun `get devices for type by page and show - success`(type: UUID, page: Int, show: Int, expected: Int) { + // when + val result = webClient.get() + .uri("/devices/type-$type/page-$page/show-$show") + .exchange() + + // then + result.expectStatus().isOk() + .expectBody>() + .consumeWith { + assertThat(it.responseBody?.size).isEqualTo(expected) + } + } + + @ParameterizedTest + @CsvSource( + value = [ + "0,1", + "1,0", + ], + ) + fun `get devices for type by page and show - fail`(page: Int, show: Int) { + // when + val result = webClient.get() + .uri("/devices/type-00000000-0000-0000-0001-000000000001/page-$page/show-$show") + .exchange() + + // then + result.expectStatus().isBadRequest + } } } diff --git a/src/test/kotlin/ltd/hlaeja/controller/DevicesControllerTest.kt b/src/test/kotlin/ltd/hlaeja/controller/DevicesControllerTest.kt index c66a00d..c84097c 100644 --- a/src/test/kotlin/ltd/hlaeja/controller/DevicesControllerTest.kt +++ b/src/test/kotlin/ltd/hlaeja/controller/DevicesControllerTest.kt @@ -51,4 +51,22 @@ class DevicesControllerTest { assertThat(response.type).isEqualToUuid(NIL_UUID) assertThat(response.timestamp).isEqualTo(timestamp) } + + @Test + fun `get all devices for type`() = runTest { + // given + coEvery { + service.getDevicesByType(any(), any(), any()) + } returns flowOf(DeviceEntity(id, timestamp, type)) + + // when + val response = controller.getDevicesByType(type).single() + + // then + coVerify(exactly = 1) { service.getDevicesByType(type, 0, 25) } + + assertThat(response.id).isEqualToUuid(NIL_UUID) + assertThat(response.type).isEqualToUuid(NIL_UUID) + assertThat(response.timestamp).isEqualTo(timestamp) + } }