-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Enrich Ktor example with stanalone kafka and and proper configuration…
… handling (#409)
- Loading branch information
Showing
44 changed files
with
906 additions
and
428 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,42 +1,47 @@ | ||
@file:Suppress("UnstableApiUsage", "DSL_SCOPE_VIOLATION") | ||
|
||
plugins { | ||
kotlin("jvm") version libs.versions.kotlin | ||
application | ||
idea | ||
kotlin("plugin.serialization") version libs.versions.kotlin | ||
kotlin("jvm") version libs.versions.kotlin | ||
application | ||
idea | ||
kotlin("plugin.serialization") version libs.versions.kotlin | ||
} | ||
|
||
application { | ||
val groupId = rootProject.group.toString() | ||
val artifactId = project.name | ||
mainClass.set("$groupId.$artifactId.ApplicationKt") | ||
val groupId = rootProject.group.toString() | ||
val artifactId = project.name | ||
mainClass.set("$groupId.$artifactId.ApplicationKt") | ||
|
||
val isDevelopment: Boolean = project.ext.has("development") | ||
applicationDefaultJvmArgs = listOf("-Dio.ktor.development=$isDevelopment") | ||
val isDevelopment: Boolean = project.ext.has("development") | ||
applicationDefaultJvmArgs = listOf("-Dio.ktor.development=$isDevelopment") | ||
} | ||
|
||
dependencies { | ||
implementation(libs.ktor.server) | ||
implementation(libs.ktor.server.netty) | ||
implementation(libs.ktor.serialization.kotlinx.json) | ||
implementation(libs.ktor.server.call.logging) | ||
implementation(libs.koin.ktor) | ||
implementation(libs.koin.logger.slf4j) | ||
implementation(libs.kotlinx.reactor) | ||
implementation(libs.r2dbc.postgresql) | ||
implementation(libs.ktor.server) | ||
implementation(libs.ktor.server.netty) | ||
implementation(libs.ktor.serialization.kotlinx.json) | ||
implementation(libs.ktor.server.call.logging) | ||
implementation(libs.koin.ktor) | ||
implementation(libs.koin.logger.slf4j) | ||
implementation(libs.kotlinx.reactor) | ||
implementation(libs.r2dbc.postgresql) | ||
implementation(libs.kafkaKotlin) | ||
implementation(libs.hoplite.yaml) | ||
implementation(libs.jackson.kotlin) | ||
implementation(libs.jackson.databind) | ||
} | ||
|
||
dependencies { | ||
testImplementation(testLibs.ktor.server.tests.jvm) | ||
testImplementation(testLibs.kotest.property.jvm) | ||
testImplementation(testLibs.kotest.runner.junit5) | ||
testImplementation(project(":lib:stove-testing-e2e-http")) | ||
testImplementation(project(":lib:stove-testing-e2e-wiremock")) | ||
testImplementation(project(":lib:stove-testing-e2e-rdbms-postgres")) | ||
testImplementation(project(":starters:ktor:stove-ktor-testing-e2e")) | ||
testImplementation(testLibs.ktor.server.tests.jvm) | ||
testImplementation(testLibs.kotest.property.jvm) | ||
testImplementation(testLibs.kotest.runner.junit5) | ||
testImplementation(projects.stove.lib.stoveTestingE2eHttp) | ||
testImplementation(projects.stove.lib.stoveTestingE2eWiremock) | ||
testImplementation(projects.stove.lib.stoveTestingE2eRdbmsPostgres) | ||
testImplementation(projects.stove.lib.stoveTestingE2eKafka) | ||
testImplementation(projects.stove.starters.ktor.stoveKtorTestingE2e) | ||
} | ||
|
||
repositories { | ||
mavenCentral() | ||
mavenCentral() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
7 changes: 6 additions & 1 deletion
7
examples/ktor-example/src/main/kotlin/stove/ktor/example/app/app.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,16 @@ | ||
package stove.ktor.example.app | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper | ||
import org.koin.dsl.* | ||
import stove.ktor.example.application.* | ||
import stove.ktor.example.domain.ProductRepository | ||
|
||
val objectMapperRef: ObjectMapper = ObjectMapper().apply { | ||
findAndRegisterModules() | ||
} | ||
|
||
fun app() = module { | ||
single { ProductRepository(get()) } | ||
single { ProductService(get(), get()) } | ||
single { ProductService(get(), get(), get()) } | ||
single { MutexLockProvider() }.bind<LockProvider>() | ||
} |
88 changes: 88 additions & 0 deletions
88
examples/ktor-example/src/main/kotlin/stove/ktor/example/app/configuration.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
package stove.ktor.example.app | ||
|
||
import com.sksamuel.hoplite.* | ||
import com.sksamuel.hoplite.env.Environment | ||
|
||
@OptIn(ExperimentalHoplite::class) | ||
inline fun <reified T : Any> loadConfiguration(args: Array<String> = arrayOf()): T = ConfigLoaderBuilder.default() | ||
.addEnvironmentSource() | ||
.addCommandLineSource(args) | ||
.withExplicitSealedTypes() | ||
.withEnvironment(AppEnv.toEnv()) | ||
.apply { | ||
when (AppEnv.current()) { | ||
AppEnv.Local -> { | ||
addResourceSource("/application.yaml", optional = true) | ||
} | ||
|
||
AppEnv.Prod -> { | ||
addResourceSource("/application-prod.yaml", optional = true) | ||
addResourceSource("/application.yaml", optional = true) | ||
} | ||
|
||
else -> { | ||
addResourceSource("/application.yaml", optional = true) | ||
} | ||
} | ||
} | ||
.build() | ||
.loadConfigOrThrow<T>() | ||
|
||
data class AppConfiguration( | ||
val port: Int, | ||
val database: DatabaseConfiguration, | ||
val kafka: KafkaConfiguration | ||
) | ||
|
||
data class DatabaseConfiguration( | ||
val host: String, | ||
val port: Int, | ||
val name: String, | ||
val jdbcUrl: String, | ||
val username: String, | ||
val password: String | ||
) | ||
|
||
data class KafkaConfiguration( | ||
val bootstrapServers: String, | ||
val groupId: String, | ||
val clientId: String, | ||
val interceptorClasses: List<String>, | ||
val topics: Map<String, TopicConfiguration> | ||
) | ||
|
||
data class TopicConfiguration( | ||
val topic: String, | ||
val retry: String, | ||
val error: String | ||
) | ||
|
||
enum class AppEnv(val env: String) { | ||
Unspecified(""), | ||
Local(Environment.local.name), | ||
Prod(Environment.prod.name) | ||
; | ||
|
||
companion object { | ||
fun current(): AppEnv = when (System.getenv("ENVIRONMENT")) { | ||
Unspecified.env -> Unspecified | ||
Local.env -> Local | ||
Prod.env -> Prod | ||
else -> Local | ||
} | ||
|
||
fun toEnv(): Environment = when (current()) { | ||
Local -> Environment.local | ||
Prod -> Environment.prod | ||
else -> Environment.local | ||
} | ||
} | ||
|
||
fun isLocal(): Boolean { | ||
return this === Local | ||
} | ||
|
||
fun isProd(): Boolean { | ||
return this === Prod | ||
} | ||
} |
15 changes: 8 additions & 7 deletions
15
examples/ktor-example/src/main/kotlin/stove/ktor/example/app/database.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
62 changes: 62 additions & 0 deletions
62
examples/ktor-example/src/main/kotlin/stove/ktor/example/app/kafka.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package stove.ktor.example.app | ||
|
||
import com.fasterxml.jackson.module.kotlin.readValue | ||
import io.github.nomisRev.kafka.publisher.* | ||
import io.github.nomisRev.kafka.receiver.* | ||
import org.apache.kafka.clients.consumer.ConsumerConfig | ||
import org.apache.kafka.clients.producer.ProducerConfig | ||
import org.apache.kafka.common.serialization.* | ||
import org.koin.core.module.Module | ||
import org.koin.dsl.module | ||
import stove.ktor.example.application.ExampleAppConsumer | ||
import java.util.* | ||
|
||
fun kafka(): Module = module { | ||
single { createReceiver<Any>(get()) } | ||
single { createPublisher(get()) } | ||
single { ExampleAppConsumer<String, Any>(get(), get()) } | ||
} | ||
|
||
private fun <V : Any> createReceiver(config: AppConfiguration): KafkaReceiver<String, V> { | ||
val settings = ReceiverSettings( | ||
config.kafka.bootstrapServers, | ||
StringDeserializer(), | ||
ExampleAppKafkaValueDeserializer<V>(), | ||
config.kafka.groupId, | ||
autoOffsetReset = AutoOffsetReset.Earliest, | ||
properties = Properties().apply { | ||
put(ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, config.kafka.interceptorClasses) | ||
put(ConsumerConfig.CLIENT_ID_CONFIG, config.kafka.clientId) | ||
put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, true) | ||
put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "1000") | ||
put(ConsumerConfig.ALLOW_AUTO_CREATE_TOPICS_CONFIG, true) | ||
put(ConsumerConfig.HEARTBEAT_INTERVAL_MS_CONFIG, "2000") | ||
} | ||
) | ||
return KafkaReceiver(settings) | ||
} | ||
|
||
private fun createPublisher(config: AppConfiguration): KafkaPublisher<String, Any> = PublisherSettings( | ||
config.kafka.bootstrapServers, | ||
StringSerializer(), | ||
ExampleAppKafkaValueSerializer(), | ||
properties = Properties().apply { | ||
put(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, config.kafka.interceptorClasses) | ||
put(ProducerConfig.CLIENT_ID_CONFIG, config.kafka.clientId) | ||
} | ||
).let { KafkaPublisher(it) } | ||
|
||
@Suppress("UNCHECKED_CAST") | ||
class ExampleAppKafkaValueDeserializer<T : Any> : Deserializer<T> { | ||
override fun deserialize( | ||
topic: String, | ||
data: ByteArray | ||
): T = objectMapperRef.readValue<Any>(data) as T | ||
} | ||
|
||
class ExampleAppKafkaValueSerializer<T : Any> : Serializer<T> { | ||
override fun serialize( | ||
topic: String, | ||
data: T | ||
): ByteArray = objectMapperRef.writeValueAsBytes(data) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.