From 86b61cab8cef51f745ad4649287e173caed5ccde Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Oct 2023 16:53:22 +0000 Subject: [PATCH 1/3] Bump io.micronaut.application from 3.7.10 to 4.1.2 Bumps io.micronaut.application from 3.7.10 to 4.1.2. --- updated-dependencies: - dependency-name: io.micronaut.application dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 19f4953..4f934de 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,7 @@ plugins { id("org.jetbrains.kotlin.kapt") version "1.9.10" id("org.jetbrains.kotlin.plugin.allopen") version "1.9.10" id("com.github.johnrengelman.shadow") version "8.1.1" - id("io.micronaut.application") version "3.7.10" + id("io.micronaut.application") version "4.1.2" id("nu.studer.jooq") version "8.2.1" id("com.github.node-gradle.node") version "7.0.1" id("org.jlleitschuh.gradle.ktlint") version "11.6.1" From 55a848d1dbd2ad174c69e33b6731eaac3b9e2faa Mon Sep 17 00:00:00 2001 From: Simon Weis Date: Fri, 3 Nov 2023 13:11:44 +0100 Subject: [PATCH 2/3] Migrate to Micronaut 4 --- build.gradle.kts | 41 ++++++++++--------- .../config/DatabaseAuthenticationProvider.kt | 3 +- .../de/w3is/recipes/images/model/ImageId.kt | 2 + .../de/w3is/recipes/recipes/RecipeService.kt | 2 +- .../infra/api/RecipeSearchController.kt | 3 +- .../recipes/infra/api/RecipeSearchModel.kt | 6 +++ .../recipes/infra/api/RecipeViewModel.kt | 7 ++++ .../de/w3is/recipes/recipes/model/Search.kt | 7 ++++ .../recipes/users/InvitationRepository.kt | 4 +- .../w3is/recipes/users/InvitationService.kt | 12 +++--- .../users/infra/api/InvitationViewModel.kt | 5 +++ .../w3is/recipes/users/infra/api/Profile.kt | 4 ++ .../persistence/JooqInvitationRepository.kt | 7 ++-- src/main/resources/application.yml | 3 ++ src/main/resources/logback.xml | 10 ++--- .../persistence/JooqRecipeRepositoryTest.kt | 6 +-- .../recipes/users/InvitationServiceTest.kt | 2 +- 17 files changed, 77 insertions(+), 47 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 4f934de..d7aa920 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,10 +4,11 @@ import org.jooq.meta.jaxb.Property plugins { id("org.jetbrains.kotlin.jvm") version "1.9.10" - id("org.jetbrains.kotlin.kapt") version "1.9.10" id("org.jetbrains.kotlin.plugin.allopen") version "1.9.10" + id("com.google.devtools.ksp") version "1.9.10-1.0.13" id("com.github.johnrengelman.shadow") version "8.1.1" id("io.micronaut.application") version "4.1.2" + id("io.micronaut.aot") version "4.1.2" id("nu.studer.jooq") version "8.2.1" id("com.github.node-gradle.node") version "7.0.1" id("org.jlleitschuh.gradle.ktlint") version "11.6.1" @@ -17,12 +18,11 @@ version = "1.0" group = "de.w3is" val kotlinVersion = "1.9.10" -val micronautVersion = "3.7.4" +val micronautVersion = "4.1.6" val postgresqlJdbcVersion = "42.6.0" val jooqVersion = "3.18.7" val nodeVersion = "18.12.0" val nodeNpmVersion = "8.19.2" -val javaxTransactionApiVersion = "1.3" val assertKVersion = "0.27.0" val thumbnailatorVersion = "0.4.20" val commonsTextVersion = "1.11.0" @@ -37,17 +37,14 @@ repositories { dependencies { jooqGenerator("org.jooq:jooq-meta-extensions:$jooqVersion") - kapt("io.micronaut:micronaut-http-validation") + ksp("io.micronaut.serde:micronaut-serde-processor") implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion") implementation("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion") - implementation("jakarta.annotation:jakarta.annotation-api") - implementation("javax.transaction:javax.transaction-api:$javaxTransactionApiVersion") + implementation("jakarta.transaction:jakarta.transaction-api") implementation("io.micronaut.kotlin:micronaut-kotlin-runtime") - implementation("io.micronaut:micronaut-http-client") - implementation("io.micronaut:micronaut-jackson-databind") - implementation("io.micronaut:micronaut-validation") + implementation("io.micronaut.serde:micronaut-serde-jackson") implementation("io.micronaut.security:micronaut-security") implementation("io.micronaut.security:micronaut-security-jwt") implementation("io.micronaut.security:micronaut-security-annotations") @@ -61,12 +58,10 @@ dependencies { implementation("net.coobird:thumbnailator:$thumbnailatorVersion") implementation("org.apache.commons:commons-text:$commonsTextVersion") + runtimeOnly("org.yaml:snakeyaml") runtimeOnly("ch.qos.logback:logback-classic") - runtimeOnly("com.fasterxml.jackson.module:jackson-module-kotlin") runtimeOnly("com.h2database:h2:$h2Version") runtimeOnly("org.postgresql:postgresql:$postgresqlJdbcVersion") - - kaptTest("io.micronaut:micronaut-inject-java") testImplementation("com.willowtreeapps.assertk:assertk:$assertKVersion") testImplementation("org.mockito.kotlin:mockito-kotlin:$mockitoVersion") } @@ -100,14 +95,7 @@ tasks { } } -kapt { - arguments { - arg("micronaut.processing.incremental", true) - arg("micronaut.processing.annotations", "de.w3is.recipes.*") - } -} - -graalvmNative.toolchainDetection.set(false) +graalvmNative.toolchainDetection.set(true) micronaut { version(micronautVersion) @@ -117,6 +105,18 @@ micronaut { incremental(true) annotations("de.w3is.recipes.*") } + aot { + optimizeServiceLoading.set(true) + convertYamlToJava.set(true) + replaceLogbackXml.set(true) + precomputeOperations.set(true) + cacheEnvironment.set(true) + optimizeClassLoading.set(true) + deduceEnvironment.set(true) + optimizeNetty.set(true) + possibleEnvironments.set(listOf("h2", "h2-local", "postgres")) + targetEnvironments.set(listOf("h2", "postgres")) + } } jooq { @@ -138,6 +138,7 @@ jooq { target.apply { packageName = "de.w3is.recipes.infra.persistence.generated" } + generate.withJpaAnnotations(true) } } } diff --git a/src/main/kotlin/de/w3is/recipes/config/DatabaseAuthenticationProvider.kt b/src/main/kotlin/de/w3is/recipes/config/DatabaseAuthenticationProvider.kt index 78182da..0ed0b2f 100644 --- a/src/main/kotlin/de/w3is/recipes/config/DatabaseAuthenticationProvider.kt +++ b/src/main/kotlin/de/w3is/recipes/config/DatabaseAuthenticationProvider.kt @@ -13,7 +13,8 @@ import org.slf4j.LoggerFactory import reactor.core.publisher.Mono @Singleton -class DatabaseAuthenticationProvider(private val userRepository: UserRepository) : AuthenticationProvider { +class DatabaseAuthenticationProvider(private val userRepository: UserRepository) : + AuthenticationProvider> { private val logger = LoggerFactory.getLogger(DatabaseAuthenticationProvider::class.java) diff --git a/src/main/kotlin/de/w3is/recipes/images/model/ImageId.kt b/src/main/kotlin/de/w3is/recipes/images/model/ImageId.kt index f36fcca..9b9e39a 100644 --- a/src/main/kotlin/de/w3is/recipes/images/model/ImageId.kt +++ b/src/main/kotlin/de/w3is/recipes/images/model/ImageId.kt @@ -1,7 +1,9 @@ package de.w3is.recipes.images.model +import io.micronaut.serde.annotation.Serdeable import java.util.* +@Serdeable data class ImageId(val value: String) { companion object { fun new(): ImageId = ImageId(UUID.randomUUID().toString()) diff --git a/src/main/kotlin/de/w3is/recipes/recipes/RecipeService.kt b/src/main/kotlin/de/w3is/recipes/recipes/RecipeService.kt index 6b06388..a7636d6 100644 --- a/src/main/kotlin/de/w3is/recipes/recipes/RecipeService.kt +++ b/src/main/kotlin/de/w3is/recipes/recipes/RecipeService.kt @@ -7,10 +7,10 @@ import de.w3is.recipes.recipes.model.Recipe import de.w3is.recipes.recipes.model.RecipeId import de.w3is.recipes.users.model.User import jakarta.inject.Singleton +import jakarta.transaction.Transactional import java.io.InputStream import java.time.Clock import java.time.OffsetDateTime -import javax.transaction.Transactional @Singleton open class RecipeService( diff --git a/src/main/kotlin/de/w3is/recipes/recipes/infra/api/RecipeSearchController.kt b/src/main/kotlin/de/w3is/recipes/recipes/infra/api/RecipeSearchController.kt index 2c70bb7..8326907 100644 --- a/src/main/kotlin/de/w3is/recipes/recipes/infra/api/RecipeSearchController.kt +++ b/src/main/kotlin/de/w3is/recipes/recipes/infra/api/RecipeSearchController.kt @@ -7,6 +7,7 @@ import de.w3is.recipes.recipes.model.AuthorId import de.w3is.recipes.recipes.model.FilterKey import de.w3is.recipes.recipes.model.Recipe import de.w3is.recipes.recipes.model.SearchRequest +import io.micronaut.http.annotation.Body import io.micronaut.http.annotation.Controller import io.micronaut.http.annotation.Post import io.micronaut.security.annotation.Secured @@ -20,7 +21,7 @@ class RecipeSearchController( ) { @Post - fun search(searchRequestViewModel: SearchRequestViewModel): SearchResponseViewModel { + fun search(@Body searchRequestViewModel: SearchRequestViewModel): SearchResponseViewModel { val searchResponse = recipeRepository.search(searchRequestViewModel.toSearchRequest()) val possibleFilter = searchResponse.possibleFilter.toViewModel() diff --git a/src/main/kotlin/de/w3is/recipes/recipes/infra/api/RecipeSearchModel.kt b/src/main/kotlin/de/w3is/recipes/recipes/infra/api/RecipeSearchModel.kt index dd1b172..81a0195 100644 --- a/src/main/kotlin/de/w3is/recipes/recipes/infra/api/RecipeSearchModel.kt +++ b/src/main/kotlin/de/w3is/recipes/recipes/infra/api/RecipeSearchModel.kt @@ -2,7 +2,9 @@ package de.w3is.recipes.recipes.infra.api import de.w3is.recipes.recipes.model.FilterKey import de.w3is.recipes.recipes.model.Order +import io.micronaut.serde.annotation.Serdeable +@Serdeable data class SearchRequestViewModel( val searchQuery: String, val page: PageViewModel, @@ -10,12 +12,14 @@ data class SearchRequestViewModel( val order: Order, ) +@Serdeable data class SearchResponseViewModel( val data: List, val page: PageViewModel, val possibleFilter: Map>, ) +@Serdeable data class SearchResponseData( val id: String, val imageUrl: String, @@ -25,12 +29,14 @@ data class SearchResponseData( val author: String, ) +@Serdeable data class PageViewModel( val size: Int, val number: Int, val maxNumber: Int?, ) +@Serdeable data class FilterValueViewModel( val value: String, val label: String, diff --git a/src/main/kotlin/de/w3is/recipes/recipes/infra/api/RecipeViewModel.kt b/src/main/kotlin/de/w3is/recipes/recipes/infra/api/RecipeViewModel.kt index bfb9797..3db09d4 100644 --- a/src/main/kotlin/de/w3is/recipes/recipes/infra/api/RecipeViewModel.kt +++ b/src/main/kotlin/de/w3is/recipes/recipes/infra/api/RecipeViewModel.kt @@ -1,5 +1,8 @@ package de.w3is.recipes.recipes.infra.api +import io.micronaut.serde.annotation.Serdeable + +@Serdeable data class NewRecipeRequest( val title: String, val category: String, @@ -10,10 +13,12 @@ data class NewRecipeRequest( val modifications: String, ) +@Serdeable data class NewRecipeResponse( val id: String, ) +@Serdeable data class RecipeViewModel( val id: String, val author: AuthorViewModel, @@ -27,11 +32,13 @@ data class RecipeViewModel( val images: List, ) +@Serdeable data class AuthorViewModel( val id: String, val name: String, ) +@Serdeable data class ImageViewModel( val id: String, val url: String?, diff --git a/src/main/kotlin/de/w3is/recipes/recipes/model/Search.kt b/src/main/kotlin/de/w3is/recipes/recipes/model/Search.kt index aa03b49..bf0dcee 100644 --- a/src/main/kotlin/de/w3is/recipes/recipes/model/Search.kt +++ b/src/main/kotlin/de/w3is/recipes/recipes/model/Search.kt @@ -1,5 +1,7 @@ package de.w3is.recipes.recipes.model +import io.micronaut.serde.annotation.Serdeable + data class SearchRequest( val query: String, val limit: Int, @@ -14,28 +16,33 @@ data class SearchResponse( val possibleFilter: Map>, ) +@Serdeable data class Page( val current: Int, val max: Int, val size: Int, ) +@Serdeable enum class FilterKey { AUTHOR, CATEGORY, CUISINE, } +@Serdeable data class Order( val field: OrderField, val direction: SortDir, ) +@Serdeable enum class OrderField { TITLE, CREATED_AT, } +@Serdeable enum class SortDir { ASC, DESC, diff --git a/src/main/kotlin/de/w3is/recipes/users/InvitationRepository.kt b/src/main/kotlin/de/w3is/recipes/users/InvitationRepository.kt index f0b06ae..10ae5ac 100644 --- a/src/main/kotlin/de/w3is/recipes/users/InvitationRepository.kt +++ b/src/main/kotlin/de/w3is/recipes/users/InvitationRepository.kt @@ -2,11 +2,11 @@ package de.w3is.recipes.users import de.w3is.recipes.users.model.Invite import de.w3is.recipes.users.model.UserId -import java.time.Duration +import java.time.OffsetDateTime interface InvitationRepository { fun store(invite: Invite) - fun deleteAllOlderThan(duration: Duration) + fun deleteAllOlderThan(dateTime: OffsetDateTime) fun findByCode(code: String): Invite? fun invalidate(invite: Invite) fun findByCreator(id: UserId): Invite? diff --git a/src/main/kotlin/de/w3is/recipes/users/InvitationService.kt b/src/main/kotlin/de/w3is/recipes/users/InvitationService.kt index f4655df..c262161 100644 --- a/src/main/kotlin/de/w3is/recipes/users/InvitationService.kt +++ b/src/main/kotlin/de/w3is/recipes/users/InvitationService.kt @@ -6,10 +6,10 @@ import de.w3is.recipes.users.model.User import io.micronaut.context.annotation.Property import io.micronaut.scheduling.annotation.Scheduled import jakarta.inject.Singleton +import jakarta.transaction.Transactional import org.slf4j.LoggerFactory -import java.lang.RuntimeException -import java.time.Duration -import javax.transaction.Transactional +import java.time.Clock +import java.time.OffsetDateTime @Singleton open class InvitationService( @@ -17,6 +17,7 @@ open class InvitationService( private val userService: UserService, @Property(name = "application.allowInvitationFor") private val allowInvitationForRole: List, + private val clock: Clock, ) { private val logger = LoggerFactory.getLogger(InvitationService::class.java) @@ -48,14 +49,15 @@ open class InvitationService( @Scheduled(fixedDelay = "1h") fun deleteOldInvitations() { logger.debug("Delete old invites") - invitationRepository.deleteAllOlderThan(Duration.ofDays(1)) + invitationRepository.deleteAllOlderThan(OffsetDateTime.now(clock).minusDays(1)) } fun isAllowedToInvite(user: User): Boolean { return user.role.name in allowInvitationForRole } - fun getInviteByCode(code: String): Invite = invitationRepository.findByCode(code) ?: throw InvitationNotFoundException() + fun getInviteByCode(code: String): Invite = + invitationRepository.findByCode(code) ?: throw InvitationNotFoundException() @Transactional open fun createUserByInvite(code: String, name: String, plainPassword: PlainPassword): User { diff --git a/src/main/kotlin/de/w3is/recipes/users/infra/api/InvitationViewModel.kt b/src/main/kotlin/de/w3is/recipes/users/infra/api/InvitationViewModel.kt index 602416f..ed6e0b4 100644 --- a/src/main/kotlin/de/w3is/recipes/users/infra/api/InvitationViewModel.kt +++ b/src/main/kotlin/de/w3is/recipes/users/infra/api/InvitationViewModel.kt @@ -1,13 +1,18 @@ package de.w3is.recipes.users.infra.api +import io.micronaut.serde.annotation.Serdeable + +@Serdeable data class InvitationCodeResponse( val code: String, ) +@Serdeable data class InvitationInfoResponse( val invitingUser: String, ) +@Serdeable data class InvitationRequest( val username: String, val password: String, diff --git a/src/main/kotlin/de/w3is/recipes/users/infra/api/Profile.kt b/src/main/kotlin/de/w3is/recipes/users/infra/api/Profile.kt index e057e79..520216a 100644 --- a/src/main/kotlin/de/w3is/recipes/users/infra/api/Profile.kt +++ b/src/main/kotlin/de/w3is/recipes/users/infra/api/Profile.kt @@ -1,5 +1,8 @@ package de.w3is.recipes.users.infra.api +import io.micronaut.serde.annotation.Serdeable + +@Serdeable data class Profile( val id: String, val name: String, @@ -7,6 +10,7 @@ data class Profile( val isAllowedToInvite: Boolean, ) +@Serdeable data class ChangePasswordRequest( val oldPassword: String, val newPassword: String, diff --git a/src/main/kotlin/de/w3is/recipes/users/infra/persistence/JooqInvitationRepository.kt b/src/main/kotlin/de/w3is/recipes/users/infra/persistence/JooqInvitationRepository.kt index edec34b..1e71d0b 100644 --- a/src/main/kotlin/de/w3is/recipes/users/infra/persistence/JooqInvitationRepository.kt +++ b/src/main/kotlin/de/w3is/recipes/users/infra/persistence/JooqInvitationRepository.kt @@ -8,13 +8,12 @@ import de.w3is.recipes.users.model.UserId import jakarta.inject.Singleton import org.jooq.DSLContext import java.time.Clock -import java.time.Duration import java.time.OffsetDateTime @Singleton class JooqInvitationRepository( - private val dslContext: DSLContext, private val clock: Clock, + private val dslContext: DSLContext, ) : InvitationRepository { override fun store(invite: Invite) { @@ -25,9 +24,9 @@ class JooqInvitationRepository( }.store() } - override fun deleteAllOlderThan(duration: Duration) { + override fun deleteAllOlderThan(dateTime: OffsetDateTime) { dslContext.deleteFrom(INVITATIONS) - .where(INVITATIONS.CREATED_ON.lessThan(OffsetDateTime.now(clock).minus(duration))) + .where(INVITATIONS.CREATED_ON.lessThan(dateTime)) .execute() } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index d8e0015..0b58be4 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,6 +1,9 @@ micronaut: application: name: recipes + serde: + serialization: + inclusion: ALWAYS server: cors: enabled: true diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 6010eb5..542f3d5 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -1,15 +1,11 @@ - - true - + %cyan(%d{HH:mm:ss.SSS}) %gray([%thread]) %highlight(%-5level) %magenta(%logger{36}) - %msg%n - - + - + \ No newline at end of file diff --git a/src/test/kotlin/de/w3is/recipes/recipes/infra/persistence/JooqRecipeRepositoryTest.kt b/src/test/kotlin/de/w3is/recipes/recipes/infra/persistence/JooqRecipeRepositoryTest.kt index 8ee1b8c..9105851 100644 --- a/src/test/kotlin/de/w3is/recipes/recipes/infra/persistence/JooqRecipeRepositoryTest.kt +++ b/src/test/kotlin/de/w3is/recipes/recipes/infra/persistence/JooqRecipeRepositoryTest.kt @@ -18,8 +18,6 @@ import de.w3is.recipes.recipes.model.RecipeId import de.w3is.recipes.recipes.model.SearchRequest import de.w3is.recipes.recipes.model.SortDir import de.w3is.recipes.testUser -import io.micronaut.context.annotation.Bean -import io.micronaut.context.annotation.Replaces import io.micronaut.test.extensions.junit5.annotation.MicronautTest import jakarta.inject.Inject import org.jooq.DSLContext @@ -33,9 +31,7 @@ import java.time.ZoneOffset @MicronautTest class JooqRecipeRepositoryTest { - @get:Bean - @get:Replaces(Clock::class) - val fixedClock: Clock = Clock.fixed(Instant.now(), ZoneOffset.UTC) + private val fixedClock: Clock = Clock.fixed(Instant.now(), ZoneOffset.UTC) @Inject private lateinit var recipeRepository: JooqRecipeRepository diff --git a/src/test/kotlin/de/w3is/recipes/users/InvitationServiceTest.kt b/src/test/kotlin/de/w3is/recipes/users/InvitationServiceTest.kt index cf161f4..2bba6af 100644 --- a/src/test/kotlin/de/w3is/recipes/users/InvitationServiceTest.kt +++ b/src/test/kotlin/de/w3is/recipes/users/InvitationServiceTest.kt @@ -9,10 +9,10 @@ import de.w3is.recipes.testUser import de.w3is.recipes.users.model.PlainPassword import io.micronaut.test.extensions.junit5.annotation.MicronautTest import jakarta.inject.Inject +import jakarta.transaction.Transactional import org.jooq.DSLContext import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import javax.transaction.Transactional @MicronautTest @Transactional From 7b05d5ed12439c7643421059e5759c565072c805 Mon Sep 17 00:00:00 2001 From: Simon Weis Date: Fri, 3 Nov 2023 13:12:19 +0100 Subject: [PATCH 3/3] Frontend should expect import result to be 204 --- frontend/src/api/saltAndPepper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/api/saltAndPepper.js b/frontend/src/api/saltAndPepper.js index c88dbef..2225e08 100644 --- a/frontend/src/api/saltAndPepper.js +++ b/frontend/src/api/saltAndPepper.js @@ -517,7 +517,7 @@ export const SaltAndPepper = { }, } ); - if (result.status !== 202) { + if (result.status !== 204) { throw Error(result.statusText); } }