Is there an example somewhere how parameter inject...
# koin
j
Is there an example somewhere how parameter injection for a class while at the same time an interface (that is implemented by the class o.c.) is bound to the bean. I have the class
Copy code
class DokumentMemoryPersistenceAdapter
    (entries: List<Dokument>) : LoadDokumentPort, SaveDokumentPort
and the module
Copy code
val persistenceKoinTestModule = module(createdAtStart = true) {

    single {
        (entries: List<Dokument>) ->
        DokumentMemoryPersistenceAdapter(entries = get { parametersOf(entries) })
    } withOptions {
        bind<LoadDokumentPort>()
        bind<SaveDokumentPort>()
    }
}
In my test I do the following
Copy code
@BeforeEach
    fun beforeEach() {
        println("BeforeEach")
        val entries = emptyList<Dokument>()
        val persistenceAdapter: DokumentMemoryPersistenceAdapter by inject { parametersOf(entries) }
    }

    @Test
    fun loadDokumentOkTest() {
        val loadDokumentPort by inject<LoadDokumentPort>()
but as a result I get a message "could not create instance for DokumentMemoryPersistenceAdapter" with a root cause
Copy code
Caused by: org.koin.core.error.NoParameterFoundException: Can't get injected parameter #0 from DefinitionParameters[] for type 'java.util.List'
	at app//org.koin.core.parameter.ParametersHolder.elementAt(ParametersHolder.kt:41)
	at app//org.codeshards.aktenordner.adapter.outgoing.persistence.PersistenceKoinTestModuleKt$persistenceKoinTestModule$1$1.invoke(PersistenceKoinTestModule.kt:22)
	at app//org.codeshards.aktenordner.adapter.outgoing.persistence.PersistenceKoinTestModuleKt$persistenceKoinTestModule$1$1.invoke(PersistenceKoinTestModule.kt:14)
	at app//org.koin.core.instance.InstanceFactory.create(InstanceFactory.kt:51)
	... 81 more
Lines 14 - 22 of my module happen to be the singleton declaration. I also tried to supply the parameter for the injected interfaces along the following sample
Copy code
val saveDokumentPort: SaveDokumentPort by inject { parametersOf(entries) }
and got the same results. A similar test without a bind option works
Copy code
class ListPresenter(val a: List<Dokument>)

val presenterModule = module {
    single {
            (a: List<Dokument>) -> ListPresenter(a = get { parametersOf(a) })
    }
}
...
    @Test
    fun listTest() {
        val a = emptyList<Dokument>()
        val listPresenter: ListPresenter by inject { parametersOf(a) }
so I guess the bind option is the problem. Any feedback would be appreciated.
ł
hey, I don’t know exactly what you want to achieve. I don’t know if this answer will be what you expect. On the other hand it may be helpful, you can do something like this:
Copy code
single<LoadDokumentPort> { (entries: List<Dokument>) ->
        DokumentMemoryPersistenceAdapter(entries = get { parametersOf(entries) })
    }

    single<SaveDokumentPort> { (entries: List<Dokument>) ->
        DokumentMemoryPersistenceAdapter(entries = get { parametersOf(entries) })
    }
and then
Copy code
val port1: LoadDokumentPort by inject { parametersOf(c1) }
        val port2: SaveDokumentPort by inject { parametersOf(c2) }
j
Hi Łukasz, thanks a lot for your feedback. I'll give it a try this evening. As to what I'm trying to do: in general I'm trying to see how the approach lined out in this book can be implemented with Kotlin, Ktor and Koin. In particular I'm trying to implement a repository for persisting stuff in my application. The general idea is: • split the repository interface into independent declarations so any callers only depend on things they actually need • provide a singleton that implements all interfaces (see footnote) • have a way to provide data to the singleton when it is created, e.g. to provide certain entries on startup Footnote: The repository I'm working on right now is intended as a dummy in tests. Other repository implementations might have different implementations for some of the interfaces, e.g. stuff that is persisted in a database vs. blobs persisted in the file system.
ł
@Jerry Preissler ok, I understand. Then you can do something like this. I wrote without a compiler so quickly more or less then it would look like this:
Copy code
interface LoadDokumentPortUseCase {
    fun load(): File
}

interface SaveDokumentPortUseCase {

    fun save(file: File) //with result or not?

    fun save(json: Bleble)
}

class LoadDokumentPortImpl(
    db: MySuperDatabase // or ktor:KtorClient or other
) : LoadDokumentPortUseCase {
    override fun load(): File { // File or String or sth else
        ///db.load...
    }
}

class SaveDokumentPortImpl(
    db: MySuperDatabase // or ktor:KtorClient or other
) : SaveDokumentPortUseCase {

    override fun save(file: File) {
    }

    override fun save(json: Bleble) {
    }
}

class DokumentMemoryPersistenceAdapterImpl(
    load: LoadDokumentPortUseCase,
    save: SaveDokumentPortUseCase
) : DokumentMemoryPersistenceAdapter {
    override fun load(entries: List<String>) {
        load.load...
    }

    override fun save(entries: List<String>) {
        save.save...
    }
}

interface DokumentMemoryPersistenceAdapter {

    fun load(entries: List<String>)

    fun save(entries: List<String>)
}

val modules = module {
    single<KtorClient> {KtorClientImpl(...)}
    single<MySuperDatabase> { MySuperDatabaseImpl(...) }

    factory<LoadDokumentPortUseCase> { LoadDokumentPortUseCaseImpl(get()) }
    factory<SaveDokumentPortUseCase> { SaveDokumentPortImpl(get()) }

    single<DokumentMemoryPersistenceAdapter> {DokumentMemoryPersistenceAdapterImpl(get(),get())}
}

val persistenceAdapter: DokumentMemoryPersistenceAdapter by inject

//depends on your architecture on view model or sth else
persistenceAdapter.load
persistenceAdapter.save
You would also have to consider whether or not the adapter in this case should be a singleton. Same situation is with ktor or database
j
Just in case someone tries something similar in the future: I was thinking too complex - if I simply declare the parameters in the Koin module I can get what I want. The following works for me: My module definition
Copy code
val presentId =  AggregateId.fromString("e58ed763-928c-4155-bee9-fdbaaadc15f3")
val entry = Dokument(presentId)

val persistenceKoinTestModule = module {
    single<DummyDokumentPersistenceAdapter> { DummyDokumentPersistenceAdapter(listOf<Dokument>(entry)) } withOptions {
        bind<LoadDokumentPort>()
    }
}
and the test case:
Copy code
class DummyDokumentPersistenceAdapter(): LoadDokumentPort {
    private val dokumente: MutableMap<AggregateId, Dokument> = mutableMapOf()

    constructor(entries: List<Dokument>): this() {
   ....
    }
....
}

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class KoinExploration : KoinTest {

    @JvmField
    @RegisterExtension
    @Suppress("unused")
    val koinTestExtension = KoinTestExtension.create {
        modules(
            persistenceKoinTestModule
        )
    }

    val presentId = org.codeshards.aktenordner.adapter.outgoing.persistence.presentId
    @Test
    fun listTest() {
        val adapter: LoadDokumentPort by inject()
        when (val result = adapter.loadDokument(presentId)) {
            is OperationResult.SUCCESS ->
                assertEquals(entry.id, result.value.id)
            is OperationResult.ERROR ->
                fail(result.message, result.cause)
        }
    }
}
This does what I need for now. I'll revisit setting the parameters directly from the test code once I have a better understanding of the Koin DSL. Thanks a lot for your help.
361 Views