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
This commit is contained in:
2025-08-17 19:16:26 +02:00
committed by swordsteel
parent 6f44c05330
commit 14d36f21db
6 changed files with 213 additions and 64 deletions

View File

@@ -6,3 +6,12 @@ GET {{hostname}}/devices/page-1
### get all types ### get all types
GET {{hostname}}/devices/page-1/show-2 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

View File

@@ -1,6 +1,7 @@
package ltd.hlaeja.controller package ltd.hlaeja.controller
import jakarta.validation.constraints.Min import jakarta.validation.constraints.Min
import java.util.UUID
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import ltd.hlaeja.library.deviceRegistry.Devices import ltd.hlaeja.library.deviceRegistry.Devices
@@ -30,4 +31,16 @@ class DevicesController(
@PathVariable(required = false) @Min(1) show: Int = DEFAULT_SIZE, @PathVariable(required = false) @Min(1) show: Int = DEFAULT_SIZE,
): Flow<Devices.Response> = deviceService.getDevices((page - 1) * show, show) ): Flow<Devices.Response> = deviceService.getDevices((page - 1) * show, show)
.map { it.toDevicesResponse() } .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<Devices.Response> = deviceService.getDevicesByType(type, (page - 1) * show, show)
.map { it.toDevicesResponse() }
} }

View File

@@ -16,4 +16,11 @@ interface DeviceRepository : CoroutineCrudRepository<DeviceEntity, UUID> {
@Param("offset") offset: Int, @Param("offset") offset: Int,
@Param("limit") limit: Int, @Param("limit") limit: Int,
): Flow<DeviceEntity> ): Flow<DeviceEntity>
@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<DeviceEntity>
} }

View File

@@ -37,4 +37,10 @@ class DeviceService(
page: Int, page: Int,
show: Int, show: Int,
): Flow<DeviceEntity> = deviceRepository.findAll(page, show) ): Flow<DeviceEntity> = deviceRepository.findAll(page, show)
suspend fun getDevicesByType(
type: UUID,
page: Int,
show: Int,
): Flow<DeviceEntity> = deviceRepository.findAllByType(type, page, show)
} }

View File

@@ -1,9 +1,11 @@
package ltd.hlaeja.controller package ltd.hlaeja.controller
import java.util.UUID
import ltd.hlaeja.library.deviceRegistry.Devices import ltd.hlaeja.library.deviceRegistry.Devices
import ltd.hlaeja.test.container.PostgresTestContainer import ltd.hlaeja.test.container.PostgresTestContainer
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.CsvSource import org.junit.jupiter.params.provider.CsvSource
@@ -27,79 +29,173 @@ class DevicesEndpoint {
webClient = WebTestClient.bindToServer().baseUrl("http://localhost:$port").build() webClient = WebTestClient.bindToServer().baseUrl("http://localhost:$port").build()
} }
@Test @Nested
fun `get devices default - success`() { inner class GetDevices {
// when
val result = webClient.get().uri("/devices").exchange()
// then @Test
result.expectStatus().isOk() fun `get devices default - success`() {
.expectBody<List<Devices.Response>>() // when
.consumeWith { val result = webClient.get().uri("/devices").exchange()
assertThat(it.responseBody?.size).isEqualTo(4)
} // then
result.expectStatus().isOk()
.expectBody<List<Devices.Response>>()
.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<List<Devices.Response>>()
.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<List<Devices.Response>>()
.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 @Nested
@CsvSource( inner class GetDevicesByType {
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 @Test
result.expectStatus().isOk() fun `get devices for type default - success`() {
.expectBody<List<Devices.Response>>() // when
.consumeWith { val result = webClient.get()
assertThat(it.responseBody?.size).isEqualTo(expected) .uri("/devices/type-00000000-0000-0000-0001-000000000001")
} .exchange()
}
@Test // then
fun `get devices by pages - fail`() { result.expectStatus().isOk()
// when .expectBody<List<Devices.Response>>()
val result = webClient.get().uri("/devices/page-0").exchange() .consumeWith {
assertThat(it.responseBody?.size).isEqualTo(2)
}
}
// then @ParameterizedTest
result.expectStatus().isBadRequest @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 // then
@CsvSource( result.expectStatus().isOk()
value = [ .expectBody<List<Devices.Response>>()
"1,2,2", .consumeWith {
"2,2,2", assertThat(it.responseBody?.size).isEqualTo(expected)
"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 @Test
result.expectStatus().isOk() fun `get devices for type by pages - fail`() {
.expectBody<List<Devices.Response>>() // when
.consumeWith { val result = webClient.get()
assertThat(it.responseBody?.size).isEqualTo(expected) .uri("/devices/type-00000000-0000-0000-0001-000000000001/page-0")
} .exchange()
}
@ParameterizedTest // then
@CsvSource( result.expectStatus().isBadRequest
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 @ParameterizedTest
result.expectStatus().isBadRequest @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<List<Devices.Response>>()
.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
}
} }
} }

View File

@@ -51,4 +51,22 @@ class DevicesControllerTest {
assertThat(response.type).isEqualToUuid(NIL_UUID) assertThat(response.type).isEqualToUuid(NIL_UUID)
assertThat(response.timestamp).isEqualTo(timestamp) 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)
}
} }