Skip to content

Commit

Permalink
feat: koin di support
Browse files Browse the repository at this point in the history
Get instances from Koin DI inside routes
  • Loading branch information
programadorthi committed Aug 11, 2024
1 parent 47884bf commit 9249022
Show file tree
Hide file tree
Showing 16 changed files with 876 additions and 0 deletions.
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ serialization = "1.6.2"
slf4j = "2.0.4"
voyager = "1.1.0-alpha03"
kodein = "7.21.2"
koin = "3.5.3"

junit = "4.13.2"
robolectric = "4.11.1"
Expand Down Expand Up @@ -48,6 +49,7 @@ slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" }
voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.ref = "voyager" }
kodein-di = { module = "org.kodein.di:kodein-di", version.ref = "kodein" }
koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" }

test-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" }
test-coroutines-debug = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-debug", version.ref = "coroutines" }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/*
* Copyright Kodein the original author or authors.
* https://github.com/kosi-libs/Kodein/blob/master/framework/ktor/kodein-di-framework-ktor-server-jvm/src/main/kotlin/org/kodein/di/ktor/closest.kt
*/
package dev.programadorthi.routing.kodein

import dev.programadorthi.routing.core.Route
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/*
* Copyright Kodein the original author or authors.
* https://github.com/kosi-libs/Kodein/blob/master/framework/ktor/kodein-di-framework-ktor-server-jvm/src/main/kotlin/org/kodein/di/ktor/plugin.kt
*/
package dev.programadorthi.routing.kodein

import dev.programadorthi.routing.core.application.Application
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/*
* Copyright Kodein the original author or authors.
* https://github.com/kosi-libs/Kodein/blob/master/framework/ktor/kodein-di-framework-ktor-server-jvm/src/main/kotlin/org/kodein/di/ktor/subs.kt
*/
package dev.programadorthi.routing.kodein

import dev.programadorthi.routing.core.Route
Expand Down
25 changes: 25 additions & 0 deletions integration/koin/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
plugins {
kotlin("multiplatform")
id("org.jlleitschuh.gradle.ktlint")
id("org.jetbrains.kotlinx.kover")
alias(libs.plugins.maven.publish)
}

applyBasicSetup()

kotlin {
sourceSets {
commonMain {
dependencies {
implementation(projects.core)
implementation(libs.koin.core)
}
}

commonTest {
dependencies {
implementation(projects.statusPages)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright Koin the original author or authors.
* https://github.com/InsertKoinIO/koin/blob/main/projects/ktor/koin-ktor/src/main/kotlin/org/koin/ktor/plugin/KoinApplicationEvents.kt
*/
package dev.programadorthi.routing.koin

import io.ktor.events.EventDefinition
import org.koin.core.KoinApplication

/*
* @author Arnaud Giuliani
* @author Victor Alenkov
*/

/**
* Event definition for [KoinApplication] Started event
*/
public val KoinApplicationStarted: EventDefinition<KoinApplication> = EventDefinition()

/**
* Event definition for an event that is fired when the [KoinApplication] is going to stop
*/
public val KoinApplicationStopPreparing: EventDefinition<KoinApplication> = EventDefinition()

/**
* Event definition for [KoinApplication] Stopping event
*/
public val KoinApplicationStopped: EventDefinition<KoinApplication> = EventDefinition()
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright Koin the original author or authors.
* https://github.com/InsertKoinIO/koin/blob/main/projects/ktor/koin-ktor/src/main/kotlin/org/koin/ktor/plugin/KoinIsolatedContextPlugin.kt
*/
package dev.programadorthi.routing.koin

import dev.programadorthi.routing.core.application.ApplicationPlugin
import dev.programadorthi.routing.core.application.createApplicationPlugin
import org.koin.core.annotation.KoinInternalApi

/**
* @author Arnaud Giuliani
*
* Ktor Feature class. Allows Koin Isolatd Context to start using Ktor default install(<feature>) method.
*
*/
@OptIn(KoinInternalApi::class)
public val KoinIsolated: ApplicationPlugin<KoinConfig> =
createApplicationPlugin(name = "Koin", createConfiguration = ::KoinConfig) {
val koinApplication = setupKoinApplication()
setupMonitoring(koinApplication)
koinApplication.koin.logger.info("Koin is using Ktor isolated context")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright Koin the original author or authors.
* https://github.com/InsertKoinIO/koin/blob/main/projects/ktor/koin-ktor/src/main/kotlin/org/koin/ktor/plugin/KoinPlugin.kt
*/
package dev.programadorthi.routing.koin

import dev.programadorthi.routing.core.application.Application
import dev.programadorthi.routing.core.application.ApplicationPlugin
import dev.programadorthi.routing.core.application.ApplicationStopping
import dev.programadorthi.routing.core.application.PluginBuilder
import dev.programadorthi.routing.core.application.createApplicationPlugin
import io.ktor.util.AttributeKey
import org.koin.core.KoinApplication
import org.koin.core.context.startKoin
import org.koin.core.context.stopKoin
import org.koin.dsl.KoinAppDeclaration
import org.koin.mp.KoinPlatformTools

private const val KOIN_KEY = "KOIN"
internal val KOIN_ATTRIBUTE_KEY = AttributeKey<KoinApplication>(KOIN_KEY)

public class KoinConfig {
public var koinApplication: KoinApplication? = null

public fun setup(
koinApplication: KoinApplication? = null,
configuration: KoinAppDeclaration,
) {
val instance = koinApplication ?: KoinApplication.init()
this.koinApplication = instance
configuration(instance)
}
}

/**
* @author Arnaud Giuliani
* @author Vinicius Carvalho
* @author Victor Alenkov
* @author Zak Henry
*
* Ktor Feature class. Allows Koin Standard Context to start using Ktor default install(<feature>) method.
*
*/
public val Koin: ApplicationPlugin<KoinConfig> =
createApplicationPlugin(name = "Koin", createConfiguration = ::KoinConfig) {
val koinApplication = setupKoinApplication()
KoinPlatformTools.defaultContext().getOrNull()?.let { stopKoin() } // for ktor auto-reload
startKoin(koinApplication)
setupMonitoring(koinApplication)
}

internal fun PluginBuilder<KoinConfig>.setupKoinApplication(): KoinApplication {
val koinApplication =
checkNotNull(pluginConfig.koinApplication) {
"Koin plugin not installed"
}
koinApplication.createEagerInstances()
application.setKoinApplication(koinApplication)
return koinApplication
}

public fun Application.setKoinApplication(koinApplication: KoinApplication) {
attributes.put(KOIN_ATTRIBUTE_KEY, koinApplication)
}

internal fun PluginBuilder<KoinConfig>.setupMonitoring(koinApplication: KoinApplication) {
val monitor = environment?.monitor
monitor?.raise(KoinApplicationStarted, koinApplication)
monitor?.subscribe(ApplicationStopping) {
monitor.raise(KoinApplicationStopPreparing, koinApplication)
koinApplication.koin.close()
monitor.raise(KoinApplicationStopped, koinApplication)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright Koin the original author or authors.
* https://github.com/InsertKoinIO/koin/blob/main/projects/ktor/koin-ktor/src/main/kotlin/org/koin/ktor/ext/ApplicationCallExt.kt
*/
package dev.programadorthi.routing.koin.ext

import dev.programadorthi.routing.core.application.ApplicationCall
import org.koin.core.Koin
import org.koin.core.parameter.ParametersDefinition
import org.koin.core.qualifier.Qualifier

/*
* Ktor Koin extensions for ApplicationCall class
*
* @author Gopal Sharma
*/

/**
* inject lazily given dependency
* @param qualifier - bean name / optional
* @param parameters
*/
public inline fun <reified T : Any> ApplicationCall.inject(
qualifier: Qualifier? = null,
noinline parameters: ParametersDefinition? = null,
): Lazy<T> = lazy { get<T>(qualifier, parameters) }

/**
* Retrieve given dependency for KoinComponent
* @param qualifier - bean name / optional
* @param parameters
*/
public inline fun <reified T : Any> ApplicationCall.get(
qualifier: Qualifier? = null,
noinline parameters: ParametersDefinition? = null,
): T = getKoin().get<T>(qualifier, parameters)

/**
* Retrieve given property for KoinComponent
* @param key - key property
*/
public fun <T : Any> ApplicationCall.getProperty(key: String): T? = getKoin().getProperty(key)

/**
* Retrieve given property for KoinComponent
* give a default value if property is missing
*
* @param key - key property
* @param defaultValue - default value if property is missing
*
*/
public fun ApplicationCall.getProperty(
key: String,
defaultValue: String,
): String = getKoin().getProperty(key) ?: defaultValue

/**
* Help work on ModuleDefinition
*/
public fun ApplicationCall.getKoin(): Koin = application.getKoin()
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright Koin the original author or authors.
* https://github.com/InsertKoinIO/koin/blob/main/projects/ktor/koin-ktor/src/main/kotlin/org/koin/ktor/ext/ApplicationExt.kt
*/
package dev.programadorthi.routing.koin.ext

import dev.programadorthi.routing.core.application.Application
import dev.programadorthi.routing.core.application.install
import dev.programadorthi.routing.core.application.pluginOrNull
import dev.programadorthi.routing.koin.KOIN_ATTRIBUTE_KEY
import dev.programadorthi.routing.koin.Koin
import org.koin.core.Koin
import org.koin.core.parameter.ParametersDefinition
import org.koin.core.qualifier.Qualifier
import org.koin.dsl.KoinAppDeclaration

/*
* Ktor Koin extensions
*
* @author Arnaud Giuliani
* @author Laurent Baresse
*/

/**
* Help work on ModuleDefinition
*/
public fun Application.getKoin(): Koin =
attributes.getOrNull(KOIN_ATTRIBUTE_KEY)?.koin
?: error("No Koin instance started. Use install(Koin) or startKoin()")

/**
* inject lazily given dependency
* @param qualifier - bean name / optional
* @param scope
* @param parameters
*/
public inline fun <reified T : Any> Application.inject(
qualifier: Qualifier? = null,
noinline parameters: ParametersDefinition? = null,
): Lazy<T> = lazy { get<T>(qualifier, parameters) }

/**
* Retrieve given dependency for KoinComponent
* @param qualifier - bean name / optional
* @param scope
* @param parameters
*/
public inline fun <reified T : Any> Application.get(
qualifier: Qualifier? = null,
noinline parameters: ParametersDefinition? = null,
): T = getKoin().get<T>(qualifier, parameters)

/**
* Retrieve given property for KoinComponent
* @param key - key property
*/
public fun <T : Any> Application.getProperty(key: String): T? = getKoin().getProperty(key)

/**
* Retrieve given property for KoinComponent
* give a default value if property is missing
*
* @param key - key property
* @param defaultValue - default value if property is missing
*
*/
public fun Application.getProperty(
key: String,
defaultValue: String,
): String = getKoin().getProperty(key) ?: defaultValue

/**
* Run extra koin configuration, like modules()
*/
public fun Application.koin(configuration: KoinAppDeclaration) {
pluginOrNull(Koin)?.let {
attributes.getOrNull(KOIN_ATTRIBUTE_KEY)?.apply(configuration)
} ?: install(Koin) {
setup(koinApplication = null, configuration = configuration)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.programadorthi.routing.koin.ext

import dev.programadorthi.routing.core.application.ApplicationCall
import dev.programadorthi.routing.core.application.call
import dev.programadorthi.routing.core.application.route
import io.ktor.util.pipeline.PipelineContext
import org.koin.core.Koin
import org.koin.core.parameter.ParametersDefinition
import org.koin.core.qualifier.Qualifier

public inline fun <reified T : Any> PipelineContext<*, ApplicationCall>.inject(
qualifier: Qualifier? = null,
noinline parameters: ParametersDefinition? = null,
): Lazy<T> = lazy { get<T>(qualifier, parameters) }

public inline fun <reified T : Any> PipelineContext<*, ApplicationCall>.get(
qualifier: Qualifier? = null,
noinline parameters: ParametersDefinition? = null,
): T = getKoin().get<T>(qualifier, parameters)

public fun <T : Any> PipelineContext<*, ApplicationCall>.getProperty(key: String): T? = getKoin().getProperty(key)

public fun PipelineContext<*, ApplicationCall>.getProperty(
key: String,
defaultValue: String,
): String = getKoin().getProperty(key) ?: defaultValue

public fun PipelineContext<*, ApplicationCall>.getKoin(): Koin {
val route =
requireNotNull(route()) {
"Invalid context to get the koin instance: $call"
}
return route.getKoin()
}
Loading

0 comments on commit 9249022

Please sign in to comment.