Skip to content

Commit

Permalink
add micronaut support 🚀 (#700)
Browse files Browse the repository at this point in the history
* micronaut framework implemented. (#673)

* micronaut framework implemented

* hardcoded versions fixed, comment lines removed

* Delete starters/micronaut-starter/stove-micronaut-testing-e2e directory

Signed-off-by: Anılcan <[email protected]>

* Delete examples/micronaut-example/.gitignore

Signed-off-by: Anılcan <[email protected]>

* Delete examples/micronaut-example/gradle/wrapper directory

Signed-off-by: Anılcan <[email protected]>

* fixed hardcoded lines

* fixed test name, duplicated versions, variable name

* removed packages configuration from main meth

* revert gradle properties and binaryvalidator

* fix micronaut bridge tests but it does not work yet

---------

Signed-off-by: Anılcan <[email protected]>
Signed-off-by: Oğuzhan Soykan <[email protected]>
Co-authored-by: anilcan.gul <[email protected]>
Co-authored-by: Oguzhan Soykan <[email protected]>

* works

* use different port for wiremock

* add pre-commmit hook

* bump version

* kotest arrow

* fix for api

---------

Signed-off-by: Anılcan <[email protected]>
Signed-off-by: Oğuzhan Soykan <[email protected]>
Co-authored-by: Anılcan <[email protected]>
Co-authored-by: anilcan.gul <[email protected]>
  • Loading branch information
3 people authored Feb 13, 2025
1 parent efbbc7e commit b441736
Show file tree
Hide file tree
Showing 34 changed files with 1,156 additions and 19 deletions.
15 changes: 7 additions & 8 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import org.gradle.kotlin.dsl.libs
import org.gradle.plugins.ide.idea.model.IdeaModel
import org.jetbrains.kotlin.gradle.dsl.JvmTarget

Expand All @@ -13,6 +12,7 @@ plugins {
idea
java
}

group = "com.trendyol"
version = CI.version(project)

Expand All @@ -36,14 +36,14 @@ kover {
}
}
}
val related = subprojects.of("lib", "spring", "examples", "ktor")
val related = subprojects.of("lib", "spring", "examples", "ktor", "micronaut")
dependencies {
related.forEach {
kover(it)
}
}

subprojects.of("lib", "spring", "examples", "ktor") {
subprojects.of("lib", "spring", "examples", "ktor", "micronaut") {
apply {
plugin("kotlin")
plugin(rootProject.libs.plugins.spotless.get().pluginId)
Expand All @@ -69,13 +69,12 @@ subprojects.of("lib", "spring", "examples", "ktor") {
testImplementation(libs.kotest.runner.junit5)
testImplementation(libs.kotest.framework.api)
testImplementation(libs.kotest.property)
testImplementation(libs.kotest.arrow)
detektPlugins(libs.detekt.formatting)
}

spotless {
kotlin {
ktlint(libs.versions.ktlint.get()).setEditorConfigPath(rootProject.layout.projectDirectory.file(".editorconfig"))
ktlint().setEditorConfigPath(rootProject.layout.projectDirectory.file(".editorconfig"))
targetExclude("build/", "generated/", "out/")
targetExcludeIfContentContains("generated")
targetExcludeIfContentContainsRegex("generated.*")
Expand Down Expand Up @@ -134,10 +133,11 @@ val publishedProjects = listOf(
"stove-testing-e2e-redis",
"stove-ktor-testing-e2e",
"stove-spring-testing-e2e",
"stove-spring-testing-e2e-kafka"
"stove-spring-testing-e2e-kafka",
"stove-micronaut-testing-e2e"
)

subprojects.of("lib", "spring", "ktor", filter = { p -> publishedProjects.contains(p.name) }) {
subprojects.of("lib", "spring", "ktor", "micronaut", filter = { p -> publishedProjects.contains(p.name) }) {
apply {
plugin("java")
plugin("stove-publishing")
Expand All @@ -148,4 +148,3 @@ subprojects.of("lib", "spring", "ktor", filter = { p -> publishedProjects.contai
withJavadocJar()
}
}

311 changes: 311 additions & 0 deletions examples/micronaut-example/api/micronaut-example.api

Large diffs are not rendered by default.

73 changes: 73 additions & 0 deletions examples/micronaut-example/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
plugins {
kotlin("jvm") version libs.versions.kotlin
id("org.jetbrains.kotlin.plugin.allopen") version libs.versions.kotlin
kotlin("plugin.serialization") version libs.versions.kotlin
alias(libs.plugins.google.ksp)
alias(libs.plugins.shadowJar)
alias(libs.plugins.micronaut.application)
alias(libs.plugins.micronaut.aot)
application
idea
}

dependencies {
runtimeOnly(libs.snakeyaml)
implementation(platform(libs.micronaut.parent))
implementation(libs.micronaut.kotlin.runtime)
implementation(libs.micronaut.serde.jackson)
implementation(libs.micronaut.http.client)
implementation(libs.micronaut.http.server.netty)
implementation(libs.micronaut.inject)
implementation(libs.micronaut.core)
implementation(libs.micronaut.micrometer.core)
implementation(libs.jackson.kotlin)
implementation(libs.couchbase.client.metrics)
implementation(libs.kafka)
implementation(libs.kotlinx.reactor)
implementation(libs.kotlinx.core)
implementation(libs.kotlinx.reactive)
implementation(libs.couchbase.client)
implementation(libs.couchbase.client.metrics)
implementation(libs.jackson.kotlin)
implementation(libs.kotlinx.slf4j)
}

dependencies {
testImplementation(libs.kotest.property)
testImplementation(libs.kotest.runner.junit5)
testImplementation(projects.stove.lib.stoveTestingE2eHttp)
testImplementation(projects.stove.lib.stoveTestingE2eWiremock)
testImplementation(projects.stove.lib.stoveTestingE2eCouchbase)
testImplementation(projects.stove.lib.stoveTestingE2eElasticsearch)
testImplementation(projects.stove.starters.micronaut.stoveMicronautTestingE2e)
}

application {
mainClass = "stove.micronaut.example.ApplicationKt"
}

graalvmNative.toolchainDetection = false

java {
sourceCompatibility = JavaVersion.toVersion("17")
}

micronaut {
version(libs.versions.micronaut.starter.get())
runtime("netty")
testRuntime("kotest5")
processing {
incremental(true)
annotations("stove.micronaut.example.*")
}
aot {
optimizeServiceLoading = false
convertYamlToJava = false
precomputeOperations = true
cacheEnvironment = true
optimizeClassLoading = true
deduceEnvironment = true
optimizeNetty = true
replaceLogbackXml = true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package stove.micronaut.example

import io.micronaut.context.ApplicationContext
import io.micronaut.runtime.EmbeddedApplication

fun main(args: Array<String>) {
run(args)
}

fun run(
args: Array<String>,
init: ApplicationContext.() -> Unit = {}
): ApplicationContext {
val context = ApplicationContext
.builder()
.args(*args)
.build()
.also(init)
.start()

context.findBean(EmbeddedApplication::class.java).ifPresent { app ->
if (!app.isRunning) {
app.start()
}
}

return context
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package stove.micronaut.example.application.domain

import io.micronaut.serde.annotation.Serdeable
import java.util.*

@Serdeable
data class Product(
val id: String,
val name: String,
val supplierId: Long,
val isBlacklist: Boolean,
val createdDate: Date
) {
companion object {
fun new(id: String, name: String, supplierId: Long, isBlacklist: Boolean): Product = Product(
id = id,
name = name,
supplierId = supplierId,
createdDate = Date(),
isBlacklist = isBlacklist
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package stove.micronaut.example.application.repository

import stove.micronaut.example.application.domain.Product

interface ProductRepository {
suspend fun save(product: Product): Product

suspend fun findById(id: Long): Product?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package stove.micronaut.example.application.services

import jakarta.inject.Singleton
import stove.micronaut.example.application.domain.Product
import stove.micronaut.example.application.repository.ProductRepository
import stove.micronaut.example.infrastructure.http.SupplierHttpService

@Singleton
class ProductService(
private val productRepository: ProductRepository,
private val supplierHttpService: SupplierHttpService
) {
suspend fun createProduct(id: String, productName: String, supplierId: Long): Product {
val supplier = supplierHttpService.getSupplierPermission(supplierId)
val product = Product.new(id, productName, supplierId, supplier!!.isBlacklisted)
productRepository.save(product)
return product
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package stove.micronaut.example.application.services

import io.micronaut.serde.annotation.Serdeable

@Serdeable
data class SupplierPermission(
val id: Long,
val isBlacklisted: Boolean
)

interface SupplierService {
suspend fun getSupplierPermission(supplierId: Long): SupplierPermission?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package stove.micronaut.example.infrastructure.api

import io.micronaut.http.annotation.*
import stove.micronaut.example.application.domain.Product
import stove.micronaut.example.application.services.ProductService
import stove.micronaut.example.infrastructure.api.model.request.CreateProductRequest

@Controller("/products")
class ProductController(
private val productService: ProductService
) {
@Get("/index")
fun get(
@QueryValue keyword: String = "default"
): String = "Hi from Stove framework with $keyword"

@Post("/create")
suspend fun createProduct(
@Body request: CreateProductRequest
): Product = productService.createProduct(
id = request.id,
productName = request.name,
supplierId = request.supplierId
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package stove.micronaut.example.infrastructure.api.model.request

import io.micronaut.serde.annotation.Serdeable

@Serdeable
data class CreateProductRequest(
val id: String,
val name: String,
val supplierId: Long
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package stove.micronaut.example.infrastructure.couchbase

import com.couchbase.client.java.*
import com.couchbase.client.java.Collection
import com.couchbase.client.java.codec.JacksonJsonSerializer
import com.couchbase.client.java.env.ClusterEnvironment
import com.couchbase.client.java.json.JsonValueModule
import com.fasterxml.jackson.databind.ObjectMapper
import io.micronaut.context.annotation.*
import jakarta.annotation.PreDestroy
import jakarta.inject.Singleton
import java.time.Duration

@Factory
class CouchbaseConfiguration(
private val couchbaseProperties: CouchbaseProperties
) {
companion object {
val objectMapper: ObjectMapper =
ObjectMapper()
.findAndRegisterModules()
.registerModule(JsonValueModule())
}

@Primary
@Context
fun clusterEnvironment(): ClusterEnvironment {
val cbSerializer = JacksonJsonSerializer.create(objectMapper)
return ClusterEnvironment
.builder()
.timeoutConfig {
it
.kvTimeout(Duration.ofMillis(couchbaseProperties.kvTimeout))
.connectTimeout(Duration.ofMillis(couchbaseProperties.connectTimeout))
.queryTimeout(Duration.ofMillis(couchbaseProperties.queryTimeout))
.viewTimeout(Duration.ofMillis(couchbaseProperties.viewTimeout))
}.jsonSerializer(cbSerializer)
.build()
}

@Primary
@Singleton
fun cluster(clusterEnvironment: ClusterEnvironment): Cluster {
val clusterOptions = ClusterOptions
.clusterOptions(couchbaseProperties.username, couchbaseProperties.password)
.environment(clusterEnvironment)

return Cluster.connect(couchbaseProperties.hosts.joinToString(","), clusterOptions)
}

@Primary
@Singleton
fun bucket(cluster: Cluster): Bucket = cluster.bucket(couchbaseProperties.bucketName)

@Primary
@Singleton
fun productCouchbaseCollection(bucket: Bucket): Collection = bucket.defaultCollection()

@PreDestroy
fun cleanup(cluster: Cluster, clusterEnvironment: ClusterEnvironment) {
cluster.disconnect()
clusterEnvironment.shutdown()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package stove.micronaut.example.infrastructure.couchbase

import io.micronaut.context.annotation.*

@ConfigurationProperties("couchbase")
class CouchbaseProperties {
var username: String? = null
var password: String? = null
var bucketName: String = ""
var hosts: List<String> = listOf()
var kvTimeout: Long = 0
var connectTimeout: Long = 0
var queryTimeout: Long = 0
var viewTimeout: Long = 0
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package stove.micronaut.example.infrastructure.couchbase

import com.fasterxml.jackson.annotation.JsonInclude
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.module.kotlin.KotlinModule
import io.micronaut.context.annotation.Bean
import io.micronaut.context.annotation.Factory

@Factory
class ObjectMapperConfig {
companion object {
fun createObjectMapperWithDefaults(): ObjectMapper {
val isoInstantModule = SimpleModule()
return ObjectMapper()
.registerModule(KotlinModule.Builder().build())
.registerModule(isoInstantModule)
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
}
}

@Bean
fun objectMapper(): ObjectMapper = createObjectMapperWithDefaults()
}
Loading

0 comments on commit b441736

Please sign in to comment.