Use new config library + refactor

This commit is contained in:
Hubert Van De Walle 2020-04-29 20:04:06 +02:00
parent cbe1a8a7b7
commit fb95413856
8 changed files with 119 additions and 68 deletions

View File

@ -18,6 +18,7 @@
<flyway_version>6.3.3</flyway_version> <flyway_version>6.3.3</flyway_version>
<javajwt_version>3.10.2</javajwt_version> <javajwt_version>3.10.2</javajwt_version>
<jbcrypt_version>0.4</jbcrypt_version> <jbcrypt_version>0.4</jbcrypt_version>
<hoplite_version>1.2.2</hoplite_version>
<kotlin.code.style>official</kotlin.code.style> <kotlin.code.style>official</kotlin.code.style>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@ -149,6 +150,16 @@
<artifactId>HikariCP</artifactId> <artifactId>HikariCP</artifactId>
<version>3.4.2</version> <version>3.4.2</version>
</dependency> </dependency>
<dependency>
<groupId>com.sksamuel.hoplite</groupId>
<artifactId>hoplite-core</artifactId>
<version>${hoplite_version}</version>
</dependency>
<dependency>
<groupId>com.sksamuel.hoplite</groupId>
<artifactId>hoplite-yaml</artifactId>
<version>${hoplite_version}</version>
</dependency>
<dependency> <dependency>
<groupId>com.github.javafaker</groupId> <groupId>com.github.javafaker</groupId>
<artifactId>javafaker</artifactId> <artifactId>javafaker</artifactId>

View File

@ -1,22 +0,0 @@
ktor {
deployment {
port = 8081
port = ${?PORT}
}
application {
modules = [ be.vandewalleh.NotesApplicationKt.module ]
}
}
database {
host = "127.0.0.1"
port = "3306"
name = "Notes"
user = "test"
password = "test"
}
jwt {
secret = "thisisasecret"
secret = ${?SECRET}
}

View File

@ -0,0 +1,22 @@
env: staging
database:
host: 127.0.0.1
port: 3306
name: Notes
username: test
password: test
server:
host: 0.0.0.0
port: 8081
jwt:
secret: ${random.string(25)}
auth:
validity: 1
unit: HOURS
refresh:
validity: 15
unit: DAYS

View File

@ -1,42 +1,64 @@
package be.vandewalleh package be.vandewalleh
import be.vandewalleh.features.configurationFeature import be.vandewalleh.features.Config
import be.vandewalleh.features.configurationModule import be.vandewalleh.features.configurationModule
import be.vandewalleh.features.features import be.vandewalleh.features.loadFeatures
import be.vandewalleh.migrations.Migration import be.vandewalleh.migrations.Migration
import be.vandewalleh.routing.registerRoutes import be.vandewalleh.routing.registerRoutes
import be.vandewalleh.services.serviceModule import be.vandewalleh.services.serviceModule
import io.ktor.application.* import io.ktor.application.*
import io.ktor.routing.* import io.ktor.routing.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import me.liuwj.ktorm.database.* import me.liuwj.ktorm.database.*
import org.kodein.di.Kodein import org.kodein.di.Kodein
import org.kodein.di.description import org.kodein.di.description
import org.kodein.di.generic.bind import org.kodein.di.generic.bind
import org.kodein.di.generic.instance import org.kodein.di.generic.instance
import org.kodein.di.generic.singleton import org.kodein.di.generic.singleton
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import javax.sql.DataSource import javax.sql.DataSource
import kotlin.system.exitProcess
lateinit var kodein: Kodein val kodein = Kodein {
import(serviceModule)
@Suppress("unused") // Referenced in application.conf import(configurationModule)
fun Application.module() { bind<Logger>() with singleton { LoggerFactory.getLogger("Application") }
// must be first to be loaded bind<Migration>() with singleton { Migration(this.kodein) }
configurationFeature() bind<Database>() with singleton { Database.connect(this.instance<DataSource>()) }
}
kodein = Kodein {
import(configurationModule)
import(serviceModule)
bind<Migration>() with singleton { Migration(this.kodein) }
bind<Database>() with singleton { Database.Companion.connect(this.instance<DataSource>()) }
}
features()
log.debug(kodein.container.tree.bindings.description())
fun main() {
val config by kodein.instance<Config>()
val logger by kodein.instance<Logger>()
logger.info("Running application with configuration $config")
val migration by kodein.instance<Migration>() val migration by kodein.instance<Migration>()
migration.migrate() migration.migrate()
serve(kodein)
}
fun serve(kodein: Kodein) {
val config by kodein.instance<Config>()
val logger by kodein.instance<Logger>()
val env = applicationEngineEnvironment {
module {
module()
}
log = logger
connector {
host = config.server.host
port = config.server.port
}
}
embeddedServer(Netty, env).start(wait = true)
}
fun Application.module() {
loadFeatures()
log.debug(kodein.container.tree.bindings.description())
routing { routing {
registerRoutes(kodein) registerRoutes(kodein)

View File

@ -1,42 +1,61 @@
package be.vandewalleh.features package be.vandewalleh.features
import be.vandewalleh.auth.SimpleJWT import be.vandewalleh.auth.SimpleJWT
import com.sksamuel.hoplite.ConfigLoader
import com.sksamuel.hoplite.Masked
import com.zaxxer.hikari.HikariConfig import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource import com.zaxxer.hikari.HikariDataSource
import io.ktor.application.*
import org.kodein.di.Kodein import org.kodein.di.Kodein
import org.kodein.di.generic.bind import org.kodein.di.generic.bind
import org.kodein.di.generic.instance import org.kodein.di.generic.instance
import org.kodein.di.generic.singleton
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.sql.DataSource import javax.sql.DataSource
/** /**
* [Kodein] configuration module * [Kodein] controller module containing the app configuration
*/ */
lateinit var configurationModule: Kodein.Module val configurationModule = Kodein.Module(name = "Configuration") {
bind() from singleton { ConfigLoader().loadConfigOrThrow<Config>("/application.yaml") }
bind<SimpleJWT>(tag = "auth") with singleton { configureAuthJwt(this.kodein) }
bind<SimpleJWT>(tag = "refresh") with singleton { configureRefreshJwt(this.kodein) }
bind<DataSource>() with singleton { configureDatasource(this.kodein) }
}
fun Application.configurationFeature() { data class DatabaseConfig(val host: String, val port: Int, val name: String, val username: String, val password: Masked)
val dataSource: DataSource = with(environment.config) { data class ServerConfig(val host: String, val port: Int)
val host = property("database.host").getString() data class JwtConfig(val secret: Masked, val auth: JwtValidity, val refresh: JwtValidity)
val port = property("database.port").getString() data class JwtValidity(val validity: Long, val unit: TimeUnit)
val name = property("database.name").getString() data class Config(val database: DatabaseConfig, val server: ServerConfig, val jwt: JwtConfig)
val hikariConfig = HikariConfig().apply {
jdbcUrl = "jdbc:mariadb://$host:$port/$name"
username = this@with.property("database.user").getString()
password = this@with.property("database.password").getString()
}
HikariDataSource(hikariConfig) private fun configureAuthJwt(kodein: Kodein): SimpleJWT {
val config by kodein.instance<Config>()
val jwtSecret = config.jwt.secret
val authConfig = config.jwt.auth
return SimpleJWT(jwtSecret.value, authConfig.validity, authConfig.unit)
}
private fun configureRefreshJwt(kodein: Kodein): SimpleJWT {
val config by kodein.instance<Config>()
val jwtSecret = config.jwt.secret
val refreshConfig = config.jwt.auth
return SimpleJWT(jwtSecret.value, refreshConfig.validity, refreshConfig.unit)
}
private fun configureDatasource(kodein: Kodein): DataSource {
val config by kodein.instance<Config>()
val dbConfig = config.database
val host = dbConfig.host
val port = dbConfig.port
val name = dbConfig.name
val hikariConfig = HikariConfig().apply {
jdbcUrl = "jdbc:mariadb://$host:$port/$name"
username = dbConfig.username
password = dbConfig.password.value
} }
val jwtSecret = environment.config.property("jwt.secret").getString() return HikariDataSource(hikariConfig)
val authSimpleJwt = SimpleJWT(jwtSecret, 1, TimeUnit.HOURS)
val refreshSimpleJwt = SimpleJWT(jwtSecret, 7, TimeUnit.DAYS)
configurationModule = Kodein.Module("Configuration") {
bind<DataSource>() with instance(dataSource)
bind<SimpleJWT>(tag = "auth") with instance(authSimpleJwt)
bind<SimpleJWT>(tag = "refresh") with instance(refreshSimpleJwt)
}
} }

View File

@ -1,6 +1,5 @@
package be.vandewalleh.features package be.vandewalleh.features
import com.fasterxml.jackson.databind.SerializationFeature
import io.ktor.application.* import io.ktor.application.*
import io.ktor.features.* import io.ktor.features.*
import io.ktor.jackson.* import io.ktor.jackson.*

View File

@ -8,7 +8,7 @@ import io.ktor.utils.io.errors.*
fun Application.handleErrors() { fun Application.handleErrors() {
install(StatusPages) { install(StatusPages) {
exception<IOException> { _ -> exception<IOException> {
call.respond(HttpStatusCode.BadRequest) call.respond(HttpStatusCode.BadRequest)
} }
} }

View File

@ -3,7 +3,7 @@ package be.vandewalleh.features
import be.vandewalleh.auth.authenticationModule import be.vandewalleh.auth.authenticationModule
import io.ktor.application.* import io.ktor.application.*
fun Application.features() { fun Application.loadFeatures() {
corsFeature() corsFeature()
contentNegotiationFeature() contentNegotiationFeature()
authenticationModule() authenticationModule()