From 1525702f07c37192319433ecae2e5d5fae54acb9 Mon Sep 17 00:00:00 2001 From: Swordsteel Date: Tue, 4 Mar 2025 00:01:38 +0100 Subject: [PATCH] add device type add postEditType to TypeController update form.html and messages.html for saving types add updateType in DeviceRegistryService add WebClient deviceRegistryTypesUpdate in DeviceRegisterWebClientCalls.kt add getEditType to TypeController update type form.html for edit device type add Type Response toTypeForm in Mapping.kt add getType to DeviceRegistryService add WebClient deviceRegistryType in DeviceRegisterWebClientCalls.kt update edit link in device type list.html add getCreateType and postCreateType to TypeController add device type form.html add TypeFormadd TypeForm toTypeRequest in Mapping.kt add TypeForm add createType to DeviceRegistryService add deviceRegistryTypesCreate in DeviceRegisterWebClientCalls.kt add TypeNameDuplicateException add DeviceRegistryException extract validationErrors from postCreateAccount in AccountController to util Controller update AccountController getAccounts and TypeController getTypes with max show value add redis for spring boot session - make RemoteUserDetail Serializable - add application properties - add dependencies add TypeController add list.html for type extract fragment pagination.html from users.html add DeviceRegistryService with getTypes add DeviceRegisterWebClientCalls.kt with deviceRegistryTypes set up hlaeja device registry add type to layout menu add type to admin SecurityConfiguration --- README.md | 1 + build.gradle.kts | 2 + gradle.properties | 2 +- src/main/kotlin/ltd/hlaeja/Application.kt | 2 + .../configuration/SecurityConfiguration.kt | 1 + .../hlaeja/controller/AccountController.kt | 84 ++++------- .../ltd/hlaeja/controller/TypeController.kt | 142 ++++++++++++++++++ .../exception/DeviceRegistryException.kt | 23 +++ .../exception/TypeNameDuplicateException.kt | 23 +++ src/main/kotlin/ltd/hlaeja/form/TypeForm.kt | 10 ++ .../hlaeja/property/DeviceRegistryProperty.kt | 8 + .../ltd/hlaeja/security/RemoteUserDetail.kt | 8 +- .../hlaeja/service/DeviceRegistryService.kt | 63 ++++++++ src/main/kotlin/ltd/hlaeja/util/Controller.kt | 11 ++ .../util/DeviceRegisterWebClientCalls.kt | 61 ++++++++ src/main/kotlin/ltd/hlaeja/util/Mapping.kt | 12 ++ src/main/resources/application.yml | 22 +++ .../resources/templates/account/users.html | 22 +-- .../resources/templates/device/type/form.html | 44 ++++++ .../resources/templates/device/type/list.html | 55 +++++++ src/main/resources/templates/layout.html | 1 + src/main/resources/templates/messages.html | 2 +- src/main/resources/templates/pagination.html | 19 +++ src/test/resources/application.yml | 2 + 24 files changed, 544 insertions(+), 76 deletions(-) create mode 100644 src/main/kotlin/ltd/hlaeja/controller/TypeController.kt create mode 100644 src/main/kotlin/ltd/hlaeja/exception/DeviceRegistryException.kt create mode 100644 src/main/kotlin/ltd/hlaeja/exception/TypeNameDuplicateException.kt create mode 100644 src/main/kotlin/ltd/hlaeja/form/TypeForm.kt create mode 100644 src/main/kotlin/ltd/hlaeja/property/DeviceRegistryProperty.kt create mode 100644 src/main/kotlin/ltd/hlaeja/service/DeviceRegistryService.kt create mode 100644 src/main/kotlin/ltd/hlaeja/util/Controller.kt create mode 100644 src/main/kotlin/ltd/hlaeja/util/DeviceRegisterWebClientCalls.kt create mode 100644 src/main/resources/templates/device/type/form.html create mode 100644 src/main/resources/templates/device/type/list.html create mode 100644 src/main/resources/templates/pagination.html diff --git a/README.md b/README.md index 3b97d95..0fa9f52 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ In realms of connectedness, where devices roam free, A nexus of management, harm | spring.profiles.active | ✓ | Spring Boot environment | | jwt.public-key | ✓ | JWT public key file | | account-registry.url | ✓ | Account Register URL | +| device-registry.url | ✓ | Device Register URL | *Required: ✓ can be stored as text, and ✗ need to be stored as secret.* diff --git a/build.gradle.kts b/build.gradle.kts index f402985..88d3a73 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -16,7 +16,9 @@ dependencies { implementation(hlaeja.library.common.messages) implementation(hlaeja.library.jwt) implementation(hlaeja.projectreactor.kotlin.reactor.extensions) + implementation(hlaeja.springboot.redis.session) implementation(hlaeja.springboot.starter.actuator) + implementation(hlaeja.springboot.starter.redis) implementation(hlaeja.springboot.starter.security) implementation(hlaeja.springboot.starter.thymeleaf) implementation(hlaeja.springboot.starter.validation) diff --git a/gradle.properties b/gradle.properties index 6150f21..cad1528 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ kotlin.code.style=official version=0.2.0-SNAPSHOT -catalog=0.9.0 +catalog=0.10.0-SNAPSHOT container.port.host=9060 diff --git a/src/main/kotlin/ltd/hlaeja/Application.kt b/src/main/kotlin/ltd/hlaeja/Application.kt index dfbffee..adca0c4 100644 --- a/src/main/kotlin/ltd/hlaeja/Application.kt +++ b/src/main/kotlin/ltd/hlaeja/Application.kt @@ -1,12 +1,14 @@ package ltd.hlaeja import ltd.hlaeja.property.AccountRegistryProperty +import ltd.hlaeja.property.DeviceRegistryProperty import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.boot.runApplication @EnableConfigurationProperties( AccountRegistryProperty::class, + DeviceRegistryProperty::class, ) @SpringBootApplication class Application diff --git a/src/main/kotlin/ltd/hlaeja/configuration/SecurityConfiguration.kt b/src/main/kotlin/ltd/hlaeja/configuration/SecurityConfiguration.kt index bd35828..8ebf91d 100644 --- a/src/main/kotlin/ltd/hlaeja/configuration/SecurityConfiguration.kt +++ b/src/main/kotlin/ltd/hlaeja/configuration/SecurityConfiguration.kt @@ -36,6 +36,7 @@ class SecurityConfiguration { private fun AuthorizeExchangeSpec.adminPaths(): AuthorizeExchangeSpec.Access = pathMatchers( "/account/**", + "/type/**", ) private fun AuthorizeExchangeSpec.publicPaths(): AuthorizeExchangeSpec.Access = pathMatchers( diff --git a/src/main/kotlin/ltd/hlaeja/controller/AccountController.kt b/src/main/kotlin/ltd/hlaeja/controller/AccountController.kt index 7209a48..e4d617a 100644 --- a/src/main/kotlin/ltd/hlaeja/controller/AccountController.kt +++ b/src/main/kotlin/ltd/hlaeja/controller/AccountController.kt @@ -1,5 +1,7 @@ package ltd.hlaeja.controller +import jakarta.validation.constraints.Max +import jakarta.validation.constraints.Min import java.util.UUID import ltd.hlaeja.controller.validation.CreateGroup import ltd.hlaeja.controller.validation.EditGroup @@ -11,6 +13,7 @@ import ltd.hlaeja.form.AccountForm import ltd.hlaeja.service.AccountRegistryService import ltd.hlaeja.util.toAccountForm import ltd.hlaeja.util.toAccountRequest +import ltd.hlaeja.util.validationErrors import org.springframework.stereotype.Controller import org.springframework.ui.Model import org.springframework.validation.BindingResult @@ -30,6 +33,8 @@ class AccountController( companion object { const val DEFAULT_PAGE: Int = 1 const val DEFAULT_SIZE: Int = 25 + const val MIN: Long = 1 + const val MAX: Long = 100 } @GetMapping("/edit-{account}") @@ -50,22 +55,13 @@ class AccountController( @Validated(EditGroup::class) @ModelAttribute("accountForm") accountForm: AccountForm, bindingResult: BindingResult, model: Model, - ): Mono { - val validationErrors = if (bindingResult.hasErrors()) { - bindingResult.allErrors.map { error -> - error.defaultMessage ?: "Unknown validation error" - } - } else { - emptyList() - } - if (bindingResult.hasErrors()) { - model.addAttribute("accountForm", accountForm) - model.addAttribute("validationErrors", validationErrors) - model.addAttribute("roleGroups", accountRegistryService.getRoles()) - return Mono.just("account/edit") - } - - return Mono.just(accountForm) + ): Mono = if (bindingResult.hasErrors()) { + model.addAttribute("accountForm", accountForm) + model.addAttribute("validationErrors", validationErrors(bindingResult)) + model.addAttribute("roleGroups", accountRegistryService.getRoles()) + Mono.just("account/edit") + } else { + Mono.just(accountForm) .flatMap { accountRegistryService.updateAccount(account, it.toAccountRequest()) } .doOnNext { model.addAttribute("successMessage", listOf("Saved changes!!!")) @@ -82,6 +78,7 @@ class AccountController( "validationErrors", "Username already exists. Please choose another.", ) + else -> Pair("validationErrors", "An unexpected error occurred. Please try again later.") } @@ -107,21 +104,13 @@ class AccountController( @Validated(CreateGroup::class) @ModelAttribute("accountForm") accountForm: AccountForm, bindingResult: BindingResult, model: Model, - ): Mono { - val validationErrors = if (bindingResult.hasErrors()) { - bindingResult.allErrors.map { error -> - error.defaultMessage ?: "Unknown validation error" - } - } else { - emptyList() - } - if (bindingResult.hasErrors()) { - model.addAttribute("accountForm", accountForm) - model.addAttribute("validationErrors", validationErrors) - model.addAttribute("roleGroups", accountRegistryService.getRoles()) - return Mono.just("account/create") - } - return Mono.just(accountForm) + ): Mono = if (bindingResult.hasErrors()) { + model.addAttribute("accountForm", accountForm) + model.addAttribute("roleGroups", accountRegistryService.getRoles()) + model.addAttribute("validationErrors", validationErrors(bindingResult)) + Mono.just("account/create") + } else { + Mono.just(accountForm) .flatMap { accountRegistryService.addAccount(it.toAccountRequest()) } .map { "redirect:/account" } .onErrorResume { error -> @@ -135,33 +124,20 @@ class AccountController( } } - @GetMapping - fun getDefaultAccounts( + @GetMapping( + "", + "/page-{page}", + "/page-{page}/show-{show}", + ) + fun getAccounts( + @PathVariable(required = false) @Min(MIN) page: Int = DEFAULT_PAGE, + @PathVariable(required = false) @Min(MIN) @Max(MAX) show: Int = DEFAULT_SIZE, model: Model, - ): Mono = getAccounts(DEFAULT_PAGE, DEFAULT_SIZE, model) - - @GetMapping("/page-{page}") - fun getAccountsPage( - @PathVariable page: Int, - model: Model, - ): Mono = getAccounts(page, DEFAULT_SIZE, model) - - @GetMapping("/page-{page}/show-{size}") - fun getAccountsPageSize( - @PathVariable page: Int, - @PathVariable size: Int, - model: Model, - ): Mono = getAccounts(page, size, model) - - private fun getAccounts( - page: Int, - size: Int, - model: Model, - ) = accountRegistryService.getAccounts(page, size) + ): Mono = accountRegistryService.getAccounts(page, show) .collectList() .doOnNext { items -> model.addAttribute("items", items) - model.addAttribute("pagination", Pagination(page, size, items.size, DEFAULT_SIZE)) + model.addAttribute("pagination", Pagination(page, show, items.size, DEFAULT_SIZE)) } .then(Mono.just("account/users")) } diff --git a/src/main/kotlin/ltd/hlaeja/controller/TypeController.kt b/src/main/kotlin/ltd/hlaeja/controller/TypeController.kt new file mode 100644 index 0000000..a862ba3 --- /dev/null +++ b/src/main/kotlin/ltd/hlaeja/controller/TypeController.kt @@ -0,0 +1,142 @@ +package ltd.hlaeja.controller + +import jakarta.validation.constraints.Max +import jakarta.validation.constraints.Min +import java.util.UUID +import ltd.hlaeja.dto.Pagination +import ltd.hlaeja.exception.NoChangeException +import ltd.hlaeja.exception.NotFoundException +import ltd.hlaeja.exception.TypeNameDuplicateException +import ltd.hlaeja.form.TypeForm +import ltd.hlaeja.service.DeviceRegistryService +import ltd.hlaeja.util.toTypeForm +import ltd.hlaeja.util.toTypeRequest +import ltd.hlaeja.util.validationErrors +import org.springframework.stereotype.Controller +import org.springframework.ui.Model +import org.springframework.validation.BindingResult +import org.springframework.validation.annotation.Validated +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.ModelAttribute +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping +import reactor.core.publisher.Mono + +@Controller +class TypeController( + private val deviceRegistryService: DeviceRegistryService, +) { + companion object { + const val DEFAULT_PAGE: Int = 1 + const val DEFAULT_SIZE: Int = 25 + const val MIN: Long = 1 + const val MAX: Long = 100 + } + + @GetMapping( + "/type", + "/type/page-{page}", + "/type/page-{page}/show-{show}", + ) + fun getTypes( + @PathVariable(required = false) @Min(MIN) page: Int = DEFAULT_PAGE, + @PathVariable(required = false) @Min(MIN) @Max(MAX) show: Int = DEFAULT_SIZE, + model: Model, + ) = deviceRegistryService.getTypes(page, show) + .collectList() + .doOnNext { items -> + model.addAttribute("items", items) + model.addAttribute("pagination", Pagination(page, show, items.size, DEFAULT_SIZE)) + } + .then(Mono.just("device/type/list")) + + @GetMapping("/type/create") + fun getCreateType( + model: Model, + ): Mono = Mono.just("device/type/form") + .doOnNext { + model.addAttribute("typeForm", TypeForm()) + } + + @PostMapping("/type/create") + fun postCreateType( + @Validated @ModelAttribute("typeForm") typeForm: TypeForm, + bindingResult: BindingResult, + model: Model, + ): Mono = if (bindingResult.hasErrors()) { + model.addAttribute("typeForm", typeForm) + model.addAttribute("validationErrors", validationErrors(bindingResult)) + Mono.just("device/type/form") + } else { + Mono.just(typeForm) + .flatMap { deviceRegistryService.createType(it.toTypeRequest()) } + .map { "redirect:/type" } + .onErrorResume { error -> + val errorMessage = when (error) { + is TypeNameDuplicateException -> "Type name already exists. Please choose another." + else -> "An unexpected error occurred. Please try again later." + } + model.addAttribute("validationErrors", listOf(errorMessage)) + Mono.just("device/type/form") + } + } + + @GetMapping("/type-{type}") + fun getEditType( + @PathVariable type: UUID, + model: Model, + ): Mono = deviceRegistryService.getType(type) + .doOnNext { + model.addAttribute("type", it) + model.addAttribute("typeForm", it.toTypeForm()) + } + .then(Mono.just("device/type/form")) + + @PostMapping("/type-{type}") + fun postEditType( + @PathVariable type: UUID, + @Validated @ModelAttribute("typeForm") typeForm: TypeForm, + bindingResult: BindingResult, + model: Model, + ): Mono = if (bindingResult.hasErrors()) { + deviceRegistryService.getType(type) + .doOnNext { + model.addAttribute("type", it) + model.addAttribute("typeForm", typeForm) + model.addAttribute("validationErrors", validationErrors(bindingResult)) + } + .then( + Mono.just("device/type/form"), + ) + } else { + Mono.just(typeForm) + .flatMap { deviceRegistryService.updateType(type, it.toTypeRequest()) } + .doOnNext { + model.addAttribute("successMessage", listOf("Saved changes!!!")) + model.addAttribute("type", it) + model.addAttribute("typeForm", it.toTypeForm()) + } + .then(Mono.just("device/type/form")) + .onErrorResume { error -> + val errorMessage = when (error) { + is NoChangeException -> Pair("successMessage", "No change to save.") + is NotFoundException -> Pair("validationErrors", "User dont exists. how did this happen?") + is TypeNameDuplicateException -> Pair( + "validationErrors", + "Type name already exists. Please choose another.", + ) + + else -> Pair("validationErrors", "An unexpected error occurred. Please try again later.") + } + deviceRegistryService.getType(type) + .doOnNext { + model.addAttribute(errorMessage.first, listOf(errorMessage.second)) + model.addAttribute("type", it) + model.addAttribute("typeForm", typeForm) + } + .then( + Mono.just("device/type/form"), + ) + } + } +} diff --git a/src/main/kotlin/ltd/hlaeja/exception/DeviceRegistryException.kt b/src/main/kotlin/ltd/hlaeja/exception/DeviceRegistryException.kt new file mode 100644 index 0000000..b2f2aa0 --- /dev/null +++ b/src/main/kotlin/ltd/hlaeja/exception/DeviceRegistryException.kt @@ -0,0 +1,23 @@ +package ltd.hlaeja.exception + +@Suppress("unused") +open class DeviceRegistryException : HlaejaException { + + constructor() : super() + + constructor(message: String) : super(message) + + constructor(cause: Throwable) : super(cause) + + constructor( + message: String, + cause: Throwable, + ) : super(message, cause) + + constructor( + message: String, + cause: Throwable, + enableSuppression: Boolean, + writableStackTrace: Boolean, + ) : super(message, cause, enableSuppression, writableStackTrace) +} diff --git a/src/main/kotlin/ltd/hlaeja/exception/TypeNameDuplicateException.kt b/src/main/kotlin/ltd/hlaeja/exception/TypeNameDuplicateException.kt new file mode 100644 index 0000000..30c8d2c --- /dev/null +++ b/src/main/kotlin/ltd/hlaeja/exception/TypeNameDuplicateException.kt @@ -0,0 +1,23 @@ +package ltd.hlaeja.exception + +@Suppress("unused") +open class TypeNameDuplicateException : DeviceRegistryException { + + constructor() : super() + + constructor(message: String) : super(message) + + constructor(cause: Throwable) : super(cause) + + constructor( + message: String, + cause: Throwable, + ) : super(message, cause) + + constructor( + message: String, + cause: Throwable, + enableSuppression: Boolean, + writableStackTrace: Boolean, + ) : super(message, cause, enableSuppression, writableStackTrace) +} diff --git a/src/main/kotlin/ltd/hlaeja/form/TypeForm.kt b/src/main/kotlin/ltd/hlaeja/form/TypeForm.kt new file mode 100644 index 0000000..b2a0d99 --- /dev/null +++ b/src/main/kotlin/ltd/hlaeja/form/TypeForm.kt @@ -0,0 +1,10 @@ +package ltd.hlaeja.form + +import jakarta.validation.constraints.Size + +data class TypeForm( + @field:Size(min = 2, max = 50, message = "Name must be between 2 and 50 characters") + val name: String = "", + @field:Size(min = 2, max = 1000, message = "Description must be between 2 and 1000 characters") + val description: String = "", +) diff --git a/src/main/kotlin/ltd/hlaeja/property/DeviceRegistryProperty.kt b/src/main/kotlin/ltd/hlaeja/property/DeviceRegistryProperty.kt new file mode 100644 index 0000000..bee98ec --- /dev/null +++ b/src/main/kotlin/ltd/hlaeja/property/DeviceRegistryProperty.kt @@ -0,0 +1,8 @@ +package ltd.hlaeja.property + +import org.springframework.boot.context.properties.ConfigurationProperties + +@ConfigurationProperties(prefix = "device-registry") +data class DeviceRegistryProperty( + val url: String, +) diff --git a/src/main/kotlin/ltd/hlaeja/security/RemoteUserDetail.kt b/src/main/kotlin/ltd/hlaeja/security/RemoteUserDetail.kt index 6089ddf..bc6d35c 100644 --- a/src/main/kotlin/ltd/hlaeja/security/RemoteUserDetail.kt +++ b/src/main/kotlin/ltd/hlaeja/security/RemoteUserDetail.kt @@ -1,8 +1,14 @@ package ltd.hlaeja.security +import java.io.Serializable import java.util.UUID data class RemoteUserDetail( val id: UUID, val username: String, -) +) : Serializable { + companion object { + @Suppress("ConstPropertyName") + private const val serialVersionUID = 1L + } +} diff --git a/src/main/kotlin/ltd/hlaeja/service/DeviceRegistryService.kt b/src/main/kotlin/ltd/hlaeja/service/DeviceRegistryService.kt new file mode 100644 index 0000000..0a42dcf --- /dev/null +++ b/src/main/kotlin/ltd/hlaeja/service/DeviceRegistryService.kt @@ -0,0 +1,63 @@ +package ltd.hlaeja.service + +import java.util.UUID +import ltd.hlaeja.exception.DeviceRegistryException +import ltd.hlaeja.exception.HlaejaException +import ltd.hlaeja.library.deviceRegistry.Type +import ltd.hlaeja.library.deviceRegistry.Types +import ltd.hlaeja.property.DeviceRegistryProperty +import ltd.hlaeja.util.deviceRegistryType +import ltd.hlaeja.util.deviceRegistryTypes +import ltd.hlaeja.util.deviceRegistryTypesCreate +import ltd.hlaeja.util.deviceRegistryTypesUpdate +import org.springframework.http.HttpStatus.BAD_REQUEST +import org.springframework.stereotype.Service +import org.springframework.web.reactive.function.client.WebClient +import org.springframework.web.server.ResponseStatusException +import reactor.core.publisher.Flux +import reactor.core.publisher.Mono + +@Service +class DeviceRegistryService( + private val webClient: WebClient, + private val property: DeviceRegistryProperty, +) { + + fun getTypes( + page: Int, + show: Int, + ): Flux = webClient.deviceRegistryTypes(page, show, property) + + fun createType( + request: Type.Request, + ): Mono = webClient.deviceRegistryTypesCreate(request, property) + .onErrorResume { error -> + when (error) { + is DeviceRegistryException -> Mono.error(error) + else -> Mono.error(ResponseStatusException(BAD_REQUEST, error.message)) + } + } + + fun getType( + type: UUID, + ): Mono = webClient.deviceRegistryType(type, property) + .onErrorResume { error -> + when (error) { + is ResponseStatusException -> Mono.error(error) + else -> Mono.error(ResponseStatusException(BAD_REQUEST, error.message)) + } + } + + fun updateType( + type: UUID, + request: Type.Request, + ): Mono = webClient.deviceRegistryTypesUpdate(type, request, property) + .onErrorResume(::errorHandler) + + private fun errorHandler( + error: Throwable, + ): Mono = when (error) { + is HlaejaException -> Mono.error(error) + else -> Mono.error(ResponseStatusException(BAD_REQUEST, error.message)) + } +} diff --git a/src/main/kotlin/ltd/hlaeja/util/Controller.kt b/src/main/kotlin/ltd/hlaeja/util/Controller.kt new file mode 100644 index 0000000..a4c7dcd --- /dev/null +++ b/src/main/kotlin/ltd/hlaeja/util/Controller.kt @@ -0,0 +1,11 @@ +package ltd.hlaeja.util + +import org.springframework.validation.BindingResult + +fun validationErrors( + bindingResult: BindingResult, +): List = if (bindingResult.hasErrors()) { + bindingResult.allErrors.map { error -> error.defaultMessage ?: "Unknown validation error" } +} else { + emptyList() +} diff --git a/src/main/kotlin/ltd/hlaeja/util/DeviceRegisterWebClientCalls.kt b/src/main/kotlin/ltd/hlaeja/util/DeviceRegisterWebClientCalls.kt new file mode 100644 index 0000000..270b153 --- /dev/null +++ b/src/main/kotlin/ltd/hlaeja/util/DeviceRegisterWebClientCalls.kt @@ -0,0 +1,61 @@ +package ltd.hlaeja.util + +import java.util.UUID +import ltd.hlaeja.exception.DeviceRegistryException +import ltd.hlaeja.exception.NoChangeException +import ltd.hlaeja.exception.NotFoundException +import ltd.hlaeja.exception.TypeNameDuplicateException +import ltd.hlaeja.library.deviceRegistry.Type +import ltd.hlaeja.library.deviceRegistry.Types +import ltd.hlaeja.property.DeviceRegistryProperty +import org.springframework.http.HttpStatus.ACCEPTED +import org.springframework.http.HttpStatus.BAD_REQUEST +import org.springframework.http.HttpStatus.CONFLICT +import org.springframework.http.HttpStatus.NOT_FOUND +import org.springframework.web.reactive.function.client.WebClient +import org.springframework.web.server.ResponseStatusException +import reactor.core.publisher.Flux +import reactor.core.publisher.Mono + +fun WebClient.deviceRegistryTypes( + page: Int, + size: Int, + property: DeviceRegistryProperty, +): Flux = get() + .uri("${property.url}/types/page-$page/show-$size".also(::logCall)) + .retrieve() + .bodyToFlux(Types.Response::class.java) + +fun WebClient.deviceRegistryTypesCreate( + request: Type.Request, + property: DeviceRegistryProperty, +): Mono = post() + .uri("${property.url}/type".also(::logCall)) + .bodyValue(request) + .retrieve() + .onStatus(CONFLICT::equals) { throw TypeNameDuplicateException("Remote service returned 409") } + .onStatus(BAD_REQUEST::equals) { throw DeviceRegistryException("Remote service returned 400") } + .bodyToMono(Type.Response::class.java) + +fun WebClient.deviceRegistryType( + type: UUID, + property: DeviceRegistryProperty, +): Mono = get() + .uri("${property.url}/type-$type".also(::logCall)) + .retrieve() + .onStatus(NOT_FOUND::equals) { throw ResponseStatusException(NOT_FOUND) } + .bodyToMono(Type.Response::class.java) + +fun WebClient.deviceRegistryTypesUpdate( + type: UUID, + request: Type.Request, + property: DeviceRegistryProperty, +): Mono = put() + .uri("${property.url}/type-$type".also(::logCall)) + .bodyValue(request) + .retrieve() + .onStatus(ACCEPTED::equals) { throw NoChangeException("Remote service returned 202") } + .onStatus(BAD_REQUEST::equals) { throw DeviceRegistryException("Remote service returned 400") } + .onStatus(NOT_FOUND::equals) { throw NotFoundException("Remote service returned 404") } + .onStatus(CONFLICT::equals) { throw TypeNameDuplicateException("Remote service returned 409") } + .bodyToMono(Type.Response::class.java) diff --git a/src/main/kotlin/ltd/hlaeja/util/Mapping.kt b/src/main/kotlin/ltd/hlaeja/util/Mapping.kt index c68b40e..024045b 100644 --- a/src/main/kotlin/ltd/hlaeja/util/Mapping.kt +++ b/src/main/kotlin/ltd/hlaeja/util/Mapping.kt @@ -1,8 +1,10 @@ package ltd.hlaeja.util import ltd.hlaeja.form.AccountForm +import ltd.hlaeja.form.TypeForm import ltd.hlaeja.library.accountRegistry.Account import ltd.hlaeja.library.accountRegistry.Authentication +import ltd.hlaeja.library.deviceRegistry.Type import org.springframework.security.core.Authentication as SpringAuthentication fun SpringAuthentication.toAuthenticationRequest(): Authentication.Request = Authentication.Request( @@ -26,3 +28,13 @@ fun Account.Response.toAccountForm(): AccountForm = AccountForm( .replaceFirstChar { char -> char.uppercase() } }, ) + +fun TypeForm.toTypeRequest(): Type.Request = Type.Request( + name = name, + description = description, +) + +fun Type.Response.toTypeForm(): Type.Request = Type.Request( + name = name, + description = description, +) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 8916164..004ae23 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -9,6 +9,14 @@ spring: os: name: "%APP_BUILD_OS_NAME%" version: "%APP_BUILD_OS_VERSION%" + session: + timeout: 60m + + redis: + namespace: "spring:session:management" + data: + redis: + port: 6379 management: endpoints: @@ -42,10 +50,17 @@ spring: web: resources: static-locations: file:src/main/resources/static/ + data: + redis: + host: localhost + database: 2 account-registry: url: http://localhost:9050 +device-registry: + url: http://localhost:9010 + --- ########################## ### Docker environment ### @@ -54,10 +69,17 @@ spring: config: activate: on-profile: docker + data: + redis: + host: Redis + database: 2 account-registry: url: http://AccountRegistry:8080 +device-registry: + url: http://DeviceRegistry:8080 + --- ############################## ### Production environment ### diff --git a/src/main/resources/templates/account/users.html b/src/main/resources/templates/account/users.html index b867e9d..aa033aa 100644 --- a/src/main/resources/templates/account/users.html +++ b/src/main/resources/templates/account/users.html @@ -17,7 +17,7 @@
@@ -39,29 +39,13 @@
-
-
- Previous - Previous - Next - Next -
-
- Previous - Previous - Next - Next -
-
+ diff --git a/src/main/resources/templates/device/type/form.html b/src/main/resources/templates/device/type/form.html new file mode 100644 index 0000000..236a7bd --- /dev/null +++ b/src/main/resources/templates/device/type/form.html @@ -0,0 +1,44 @@ + + + + + +
+
+
+
+

+

+
+
+ Created: +
+
+
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + + + + diff --git a/src/main/resources/templates/device/type/list.html b/src/main/resources/templates/device/type/list.html new file mode 100644 index 0000000..4143a1a --- /dev/null +++ b/src/main/resources/templates/device/type/list.html @@ -0,0 +1,55 @@ + + + + + +
+
+

Device Types

+
+
+
+ + + + + + + + + + + + + + + + + + + + +
NameTimeIDActions
No accounts found
Edit
+
+ +
+
+ + + + + diff --git a/src/main/resources/templates/layout.html b/src/main/resources/templates/layout.html index 2b7402d..8e04443 100644 --- a/src/main/resources/templates/layout.html +++ b/src/main/resources/templates/layout.html @@ -22,6 +22,7 @@