-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Get instances from Kodein DI inside routes
- Loading branch information
1 parent
4ec0afb
commit 47884bf
Showing
10 changed files
with
493 additions
and
1 deletion.
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
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 |
---|---|---|
@@ -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.kodein.di) | ||
} | ||
} | ||
|
||
commonTest { | ||
dependencies { | ||
implementation(projects.statusPages) | ||
} | ||
} | ||
} | ||
} |
67 changes: 67 additions & 0 deletions
67
integration/kodein/common/src/dev/programadorthi/routing/kodein/ClosestDI.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,67 @@ | ||
package dev.programadorthi.routing.kodein | ||
|
||
import dev.programadorthi.routing.core.Route | ||
import dev.programadorthi.routing.core.Routing | ||
import dev.programadorthi.routing.core.application | ||
import dev.programadorthi.routing.core.application.Application | ||
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.AttributeKey | ||
import io.ktor.util.pipeline.PipelineContext | ||
import org.kodein.di.DI | ||
import org.kodein.di.LazyDI | ||
|
||
// attribute key for storing injector in a call | ||
public val KodeinDIKey: AttributeKey<DI> = AttributeKey<DI>("KodeinDI") | ||
|
||
/** | ||
* Getting the global [DI] container from the [Application] | ||
*/ | ||
public fun Application.closestDI(): LazyDI = LazyDI { attributes[KodeinDIKey] } | ||
|
||
/** | ||
* Getting the global [DI] container from the [Application] parameter | ||
*/ | ||
public fun closestDI(getApplication: () -> Application): LazyDI = getApplication().closestDI() | ||
|
||
/** | ||
* Getting the global [DI] container from the [ApplicationCall] | ||
*/ | ||
public fun ApplicationCall.closestDI(): LazyDI = closestDI { application } | ||
|
||
/** | ||
* Getting the global [DI] container from the [Routing] feature | ||
*/ | ||
public fun Routing.closestDI(): LazyDI = closestDI { application } | ||
|
||
/** | ||
* Getting the global or local (if extended) [DI] container from the current [Route] | ||
* by browsering all the routing tree until we get to the root level, the [Routing] feature | ||
* | ||
* @throws IllegalStateException if there is no [DI] container | ||
*/ | ||
public fun Route.closestDI(): LazyDI { | ||
val attrs = | ||
when { | ||
this is Routing -> application.attributes | ||
else -> attributes | ||
} | ||
// Is there an inner DI container for this Route ? | ||
val routeDI = attrs.getOrNull(KodeinDIKey) | ||
if (routeDI is LazyDI) { | ||
return routeDI | ||
} | ||
return parent?.closestDI() ?: error("No DI container found for [$this]") | ||
} | ||
|
||
/** | ||
* Getting the global [DI] container from the [ApplicationCall] | ||
*/ | ||
public fun PipelineContext<*, ApplicationCall>.closestDI(): LazyDI { | ||
val route = | ||
requireNotNull(route()) { | ||
"Invalid context to get the closestDI: $call" | ||
} | ||
return route.closestDI() | ||
} |
45 changes: 45 additions & 0 deletions
45
integration/kodein/common/src/dev/programadorthi/routing/kodein/DIPlugin.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,45 @@ | ||
package dev.programadorthi.routing.kodein | ||
|
||
import dev.programadorthi.routing.core.application.Application | ||
import dev.programadorthi.routing.core.application.ApplicationCallPipeline | ||
import dev.programadorthi.routing.core.application.BaseApplicationPlugin | ||
import dev.programadorthi.routing.core.application.install | ||
import dev.programadorthi.routing.kodein.DIPlugin.Plugin | ||
import io.ktor.util.AttributeKey | ||
import org.kodein.di.DI | ||
import org.kodein.di.bindInstance | ||
|
||
/** | ||
* Ktor [Plugin] that provide a global [DI] container | ||
* that would be accessible from everywhere in the Ktor application | ||
*/ | ||
public class DIPlugin { | ||
/** | ||
* Configure the [DI] container then put it in the [Application.attributes], | ||
* thus it would be easily accessible (e.g. [Application.di] | ||
*/ | ||
internal companion object Plugin : | ||
BaseApplicationPlugin<ApplicationCallPipeline, DI.MainBuilder, DIPlugin> { | ||
override val key: AttributeKey<DIPlugin> = AttributeKey("DIPlugin") | ||
|
||
override fun install( | ||
pipeline: ApplicationCallPipeline, | ||
configure: DI.MainBuilder.() -> Unit, | ||
): DIPlugin { | ||
pipeline.attributes.put(KodeinDIKey, DI.lazy { configure() }) | ||
|
||
return DIPlugin() | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Installs a [DIPlugin] feature for the this [Application] and runs a [configuration] script on it | ||
* | ||
* @throws [dev.programadorthi.routing.core.application.DuplicatePluginException] if the plugin has already been installed. | ||
*/ | ||
public fun Application.di(configuration: DI.MainBuilder.() -> Unit): DIPlugin = | ||
install(DIPlugin) { | ||
bindInstance { this@di } | ||
configuration() | ||
} |
26 changes: 26 additions & 0 deletions
26
integration/kodein/common/src/dev/programadorthi/routing/kodein/SubDI.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,26 @@ | ||
package dev.programadorthi.routing.kodein | ||
|
||
import dev.programadorthi.routing.core.Route | ||
import dev.programadorthi.routing.core.Routing | ||
import dev.programadorthi.routing.core.application | ||
import org.kodein.di.Copy | ||
import org.kodein.di.DI | ||
import org.kodein.di.subDI | ||
|
||
/** | ||
* Extend the nearest [DI] container, Global (from the Application) or Local (from a parent) | ||
*/ | ||
public inline fun Route.subDI( | ||
allowSilentOverride: Boolean = false, | ||
copy: Copy = Copy.NonCached, | ||
crossinline init: DI.MainBuilder.() -> Unit, | ||
) { | ||
// Get any DI container in the parent # avoid infinite loop / StackOverflowError | ||
val parentDI = parent?.closestDI() ?: closestDI { application } | ||
val attrs = | ||
when { | ||
this is Routing -> application.attributes | ||
else -> attributes | ||
} | ||
attrs.put(KodeinDIKey, subDI(parentDI, allowSilentOverride, copy, init)) | ||
} |
Oops, something went wrong.