Skip to content

Commit

Permalink
Merge pull request #1 from connorwyatt/server
Browse files Browse the repository at this point in the history
Server package
  • Loading branch information
connorwyatt authored Nov 4, 2023
2 parents 039e87d + d082eeb commit 57b7dde
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 0 deletions.
19 changes: 19 additions & 0 deletions server/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
dependencies {
implementation(project(":configuration"))
implementation(project(":eventstore"))
implementation(project(":http"))
implementation(project(":mongodb"))
implementation(project(":rabbitmq"))
implementation(project(":time"))

implementation(libraries.kodein.di)
implementation(libraries.kodein.di.framework.ktor.server)
implementation(libraries.ktor.serialization.kotlinx.json)
implementation(libraries.ktor.server.callId)
implementation(libraries.ktor.server.callLogging)
implementation(libraries.ktor.server.cio)
implementation(libraries.ktor.server.contentNegotiation)
implementation(libraries.ktor.server.core)
implementation(libraries.ktor.server.requestValidation)
implementation(libraries.ktor.server.statusPages)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package io.connorwyatt.common.server

import io.connorwyatt.common.eventstore.configuration.EventStoreConfiguration
import io.connorwyatt.common.eventstore.ktor.configureEventStore
import io.connorwyatt.common.mongodb.configuration.MongoDBConfiguration
import io.connorwyatt.common.mongodb.ktor.configureMongoDB
import io.connorwyatt.common.rabbitmq.configuration.RabbitMQConfiguration
import io.connorwyatt.common.rabbitmq.ktor.configureRabbitMQ
import io.ktor.server.application.*
import io.ktor.server.plugins.requestvalidation.*
import io.ktor.server.plugins.statuspages.*
import io.ktor.server.routing.*
import kotlinx.coroutines.runBlocking
import org.kodein.di.DI
import org.kodein.di.ktor.di

class ApplicationConfiguration(block: Builder.() -> Unit) {
val di = DI { importAll(builder.diModules) }

private val builder: Builder = Builder().apply(block)

fun applyTo(application: Application) {
application.apply {
di { extend(di) }
builder.mongoDBConfiguration?.let { runBlocking { configureMongoDB() } }
builder.eventStoreConfiguration?.let { configureEventStore(it) }
builder.rabbitMQConfiguration?.let { configureRabbitMQ(it) }
configureSerialization()
builder.configureRequestValidation?.let { configureRequestValidation(it) }
configureStatusPages(builder.configureStatusPages)
configureCallId()
configureCallLogging()
builder.configureRouting?.let { configureRouting(it) }
}
}

class Builder internal constructor() {
internal var diModules = listOf<DI.Module>()
private set

internal var eventStoreConfiguration: EventStoreConfiguration? = null
private set

internal var mongoDBConfiguration: MongoDBConfiguration? = null
private set

internal var rabbitMQConfiguration: RabbitMQConfiguration? = null
private set

internal var http: Boolean = false
private set

internal var time: Boolean = false
private set

internal var configureRequestValidation: (RequestValidationConfig.() -> Unit)? = null
private set

internal var configureStatusPages: (StatusPagesConfig.() -> Unit)? = null
private set

internal var configureRouting: (Routing.() -> Unit)? = null
private set

fun addDIModule(diModule: DI.Module) {
diModules = diModules.plus(diModule)
}

fun addEventStore(eventStoreConfiguration: EventStoreConfiguration) {
this.eventStoreConfiguration = eventStoreConfiguration
}

fun addMongoDB(mongoDBConfiguration: MongoDBConfiguration) {
this.mongoDBConfiguration = mongoDBConfiguration
}

fun addRabbitMQ(rabbitMQConfiguration: RabbitMQConfiguration) {
this.rabbitMQConfiguration = rabbitMQConfiguration
}

fun addHttp() {
this.http = true
}

fun addTime() {
this.time = true
}

fun configureRequestValidation(
configureRequestValidation: RequestValidationConfig.() -> Unit
) {
this.configureRequestValidation = configureRequestValidation
}

fun configureStatusPages(configureStatusPages: StatusPagesConfig.() -> Unit) {
this.configureStatusPages = configureStatusPages
}

fun configureRouting(configureRouting: Routing.() -> Unit) {
this.configureRouting = configureRouting
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package io.connorwyatt.common.server

import io.connorwyatt.common.http.validation.ValidationProblemResponse
import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.application.*
import io.ktor.server.plugins.callid.*
import io.ktor.server.plugins.callloging.*
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.server.plugins.requestvalidation.*
import io.ktor.server.plugins.statuspages.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import java.util.*

internal fun Application.configureSerialization() {
install(ContentNegotiation) { json() }
}

internal fun Application.configureRequestValidation(configure: RequestValidationConfig.() -> Unit) {
install(RequestValidation) { configure.invoke(this) }
}

internal fun Application.configureStatusPages(configure: (StatusPagesConfig.() -> Unit)? = null) {
install(StatusPages) {
exception<RequestValidationException> { call, cause ->
call.response.headers.append(
HttpHeaders.ContentType,
ContentType.Application.ProblemJson.toString()
)
call.respond(HttpStatusCode.BadRequest, ValidationProblemResponse(cause.reasons))
}
exception<Throwable> { call, _ ->
call.respondText("", ContentType.Any, status = HttpStatusCode.InternalServerError)
}
configure?.invoke(this)
}
}

internal fun Application.configureCallId() {
install(CallId) {
generate { UUID.randomUUID().toString() }
replyToHeader(HttpHeaders.XRequestId)
}
}

internal fun Application.configureCallLogging() {
install(CallLogging) {
callIdMdc("request-id")
disableDefaultColors()
mdc("http-method") { call -> call.request.httpMethod.value }
mdc("request-url") { call -> call.request.uri }
mdc("status-code") { call -> call.response.status()?.value?.toString() }
}
}

internal fun Application.configureRouting(configure: Routing.() -> Unit) {
routing { configure.invoke(this) }
}
15 changes: 15 additions & 0 deletions server/src/main/kotlin/io/connorwyatt/common/server/Server.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.connorwyatt.common.server

import io.ktor.server.cio.*
import io.ktor.server.engine.*

class Server(port: Int, private val applicationConfiguration: ApplicationConfiguration) {
private val applicationEngine =
embeddedServer(CIO, port = port, host = "localhost") {
applicationConfiguration.applyTo(this)
}

fun start() {
applicationEngine.start(wait = true)
}
}
12 changes: 12 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ include(":http")
include(":mongodb")
include(":optional")
include(":rabbitmq")
include(":server")
include(":time")

pluginManagement {
Expand Down Expand Up @@ -66,7 +67,18 @@ dependencyResolutionManagement {
)
library("ktor-client-cio", "io.ktor", "ktor-client-cio").version(ktorVersion)
library("ktor-client-core", "io.ktor", "ktor-client-core").version(ktorVersion)
library("ktor-serialization-kotlinx-json", "io.ktor", "ktor-serialization-kotlinx-json").version(
ktorVersion
)
library("ktor-server-callId","io.ktor", "ktor-server-call-id").version(ktorVersion)
library("ktor-server-callLogging","io.ktor", "ktor-server-call-logging").version(ktorVersion)
library("ktor-server-cio", "io.ktor", "ktor-server-cio").version(ktorVersion)
library("ktor-server-contentNegotiation", "io.ktor", "ktor-server-content-negotiation").version(
ktorVersion
)
library("ktor-server-core", "io.ktor", "ktor-server-core").version(ktorVersion)
library("ktor-server-requestValidation", "io.ktor","ktor-server-request-validation").version(ktorVersion)
library("ktor-server-statusPages", "io.ktor","ktor-server-status-pages").version(ktorVersion)
library("mongoDB-driver", "org.mongodb", "mongodb-driver-kotlin-coroutine").version(mongoDBDriverVersion)
library("rabbitMQ-client", "com.rabbitmq", "amqp-client").version(
rabbitMQClientVersion
Expand Down

0 comments on commit 57b7dde

Please sign in to comment.