add devices endpoint

- fix missing coroutine in
  - TypeRepository
  - TypesController
  - TypeService
  - TypesControllerTest
  - TypeServiceTest
- add DevicesEndpoint
- add DevicesControllerTest
- add devices.http
- add DevicesController
- add DeviceEntity.toDevicesResponse() to Mapping.kt
- add PostgresTestContainer to DeviceService
- update DeviceRepository with find all
- update version catalog
  - update container annotation in DeviceEndpoint
  - update container annotation in IdentityEndpoint
  - update container annotation in NodeEndpoint
  - update container annotation in TypeEndpoint
  - update container annotation in TypesEndpoint
  - update version in gradle.properties
This commit is contained in:
2025-08-16 16:13:55 +02:00
committed by swordsteel
parent fc95f5d4b8
commit 6f44c05330
19 changed files with 253 additions and 40 deletions

View File

@@ -0,0 +1,33 @@
package ltd.hlaeja.controller
import jakarta.validation.constraints.Min
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import ltd.hlaeja.library.deviceRegistry.Devices
import ltd.hlaeja.service.DeviceService
import ltd.hlaeja.util.toDevicesResponse
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RestController
@RestController
class DevicesController(
private val deviceService: DeviceService,
) {
companion object {
const val DEFAULT_PAGE: Int = 1
const val DEFAULT_SIZE: Int = 25
}
@GetMapping(
"/devices",
"/devices/page-{page}",
"/devices/page-{page}/show-{show}",
)
suspend fun getDevices(
@PathVariable(required = false) @Min(1) page: Int = DEFAULT_PAGE,
@PathVariable(required = false) @Min(1) show: Int = DEFAULT_SIZE,
): Flow<Devices.Response> = deviceService.getDevices((page - 1) * show, show)
.map { it.toDevicesResponse() }
}

View File

@@ -27,7 +27,7 @@ class TypesController(
"/types/filter-{filter}/page-{page}",
"/types/filter-{filter}/page-{page}/show-{show}",
)
fun getTypes(
suspend fun getTypes(
@PathVariable(required = false) @Min(1) page: Int = DEFAULT_PAGE,
@PathVariable(required = false) @Min(1) show: Int = DEFAULT_SIZE,
@PathVariable(required = false) filter: String? = null,

View File

@@ -1,9 +1,19 @@
package ltd.hlaeja.repository
import java.util.UUID
import kotlinx.coroutines.flow.Flow
import ltd.hlaeja.entity.DeviceEntity
import org.springframework.data.r2dbc.repository.Query
import org.springframework.data.repository.kotlin.CoroutineCrudRepository
import org.springframework.data.repository.query.Param
import org.springframework.stereotype.Repository
@Repository
interface DeviceRepository : CoroutineCrudRepository<DeviceEntity, UUID>
interface DeviceRepository : CoroutineCrudRepository<DeviceEntity, UUID> {
@Query("SELECT * FROM devices LIMIT :limit OFFSET :offset")
fun findAll(
@Param("offset") offset: Int,
@Param("limit") limit: Int,
): Flow<DeviceEntity>
}

View File

@@ -13,13 +13,13 @@ import org.springframework.stereotype.Repository
interface TypeRepository : CoroutineCrudRepository<TypeEntity, UUID> {
@Query("SELECT * FROM types ORDER BY name LIMIT :limit OFFSET :offset")
fun findAll(
suspend fun findAll(
@Param("offset") offset: Int,
@Param("limit") limit: Int,
): Flow<TypeEntity>
@Query("SELECT * FROM types WHERE name ILIKE :filter ORDER BY name LIMIT :limit OFFSET :offset")
fun findAllContaining(
suspend fun findAllContaining(
@Param("filter") filter: String,
@Param("offset") offset: Int,
@Param("limit") limit: Int,

View File

@@ -3,6 +3,7 @@ package ltd.hlaeja.service
import io.github.oshai.kotlinlogging.KotlinLogging
import java.time.ZonedDateTime
import java.util.UUID
import kotlinx.coroutines.flow.Flow
import ltd.hlaeja.entity.DeviceEntity
import ltd.hlaeja.repository.DeviceRepository
import org.springframework.dao.DataIntegrityViolationException
@@ -31,4 +32,9 @@ class DeviceService(
suspend fun getDevice(device: UUID): DeviceEntity = deviceRepository.findById(device)
?.also { log.debug { "Get device ${it.id}" } }
?: throw ResponseStatusException(NOT_FOUND)
suspend fun getDevices(
page: Int,
show: Int,
): Flow<DeviceEntity> = deviceRepository.findAll(page, show)
}

View File

@@ -26,7 +26,7 @@ class TypeService(
private val typeDescriptionRepository: TypeDescriptionRepository,
) {
fun getTypes(
suspend fun getTypes(
page: Int,
show: Int,
filter: String?,

View File

@@ -2,13 +2,14 @@ package ltd.hlaeja.util
import java.time.ZonedDateTime
import java.util.UUID
import ltd.hlaeja.dto.TypeWithDescription
import ltd.hlaeja.entity.DeviceEntity
import ltd.hlaeja.entity.NodeEntity
import ltd.hlaeja.entity.TypeEntity
import ltd.hlaeja.dto.TypeWithDescription
import ltd.hlaeja.entity.TypeDescriptionEntity
import ltd.hlaeja.entity.TypeEntity
import ltd.hlaeja.jwt.service.PrivateJwtService
import ltd.hlaeja.library.deviceRegistry.Device
import ltd.hlaeja.library.deviceRegistry.Devices
import ltd.hlaeja.library.deviceRegistry.Identity
import ltd.hlaeja.library.deviceRegistry.Node
import ltd.hlaeja.library.deviceRegistry.Type
@@ -68,3 +69,9 @@ fun DeviceEntity.toDeviceResponse(
type,
jwtService.sign("device" to id),
)
fun DeviceEntity.toDevicesResponse(): Devices.Response = Devices.Response(
id ?: throw ResponseStatusException(EXPECTATION_FAILED),
type,
timestamp,
)