diff --git a/README.md b/README.md index f272003..96dead0 100644 --- a/README.md +++ b/README.md @@ -5,14 +5,14 @@ Classes crafted, identities bestowed, Each device recorded, their functions unfo ## Properties for deployment | name | required | info | -|------------------------|----------|-------------------------| -| spring.profiles.active | * | Spring Boot environment | -| spring.r2dbc.url | * | Postgres host url | -| spring.r2dbc.username | * | Postgres username | -| spring.r2dbc.password | ** | Postgres password | -| jwt.private-key | | JWT private cert | +|------------------------|:--------:|-------------------------| +| spring.profiles.active | ✓ | Spring Boot environment | +| spring.r2dbc.url | ✓ | Postgres host url | +| spring.r2dbc.username | ✓ | Postgres username | +| spring.r2dbc.password | ✗ | Postgres password | +| jwt.private-key | ✓ | JWT private cert | -Required: * can be stored as text, and ** need to be stored as secret. +*Required: ✓ can be stored as text, and ✗ need to be stored as secret.* ## Releasing Service @@ -20,34 +20,28 @@ Run `release.sh` script from `master` branch. ## Development Information -### Generate Private and Public RSA Key +### Private RSA Key -OpenSSL Project is dedicated to providing a simple installation of OpenSSL for Microsoft Windows. [Download](https://slproweb.com/products/Win32OpenSSL.html) +This service uses RAS keys to create identities for devices. The private key is used here to generate identities, while the public key is used by **[Hlæja Device API](https://github.com/swordsteel/hlaeja-device-api)** to identify a device and accept data. -Generate an RSA private key, of size 2048, and output it to a file named `private_key.pem` in to `./keys` +*For instructions on how to set these up, please refer to our [generate RSA key](https://github.com/swordsteel/hlaeja-development/blob/master/doc/rsa_key.md) documentation.* -```shell -openssl genrsa -out private_key.pem 2048 +### Global Setting + +The following global settings are used in Hlaeja Device Registry. You can configure these settings using either Gradle properties or alternatively environment variables. + +*For instructions on how to set these up, please refer to our [set global settings](https://github.com/swordsteel/hlaeja-development/blob/master/doc/global_settings.md) documentation.* + +#### Gradle Properties + +```properties +repository.user=your_user +repository.token=your_token_value ``` -Extract the public key from `private_key.pem` from `./keys`, and output it to a file named `public_key.pem` in to `./keys` +#### Environment Variables -```shell -openssl rsa -in private_key.pem -pubout -out public_key.pem +```properties +REPOSITORY_USER=your_user +REPOSITORY_TOKEN=your_token_value ``` - -### Global gradle properties - -To authenticate with Gradle to access repositories that require authentication, you can set your user and token in the `gradle.properties` file. - -Here's how you can do it: - -1. Open or create the `gradle.properties` file in your Gradle user home directory: - - On Unix-like systems (Linux, macOS), this directory is typically `~/.gradle/`. - - On Windows, this directory is typically `C:\Users\\.gradle\`. -2. Add the following lines to the `gradle.properties` file: - ```properties - repository.user=your_user - repository.token=your_token_value - ``` - or use environment variables `REPOSITORY_USER` and `REPOSITORY_TOKEN` diff --git a/gradle.properties b/gradle.properties index b378c0e..4242855 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ kotlin.code.style=official version=0.3.0-SNAPSHOT -catalog=0.6.0 +catalog=0.7.0-SNAPSHOT container.port.host=9010 diff --git a/http/device.http b/http/device.http index 156ce3b..fbfd232 100644 --- a/http/device.http +++ b/http/device.http @@ -5,3 +5,6 @@ Content-Type: application/json { "type": "00000000-0000-0000-0000-000000000000" } + +### register device for a type +GET {{hostname}}/device-00000000-0000-0000-0000-000000000000 diff --git a/src/main/kotlin/ltd/hlaeja/controller/DeviceController.kt b/src/main/kotlin/ltd/hlaeja/controller/DeviceController.kt index 962093c..db049d8 100644 --- a/src/main/kotlin/ltd/hlaeja/controller/DeviceController.kt +++ b/src/main/kotlin/ltd/hlaeja/controller/DeviceController.kt @@ -1,9 +1,12 @@ package ltd.hlaeja.controller +import java.util.UUID import ltd.hlaeja.library.deviceRegistry.Device import ltd.hlaeja.service.DeviceService import ltd.hlaeja.service.JwtService import ltd.hlaeja.util.toDeviceResponse +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RestController @@ -19,4 +22,10 @@ class DeviceController( @RequestBody request: Device.Request, ): Device.Response = deviceService.addDevice(request.type) .toDeviceResponse(jwtService) + + @GetMapping("/device-{device}") + suspend fun getDevice( + @PathVariable device: UUID, + ): Device.Response = deviceService.getDevice(device) + .toDeviceResponse(jwtService) } diff --git a/src/main/kotlin/ltd/hlaeja/service/DeviceService.kt b/src/main/kotlin/ltd/hlaeja/service/DeviceService.kt index 88ff489..ab3a868 100644 --- a/src/main/kotlin/ltd/hlaeja/service/DeviceService.kt +++ b/src/main/kotlin/ltd/hlaeja/service/DeviceService.kt @@ -1,11 +1,13 @@ package ltd.hlaeja.service +import io.github.oshai.kotlinlogging.KotlinLogging import java.time.ZonedDateTime import java.util.UUID import ltd.hlaeja.entity.DeviceEntity import ltd.hlaeja.repository.DeviceRepository -import mu.KotlinLogging +import org.springframework.http.HttpStatus.NOT_FOUND import org.springframework.stereotype.Service +import org.springframework.web.server.ResponseStatusException private val log = KotlinLogging.logger {} @@ -18,4 +20,8 @@ class DeviceService( type: UUID, ): DeviceEntity = deviceRepository.save(DeviceEntity(null, ZonedDateTime.now(), type)) .also { log.debug { "Added device ${it.id}" } } + + suspend fun getDevice(device: UUID): DeviceEntity = deviceRepository.findById(device) + ?.also { log.debug { "Get device ${it.id}" } } + ?: throw ResponseStatusException(NOT_FOUND) } diff --git a/src/main/kotlin/ltd/hlaeja/service/NodeService.kt b/src/main/kotlin/ltd/hlaeja/service/NodeService.kt index c7f2bfe..a8f123f 100644 --- a/src/main/kotlin/ltd/hlaeja/service/NodeService.kt +++ b/src/main/kotlin/ltd/hlaeja/service/NodeService.kt @@ -1,9 +1,9 @@ package ltd.hlaeja.service +import io.github.oshai.kotlinlogging.KotlinLogging import java.util.UUID import ltd.hlaeja.entity.NodeEntity import ltd.hlaeja.repository.NodeRepository -import mu.KotlinLogging import org.springframework.http.HttpStatus.NOT_FOUND import org.springframework.stereotype.Service import org.springframework.web.server.ResponseStatusException diff --git a/src/main/kotlin/ltd/hlaeja/service/TypeService.kt b/src/main/kotlin/ltd/hlaeja/service/TypeService.kt index b2f243a..2dc1681 100644 --- a/src/main/kotlin/ltd/hlaeja/service/TypeService.kt +++ b/src/main/kotlin/ltd/hlaeja/service/TypeService.kt @@ -1,9 +1,9 @@ package ltd.hlaeja.service +import io.github.oshai.kotlinlogging.KotlinLogging import kotlinx.coroutines.flow.Flow import ltd.hlaeja.entity.TypeEntity import ltd.hlaeja.repository.TypeRepository -import mu.KotlinLogging import org.springframework.dao.DuplicateKeyException import org.springframework.http.HttpStatus import org.springframework.stereotype.Service @@ -22,9 +22,9 @@ class TypeService( entity: TypeEntity, ): TypeEntity = try { typeRepository.save(entity) - .also { log.debug("Added new type: {}", it.id) } + .also { log.debug { "Added new type: $it.id" } } } catch (e: DuplicateKeyException) { - log.warn(e.localizedMessage) + log.warn { e.localizedMessage } throw ResponseStatusException(HttpStatus.CONFLICT) } } diff --git a/src/test/kotlin/ltd/hlaeja/controller/DeviceControllerTest.kt b/src/test/kotlin/ltd/hlaeja/controller/DeviceControllerTest.kt index c4ce89f..798d4ae 100644 --- a/src/test/kotlin/ltd/hlaeja/controller/DeviceControllerTest.kt +++ b/src/test/kotlin/ltd/hlaeja/controller/DeviceControllerTest.kt @@ -14,6 +14,7 @@ import ltd.hlaeja.service.DeviceService import ltd.hlaeja.service.JwtService 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.api.assertThrows import org.springframework.web.server.ResponseStatusException @@ -36,35 +37,59 @@ class DeviceControllerTest { controller = DeviceController(deviceService, jwtService) } - @Test - fun `add device - success`() = runTest { - // given - val request = Device.Request(uuid) - coEvery { deviceService.addDevice(any()) } returns DeviceEntity(uuid, timestamp, uuid) - coEvery { jwtService.makeIdentity(any()) } returns PAYLOAD + @Nested + inner class AddDeviceTest { - // when - val response = controller.addDevice(request) + @Test + fun `add device - success`() = runTest { + // given + val request = Device.Request(uuid) + coEvery { deviceService.addDevice(any()) } returns DeviceEntity(uuid, timestamp, uuid) + coEvery { jwtService.makeIdentity(any()) } returns PAYLOAD - // then - coVerify(exactly = 1) { deviceService.addDevice(any()) } - coVerify(exactly = 1) { jwtService.makeIdentity(any()) } + // when + val response = controller.addDevice(request) - assertThat(response.identity).isEqualTo(PAYLOAD) - } + // then + coVerify(exactly = 1) { deviceService.addDevice(any()) } + coVerify(exactly = 1) { jwtService.makeIdentity(any()) } - @Test - fun `add device - device service fail`() = runTest { - // given - val request = Device.Request(uuid) - coEvery { deviceService.addDevice(any()) } returns DeviceEntity(null, timestamp, uuid) - - // when exception - val exception = assertThrows { - controller.addDevice(request) + assertThat(response.identity).isEqualTo(PAYLOAD) } - // then - assertThat(exception.message).isEqualTo("417 EXPECTATION_FAILED") + @Test + fun `add device - device service fail`() = runTest { + // given + val request = Device.Request(uuid) + coEvery { deviceService.addDevice(any()) } returns DeviceEntity(null, timestamp, uuid) + + // when exception + val exception = assertThrows { + controller.addDevice(request) + } + + // then + assertThat(exception.message).isEqualTo("417 EXPECTATION_FAILED") + } + } + + @Nested + inner class GetDeviceTest { + + @Test + fun `get device - success`() = runTest { + // given + coEvery { deviceService.getDevice(any()) } returns DeviceEntity(uuid, timestamp, uuid) + coEvery { jwtService.makeIdentity(any()) } returns PAYLOAD + + // when + val response = controller.getDevice(uuid) + + // then + coVerify(exactly = 1) { deviceService.getDevice(any()) } + coVerify(exactly = 1) { jwtService.makeIdentity(any()) } + + assertThat(response.identity).isEqualTo(PAYLOAD) + } } } diff --git a/src/test/kotlin/ltd/hlaeja/service/DeviceServiceTest.kt b/src/test/kotlin/ltd/hlaeja/service/DeviceServiceTest.kt index 3c81dbe..327d67c 100644 --- a/src/test/kotlin/ltd/hlaeja/service/DeviceServiceTest.kt +++ b/src/test/kotlin/ltd/hlaeja/service/DeviceServiceTest.kt @@ -17,6 +17,8 @@ import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.springframework.web.server.ResponseStatusException class DeviceServiceTest { companion object { @@ -42,7 +44,7 @@ class DeviceServiceTest { } @Test - fun `add new type success`() = runTest { + fun `add new device success`() = runTest { // given coEvery { repository.save(any()) } answers { call -> (call.invocation.args[0] as DeviceEntity).copy(id = device) @@ -58,4 +60,37 @@ class DeviceServiceTest { assertThat(result.timestamp.toString()).isEqualTo("2000-01-01T00:00:00.001Z[UTC]") assertThat(result.type).isEqualTo(type) } + + @Test + fun `get device - success`() = runTest { + // given + val device = UUID.fromString("00000000-0000-0000-0000-000000000000") + val entity: DeviceEntity = mockk() + + coEvery { repository.findById(any()) } returns entity + coEvery { entity.id } returns device + + // when + val result = service.getDevice(type) + + // then + coVerify(exactly = 1) { repository.findById(any()) } + assertThat(result.id).isEqualTo(device) + } + + @Test + fun `get device - fail not found`() = runTest { + // given + val device = UUID.fromString("00000000-0000-0000-0000-000000000000") + + coEvery { repository.findById(any()) } returns null + + // when + val exception = assertThrows { + service.getDevice(device) + } + + // then + assertThat(exception.message).isEqualTo("404 NOT_FOUND") + } }