Skip to content

Commit

Permalink
Merge pull request #1696 from InsertKoinIO/fix/ktor_isolated_plugin
Browse files Browse the repository at this point in the history
New ktor isolated plugin + Bump Ktor 2.3.6
  • Loading branch information
arnaudgiuliani committed Nov 9, 2023
2 parents 8d9ef16 + 073e128 commit 019fd97
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 107 deletions.
27 changes: 27 additions & 0 deletions docs/reference/koin-ktor/ktor-isolated.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
title: Ktor & Koin Isolated Context
---

The `koin-ktor` module is dedicated to bring dependency injection for Ktor.


## Isolated Koin Context Plugin

To start an Isolated Koin container in Ktor, just install the `KoinIsolated` plugin like follow:

```kotlin
fun Application.main() {
// Install Koin plugin
install(KoinIsolated) {
slf4jLogger()
modules(helloAppModule)
}
}
```

:::warning
By using an isolated Koin context you won't be able to use Koin outside of Ktor server instance (i.e: by using `GlobalContext` for example)
:::



9 changes: 2 additions & 7 deletions docs/reference/koin-ktor/ktor.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: Ktor dependency injection with Koin
title: Dependency Injection in Ktor
---

The `koin-ktor` module is dedicated to bring dependency injection for Ktor.
Expand All @@ -10,7 +10,7 @@ To start a Koin container in Ktor, just install the `Koin` plugin like follow:

```kotlin
fun Application.main() {
// Install Ktor features
// Install Koin
install(Koin) {
slf4jLogger()
modules(helloAppModule)
Expand All @@ -19,11 +19,6 @@ fun Application.main() {
}
```

:::note
Koin Ktor plugin uses isolated Koin context. You won't be able to start Koin outside of Ktor
:::


## Inject in Ktor

Koin `inject()` and `get()` functions are available from `Application`,`Route`,`Routing` classes:
Expand Down
4 changes: 2 additions & 2 deletions ktor/gradle/versions.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
ext {
// Koin
koin_ktor_version = '3.5.1'
koin_ktor_version = '3.5.2'
// Ktor
ktor_version = '2.3.3'
ktor_version = '2.3.6'
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@ import io.ktor.server.application.*
import org.koin.core.Koin
import org.koin.core.parameter.ParametersDefinition
import org.koin.core.qualifier.Qualifier
import org.koin.core.scope.Scope
import org.koin.ktor.plugin.KOIN_ATTRIBUTE_KEY
import org.koin.ktor.plugin.KOIN_KEY
import org.koin.ktor.plugin.KOIN_SCOPE_ATTRIBUTE_KEY

/**
* Ktor Koin extensions for ApplicationCall class
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright 2017-2023 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 org.koin.ktor.plugin

import io.ktor.server.application.*
import org.koin.core.KoinApplication
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)
val KoinIsolated = createApplicationPlugin(name = "Koin", createConfiguration = { KoinApplication.init() }) {
val koinApplication = setupKoinApplication()
setupMonitoring(koinApplication)
setupKoinScope(koinApplication)
koinApplication.koin.logger.info("Koin is using Ktor isolated context")
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import io.ktor.server.application.*
import io.ktor.server.application.hooks.*
import io.ktor.util.*
import org.koin.core.KoinApplication
import org.koin.core.context.startKoin
import org.koin.core.scope.Scope
import org.koin.dsl.KoinAppDeclaration

Expand All @@ -28,16 +29,17 @@ import org.koin.dsl.KoinAppDeclaration
* @author Victor Alenkov
* @author Zak Henry
*
* Ktor Feature class. Allows Koin Context to start using Ktor default install(<feature>) method.
* Ktor Feature class. Allows Koin Standard Context to start using Ktor default install(<feature>) method.
*
*/
val Koin = createApplicationPlugin(name = "Koin", createConfiguration = { KoinApplication.init() }) {
val koinApplication = setupKoinApplication()
startKoin(koinApplication)
setupMonitoring(koinApplication)
setupKoinScope(koinApplication)
}

private fun PluginBuilder<KoinApplication>.setupKoinApplication(): KoinApplication {
internal fun PluginBuilder<KoinApplication>.setupKoinApplication(): KoinApplication {
val koinApplication = pluginConfig
koinApplication.createEagerInstances()
application.setKoinApplication(koinApplication)
Expand All @@ -48,7 +50,7 @@ fun Application.setKoinApplication(koinApplication: KoinApplication){
attributes.put(KOIN_ATTRIBUTE_KEY, koinApplication)
}

private fun PluginBuilder<KoinApplication>.setupMonitoring(koinApplication: KoinApplication) {
internal fun PluginBuilder<KoinApplication>.setupMonitoring(koinApplication: KoinApplication) {
val monitor = environment?.monitor
monitor?.raise(KoinApplicationStarted, koinApplication)
monitor?.subscribe(ApplicationStopping) {
Expand All @@ -58,7 +60,7 @@ private fun PluginBuilder<KoinApplication>.setupMonitoring(koinApplication: Koin
}
}

private fun PluginBuilder<KoinApplication>.setupKoinScope(koinApplication: KoinApplication) {
internal fun PluginBuilder<KoinApplication>.setupKoinScope(koinApplication: KoinApplication) {
// Scope Handling
on(CallSetup) { call ->
val scopeComponent = RequestScope(koinApplication.koin)
Expand Down
145 changes: 55 additions & 90 deletions ktor/koin-ktor/src/test/kotlin/org/koin/ktor/ext/KoinFeatureTest.kt
Original file line number Diff line number Diff line change
@@ -1,92 +1,57 @@
//package org.koin.ktor.ext
//
//import io.ktor.application.*
//import io.ktor.server.testing.*
//import org.junit.Assert.*
//import org.junit.Ignore
//import org.junit.Test
//import org.koin.core.annotation.KoinReflectAPI
//import org.koin.core.context.GlobalContext
//import org.koin.core.context.loadKoinModules
//import org.koin.core.instance.newInstance
//import org.koin.core.parameter.emptyParametersHolder
//import org.koin.dsl.module
//import org.koin.dsl.single
//
///**
// * @author vinicius
// * @author Victor Alenkov
// *
// */
//class Foo(val name: String = "")
//class Bar(val name: String = "")
//class Bar2(val name: String = "")
//
//@OptIn(KoinReflectAPI::class)
//class KoinFeatureTest {
//
// @Test
// fun `can install feature`() {
// val module = module {
// single { Foo("bar") }
// }
// withApplication {
// application.install(Koin) {
// modules(module)
// }
// val bean = GlobalContext.get().get<Foo>()
// assertNotNull(bean)
// }
// }
//
// @Test
// fun `Koin does not contain modules`() {
// withApplication {
// assertNull(GlobalContext.getOrNull())
// assertNull(application.featureOrNull(Koin))
//
// application.install(Koin)
// assertNotNull(application.featureOrNull(Koin))
// val koin = GlobalContext.getOrNull()
// assertNotNull(koin)
// requireNotNull(koin)
//
// assertNull(koin.getOrNull<Foo>())
// }
// }
//
// @Test
// fun `add a Koin module to an already running application`() {
// withApplication {
// application.install(Koin)
// val koin = application.getKoin()
//
// assertNull(koin.getOrNull<Foo>())
//
// application.featureOrNull(Koin)?.let {
// loadKoinModules(module {
// single<Foo>()
// })
// }
// assertNotNull(koin.getOrNull<Foo>())
// }
// }
//
// @Test
// fun `Using the koin extension`() {
// withApplication {
// assertNull(GlobalContext.getOrNull())
// assertNull(application.featureOrNull(Koin))
//
// application.koin {
// modules(module {
// single<Foo>()
// })
// }
// assertNotNull(application.getKoin().getOrNull<Foo>())
// }
// }
//
package org.koin.ktor.ext

import io.ktor.server.application.*
import io.ktor.server.testing.*
import org.junit.Assert.*
import org.junit.Test
import org.koin.core.annotation.KoinReflectAPI
import org.koin.dsl.module
import org.koin.ktor.plugin.Koin
import org.koin.ktor.plugin.KoinIsolated
import org.koin.mp.KoinPlatform

/**
* @author vinicius
* @author Victor Alenkov
*
*/
class Foo(val name: String = "")
class Bar(val name: String = "")
class Bar2(val name: String = "")

@OptIn(KoinReflectAPI::class)
class KoinFeatureTest {

@Test
fun `can install feature`() {
val module = module {
single { Foo("bar") }
}
withApplication {
application.install(Koin) {
modules(module)
}
val bean = KoinPlatform.getKoin().getOrNull<Foo>()
assertNotNull(bean)
}
}

@Test
fun `can install feature - isolated context`() {
val module = module {
single { Foo("bar") }
}
withApplication {
application.install(KoinIsolated) {
modules(module)
}
val bean1 = application.get<Foo>()
assertNotNull(bean1)
val bean2 = runCatching { KoinPlatform.getKoin().getOrNull<Foo>() }.getOrNull()
assertNull(bean2)
}
}

// @Test
// fun `Using the koinModules extension`() {
// withApplication {
Expand Down Expand Up @@ -213,4 +178,4 @@
// }
// assertEquals(4, c)
// }
//}
}

0 comments on commit 019fd97

Please sign in to comment.