diff --git a/.env.dist b/.env.dist index fe2f984..0f9db9e 100644 --- a/.env.dist +++ b/.env.dist @@ -1,10 +1,6 @@ -## can be generated with `openssl rand -base64 32` -JWT_SECRET= -# -## can be generated with `openssl rand -base64 32` +# mariadb MYSQL_ROOT_PASSWORD= -# -## can be generated with `openssl rand -base64 32` MYSQL_PASSWORD= -# password should be the same as mysql_password -PASSWORD= +# simplenotes +DB_PASSWORD= +JWT_SECRET= diff --git a/README.md b/README.md index b76d9fb..b644fc7 100644 --- a/README.md +++ b/README.md @@ -15,5 +15,4 @@ ## Configuration The app is configured with environments variables. -If no match is found within the env, a default value is read from a properties file in /app/src/main/resources/application.properties. -Don't use the default values for secrets ! Every value inside *.env.dist* should be changed. +If no match is found within the env, a default value is read from a yaml file in simplenotes-app/src/main/resources/application.yaml. diff --git a/docker-compose.yml b/docker-compose.yml index ef6f360..c3513d5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -32,13 +32,13 @@ services: - .env environment: - TZ=Europe/Brussels - - HOST=0.0.0.0 - - JDBCURL=jdbc:mariadb://db:3306/simplenotes - - DRIVERCLASSNAME=org.mariadb.jdbc.Driver - - USERNAME=simplenotes + - SERVER_HOST=0.0.0.0 + - DB_JDBC_URL=jdbc:mariadb://db:3306/simplenotes + - DB_DRIVER_CLASS_NAME=org.mariadb.jdbc.Driver + - DB_USERNAME=simplenotes # .env: # - JWT_SECRET - # - PASSWORD + # - DB_PASSWORD ports: - 127.0.0.1:8080:8080 healthcheck: diff --git a/simplenotes-app/build.gradle.kts b/simplenotes-app/build.gradle.kts index 9519be1..fe693e8 100644 --- a/simplenotes-app/build.gradle.kts +++ b/simplenotes-app/build.gradle.kts @@ -30,9 +30,6 @@ dependencies { implementation(Libs.micronaut) kapt(Libs.micronautProcessor) - testImplementation(Libs.micronaut) - kaptTest(Libs.micronautProcessor) - testImplementation(Libs.junit) testImplementation(Libs.assertJ) testImplementation(Libs.http4kTestingHamkrest) diff --git a/simplenotes-app/src/main/kotlin/be/simplenotes/app/Server.kt b/simplenotes-app/src/main/kotlin/be/simplenotes/app/Server.kt index 54e6105..0b4cb1d 100644 --- a/simplenotes-app/src/main/kotlin/be/simplenotes/app/Server.kt +++ b/simplenotes-app/src/main/kotlin/be/simplenotes/app/Server.kt @@ -1,13 +1,13 @@ package be.simplenotes.app -import io.micronaut.context.annotation.Context import org.http4k.server.Http4kServer import org.slf4j.LoggerFactory import javax.annotation.PostConstruct import javax.annotation.PreDestroy +import javax.inject.Singleton import be.simplenotes.config.ServerConfig as SimpleNotesServerConfig -@Context +@Singleton class Server( private val config: SimpleNotesServerConfig, private val http4kServer: Http4kServer, @@ -17,7 +17,7 @@ class Server( @PostConstruct fun start(): Server { http4kServer.start() - logger.info("Listening on http://${config.host}:${config.port}") + logger.info("Listening on http://${config.host}:${http4kServer.port()}") return this } diff --git a/simplenotes-app/src/main/kotlin/be/simplenotes/app/SimpleNotes.kt b/simplenotes-app/src/main/kotlin/be/simplenotes/app/SimpleNotes.kt index 8b5927a..1e03041 100644 --- a/simplenotes-app/src/main/kotlin/be/simplenotes/app/SimpleNotes.kt +++ b/simplenotes-app/src/main/kotlin/be/simplenotes/app/SimpleNotes.kt @@ -1,12 +1,10 @@ package be.simplenotes.app import io.micronaut.context.ApplicationContext +import java.lang.Runtime.getRuntime fun main() { - val ctx = ApplicationContext.run().start() - Runtime.getRuntime().addShutdownHook( - Thread { - ctx.stop() - } - ) + val ctx = ApplicationContext.run() + ctx.createBean(Server::class.java) + getRuntime().addShutdownHook(Thread { ctx.stop() }) } diff --git a/simplenotes-app/src/main/resources/logback.xml b/simplenotes-app/src/main/resources/logback.xml index a2bdf9f..13f8fa8 100644 --- a/simplenotes-app/src/main/resources/logback.xml +++ b/simplenotes-app/src/main/resources/logback.xml @@ -14,4 +14,5 @@ + diff --git a/simplenotes-config/src/main/kotlin/be/simplenotes/config/Config.kt b/simplenotes-config/src/main/kotlin/be/simplenotes/config/Config.kt index 963d4e4..75fd29b 100644 --- a/simplenotes-config/src/main/kotlin/be/simplenotes/config/Config.kt +++ b/simplenotes-config/src/main/kotlin/be/simplenotes/config/Config.kt @@ -1,8 +1,12 @@ package be.simplenotes.config +import io.micronaut.context.annotation.ConfigurationInject +import io.micronaut.context.annotation.ConfigurationProperties import java.util.concurrent.TimeUnit +import javax.inject.Singleton -data class DataSourceConfig( +@ConfigurationProperties("db") +data class DataSourceConfig @ConfigurationInject constructor( val jdbcUrl: String, val driverClassName: String, val username: String, @@ -14,7 +18,8 @@ data class DataSourceConfig( "username='$username', password='***', maximumPoolSize=$maximumPoolSize, connectionTimeout=$connectionTimeout)" } -data class JwtConfig( +@ConfigurationProperties("jwt") +data class JwtConfig @ConfigurationInject constructor( val secret: String, val validity: Long, val timeUnit: TimeUnit, @@ -22,7 +27,8 @@ data class JwtConfig( override fun toString() = "JwtConfig(secret='***', validity=$validity, timeUnit=$timeUnit)" } -data class ServerConfig( +@ConfigurationProperties("server") +data class ServerConfig @ConfigurationInject constructor( val host: String, val port: Int, ) diff --git a/simplenotes-config/src/main/kotlin/be/simplenotes/config/ConfigLoader.kt b/simplenotes-config/src/main/kotlin/be/simplenotes/config/ConfigLoader.kt deleted file mode 100644 index c8bf4b2..0000000 --- a/simplenotes-config/src/main/kotlin/be/simplenotes/config/ConfigLoader.kt +++ /dev/null @@ -1,47 +0,0 @@ -package be.simplenotes.config - -import java.util.* -import java.util.concurrent.TimeUnit -import javax.inject.Singleton - -@Singleton -class ConfigLoader { - //region Config loading - private val properties: Properties = javaClass - .getResource("/application.properties") - .openStream() - .use { - Properties().apply { load(it) } - } - - private val env = System.getenv() - - private fun value(key: String): String = - env[key.toUpperCase().replace(".", "_")] - ?: properties.getProperty(key) - ?: error("Missing config key $key") - //endregion - - val jwtConfig - get() = JwtConfig( - secret = value("jwt.secret"), - validity = value("jwt.validity").toLong(), - timeUnit = TimeUnit.HOURS, - ) - - val dataSourceConfig - get() = DataSourceConfig( - jdbcUrl = value("jdbcUrl"), - driverClassName = value("driverClassName"), - username = value("username"), - password = value("password"), - maximumPoolSize = value("maximumPoolSize").toInt(), - connectionTimeout = value("connectionTimeout").toLong() - ) - - val serverConfig - get() = ServerConfig( - host = value("host"), - port = value("port").toInt(), - ) -} diff --git a/simplenotes-config/src/main/kotlin/be/simplenotes/config/ConfigModule.kt b/simplenotes-config/src/main/kotlin/be/simplenotes/config/ConfigModule.kt deleted file mode 100644 index 64e6010..0000000 --- a/simplenotes-config/src/main/kotlin/be/simplenotes/config/ConfigModule.kt +++ /dev/null @@ -1,17 +0,0 @@ -package be.simplenotes.config - -import io.micronaut.context.annotation.Factory -import javax.inject.Singleton - -@Factory -class ConfigModule { - - @Singleton - internal fun dataSourceConfig(configLoader: ConfigLoader) = configLoader.dataSourceConfig - - @Singleton - internal fun jwtConfig(configLoader: ConfigLoader) = configLoader.jwtConfig - - @Singleton - internal fun serverConfig(configLoader: ConfigLoader) = configLoader.serverConfig -} diff --git a/simplenotes-config/src/main/resources/application.properties b/simplenotes-config/src/main/resources/application.properties deleted file mode 100644 index 9e93a26..0000000 --- a/simplenotes-config/src/main/resources/application.properties +++ /dev/null @@ -1,12 +0,0 @@ -host=localhost -port=8080 -# -jdbcUrl=jdbc:h2:./notes-db; -driverClassName=org.h2.Driver -username=h2 -password= -maximumPoolSize=10 -connectionTimeout=3000 -# -jwt.secret=PliLvfk7l4WF+cZJk66LR5Mpnh+ocbvJ2wfUCK2UCms= -jwt.validity=24 diff --git a/simplenotes-config/src/main/resources/application.yaml b/simplenotes-config/src/main/resources/application.yaml new file mode 100644 index 0000000..f68fe0c --- /dev/null +++ b/simplenotes-config/src/main/resources/application.yaml @@ -0,0 +1,16 @@ +db: + jdbc-url: jdbc:h2:./notes-db; + driver-class-name: org.h2.Driver + username: h2 + password: '' + connection-timeout: 3000 + maximum-pool-size: 10 + +jwt: + secret: 'PliLvfk7l4WF+cZJk66LR5Mpnh+ocbvJ2wfUCK2UCms=' + validity: 24 + time-unit: hours + +server: + host: localhost + port: 8080 diff --git a/simplenotes-persistance/build.gradle.kts b/simplenotes-persistance/build.gradle.kts index 6cea2fd..86cfbfe 100644 --- a/simplenotes-persistance/build.gradle.kts +++ b/simplenotes-persistance/build.gradle.kts @@ -16,16 +16,14 @@ dependencies { implementation(Libs.hikariCP) implementation(Libs.ktormCore) implementation(Libs.ktormMysql) + implementation(Libs.logbackClassic) - implementation(Libs.mapstruct) + compileOnly(Libs.mapstruct) kapt(Libs.mapstructProcessor) implementation(Libs.micronaut) kapt(Libs.micronautProcessor) - testImplementation(Libs.micronaut) - kaptTest(Libs.micronautProcessor) - testImplementation(Libs.junit) testImplementation(Libs.assertJ) testImplementation(Libs.logbackClassic) diff --git a/simplenotes-persistance/src/main/kotlin/be/simplenotes/persistance/PersistanceModule.kt b/simplenotes-persistance/src/main/kotlin/be/simplenotes/persistance/PersistanceModule.kt index 6ddc2a9..a943e1a 100644 --- a/simplenotes-persistance/src/main/kotlin/be/simplenotes/persistance/PersistanceModule.kt +++ b/simplenotes-persistance/src/main/kotlin/be/simplenotes/persistance/PersistanceModule.kt @@ -15,12 +15,6 @@ import javax.sql.DataSource @Factory class PersistanceModule { - @Singleton - internal fun noteConverter() = Mappers.getMapper(NoteConverter::class.java) - - @Singleton - internal fun userConverter() = Mappers.getMapper(UserConverter::class.java) - @Singleton internal fun database(migrations: DbMigrations, dataSource: DataSource): Database { migrations.migrate() diff --git a/simplenotes-persistance/src/main/kotlin/be/simplenotes/persistance/converters/NoteConverter.kt b/simplenotes-persistance/src/main/kotlin/be/simplenotes/persistance/converters/NoteConverter.kt index 8643b39..43508ba 100644 --- a/simplenotes-persistance/src/main/kotlin/be/simplenotes/persistance/converters/NoteConverter.kt +++ b/simplenotes-persistance/src/main/kotlin/be/simplenotes/persistance/converters/NoteConverter.kt @@ -9,12 +9,13 @@ import org.mapstruct.Mappings import org.mapstruct.ReportingPolicy import java.time.LocalDateTime import java.util.* +import javax.inject.Singleton -/** - * This is an abstract class because kotlin default methods in interface are not seen as default in kapt - * @see [KT-25960](https://youtrack.jetbrains.com/issue/KT-25960) - */ -@Mapper(uses = [NoteEntityFactory::class, UserEntityFactory::class], unmappedTargetPolicy = ReportingPolicy.IGNORE) +@Mapper( + uses = [NoteEntityFactory::class, UserEntityFactory::class], + unmappedTargetPolicy = ReportingPolicy.IGNORE, + componentModel = "jsr330" +) internal abstract class NoteConverter { fun toNote(entity: NoteEntity, tags: Tags) = @@ -80,4 +81,5 @@ internal abstract class NoteConverter { typealias Tags = List +@Singleton internal class NoteEntityFactory : Entity.Factory() diff --git a/simplenotes-persistance/src/main/kotlin/be/simplenotes/persistance/converters/UserConverter.kt b/simplenotes-persistance/src/main/kotlin/be/simplenotes/persistance/converters/UserConverter.kt index 5c12dd5..6ad64e3 100644 --- a/simplenotes-persistance/src/main/kotlin/be/simplenotes/persistance/converters/UserConverter.kt +++ b/simplenotes-persistance/src/main/kotlin/be/simplenotes/persistance/converters/UserConverter.kt @@ -6,8 +6,13 @@ import be.simplenotes.types.User import me.liuwj.ktorm.entity.Entity import org.mapstruct.Mapper import org.mapstruct.ReportingPolicy +import javax.inject.Singleton -@Mapper(uses = [UserEntityFactory::class], unmappedTargetPolicy = ReportingPolicy.IGNORE) +@Mapper( + uses = [UserEntityFactory::class], + unmappedTargetPolicy = ReportingPolicy.IGNORE, + componentModel = "jsr330" +) internal interface UserConverter { fun toUser(userEntity: UserEntity): User fun toPersistedUser(userEntity: UserEntity): PersistedUser @@ -15,4 +20,5 @@ internal interface UserConverter { fun toEntity(user: PersistedUser): UserEntity } +@Singleton internal class UserEntityFactory : Entity.Factory() diff --git a/simplenotes-persistance/src/test/kotlin/be/simplenotes/persistance/converters/NoteConverterTest.kt b/simplenotes-persistance/src/test/kotlin/be/simplenotes/persistance/converters/NoteConverterTest.kt index dee1786..72e0e94 100644 --- a/simplenotes-persistance/src/test/kotlin/be/simplenotes/persistance/converters/NoteConverterTest.kt +++ b/simplenotes-persistance/src/test/kotlin/be/simplenotes/persistance/converters/NoteConverterTest.kt @@ -2,23 +2,25 @@ package be.simplenotes.persistance.converters import be.simplenotes.persistance.notes.NoteEntity import be.simplenotes.types.* +import io.micronaut.context.BeanContext import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test -import org.mapstruct.factory.Mappers import java.time.LocalDateTime import java.util.* internal class NoteConverterTest { + private val ctx = BeanContext.run() + val converter = ctx.getBean(NoteConverter::class.java) + @Nested @DisplayName("Entity -> Models") inner class EntityToModels { @Test fun `convert NoteEntity to Note`() { - val converter = Mappers.getMapper(NoteConverter::class.java) val entity = NoteEntity { title = "title" markdown = "md" @@ -39,7 +41,6 @@ internal class NoteConverterTest { @Test fun `convert NoteEntity to ExportedNote`() { - val converter = Mappers.getMapper(NoteConverter::class.java) val entity = NoteEntity { title = "title" markdown = "md" @@ -62,7 +63,6 @@ internal class NoteConverterTest { @Test fun `convert NoteEntity to PersistedNoteMetadata`() { - val converter = Mappers.getMapper(NoteConverter::class.java) val entity = NoteEntity { uuid = UUID.randomUUID() title = "title" @@ -89,7 +89,6 @@ internal class NoteConverterTest { @Test fun `convert Note to NoteEntity`() { - val converter = Mappers.getMapper(NoteConverter::class.java) val note = Note(NoteMetadata("title", emptyList()), "md", "html") val entity = converter.toEntity(note, UUID.randomUUID(), 2, LocalDateTime.MIN) @@ -103,7 +102,6 @@ internal class NoteConverterTest { @Test fun `convert PersistedNoteMetadata to NoteEntity`() { - val converter = Mappers.getMapper(NoteConverter::class.java) val persistedNoteMetadata = PersistedNoteMetadata("title", emptyList(), LocalDateTime.MIN, UUID.randomUUID()) val entity = converter.toEntity(persistedNoteMetadata) @@ -116,7 +114,6 @@ internal class NoteConverterTest { @Test fun `convert NoteMetadata to NoteEntity`() { - val converter = Mappers.getMapper(NoteConverter::class.java) val noteMetadata = NoteMetadata("title", emptyList()) val entity = converter.toEntity(noteMetadata) @@ -126,7 +123,6 @@ internal class NoteConverterTest { @Test fun `convert PersistedNote to NoteEntity`() { - val converter = Mappers.getMapper(NoteConverter::class.java) val persistedNote = PersistedNote( NoteMetadata("title", emptyList()), markdown = "md", @@ -148,7 +144,6 @@ internal class NoteConverterTest { @Test fun `convert ExportedNote to NoteEntity`() { - val converter = Mappers.getMapper(NoteConverter::class.java) val exportedNote = ExportedNote( "title", emptyList(), diff --git a/simplenotes-persistance/src/test/kotlin/be/simplenotes/persistance/converters/UserConverterTest.kt b/simplenotes-persistance/src/test/kotlin/be/simplenotes/persistance/converters/UserConverterTest.kt index 062ac93..8e59050 100644 --- a/simplenotes-persistance/src/test/kotlin/be/simplenotes/persistance/converters/UserConverterTest.kt +++ b/simplenotes-persistance/src/test/kotlin/be/simplenotes/persistance/converters/UserConverterTest.kt @@ -3,15 +3,17 @@ package be.simplenotes.persistance.converters import be.simplenotes.persistance.users.UserEntity import be.simplenotes.types.PersistedUser import be.simplenotes.types.User +import io.micronaut.context.BeanContext import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test -import org.mapstruct.factory.Mappers internal class UserConverterTest { + private val ctx = BeanContext.run() + private val converter = ctx.getBean(UserConverter::class.java) + @Test fun `convert UserEntity to User`() { - val converter = Mappers.getMapper(UserConverter::class.java) val entity = UserEntity { username = "test" password = "test2" @@ -24,7 +26,6 @@ internal class UserConverterTest { @Test fun `convert UserEntity to PersistedUser`() { - val converter = Mappers.getMapper(UserConverter::class.java) val entity = UserEntity { username = "test" password = "test2" @@ -37,7 +38,6 @@ internal class UserConverterTest { @Test fun `convert User to UserEntity`() { - val converter = Mappers.getMapper(UserConverter::class.java) val user = User("test", "test2") val entity = converter.toEntity(user) diff --git a/simplenotes-persistance/src/test/kotlin/be/simplenotes/persistance/notes/BaseNoteRepositoryImplTest.kt b/simplenotes-persistance/src/test/kotlin/be/simplenotes/persistance/notes/BaseNoteRepositoryImplTest.kt index 1fce436..99f8473 100644 --- a/simplenotes-persistance/src/test/kotlin/be/simplenotes/persistance/notes/BaseNoteRepositoryImplTest.kt +++ b/simplenotes-persistance/src/test/kotlin/be/simplenotes/persistance/notes/BaseNoteRepositoryImplTest.kt @@ -16,7 +16,6 @@ import me.liuwj.ktorm.entity.toList import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.jupiter.api.* -import org.mapstruct.factory.Mappers import java.sql.SQLIntegrityConstraintViolationException internal abstract class BaseNoteRepositoryImplTest : DbTest() { @@ -142,7 +141,7 @@ internal abstract class BaseNoteRepositoryImplTest : DbTest() { fun `find an existing note`() { val fakeNote = noteRepo.insertFakeNote(user1) - val converter = Mappers.getMapper(NoteConverter::class.java) + val converter = beanContext.getBean(NoteConverter::class.java) val note = db.notes.find { it.title eq fakeNote.meta.title }!! .let { entity ->