Skip to content

[executions] KotlinDataLoader to provide DataLoader #1462

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ package com.expediagroup.graphql.examples.server.ktor.schema.dataloaders
import com.expediagroup.graphql.examples.server.ktor.schema.models.Book
import com.expediagroup.graphql.dataloader.KotlinDataLoader
import kotlinx.coroutines.runBlocking
import org.dataloader.BatchLoader
import org.dataloader.DataLoaderFactory
import java.util.concurrent.CompletableFuture

val BookDataLoader = object : KotlinDataLoader<List<Int>, List<Book>> {
override val dataLoaderName = "BATCH_BOOK_LOADER"
override fun getBatchLoader() = BatchLoader<List<Int>, List<Book>> { ids ->
override fun getDataLoader() = DataLoaderFactory.newDataLoader<List<Int>, List<Book>> { ids ->
CompletableFuture.supplyAsync {
val allBooks = runBlocking { Book.search(ids.flatten()).toMutableList() }
// produce lists of results from returned books
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ package com.expediagroup.graphql.examples.server.ktor.schema.dataloaders
import com.expediagroup.graphql.examples.server.ktor.schema.models.Course
import com.expediagroup.graphql.dataloader.KotlinDataLoader
import kotlinx.coroutines.runBlocking
import org.dataloader.BatchLoader
import org.dataloader.DataLoaderFactory
import java.util.concurrent.CompletableFuture

val CourseDataLoader = object : KotlinDataLoader<Int, Course?> {
override val dataLoaderName = "COURSE_LOADER"
override fun getBatchLoader() = BatchLoader<Int, Course?> { ids ->
override fun getDataLoader() = DataLoaderFactory.newDataLoader<Int, Course?> { ids ->
CompletableFuture.supplyAsync {
runBlocking { Course.search(ids).toMutableList() }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,14 @@ package com.expediagroup.graphql.examples.server.ktor.schema.dataloaders
import com.expediagroup.graphql.examples.server.ktor.schema.models.University
import com.expediagroup.graphql.dataloader.KotlinDataLoader
import kotlinx.coroutines.runBlocking
import org.dataloader.BatchLoader
import org.dataloader.DataLoaderFactory
import java.util.concurrent.CompletableFuture

val UniversityDataLoader = object : KotlinDataLoader<Int, University?> {
override val dataLoaderName = "UNIVERSITY_LOADER"
override fun getBatchLoader(): BatchLoader<Int, University?> =
BatchLoader<Int, University?> { ids ->
CompletableFuture.supplyAsync {
runBlocking { University.search(ids).toMutableList() }
}
override fun getDataLoader() = DataLoaderFactory.newDataLoader<Int, University?> { ids ->
CompletableFuture.supplyAsync {
runBlocking { University.search(ids).toMutableList() }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ package com.expediagroup.graphql.examples.server.spring.dataloaders

import com.expediagroup.graphql.examples.server.spring.model.Company
import com.expediagroup.graphql.dataloader.KotlinDataLoader
import org.dataloader.BatchLoader
import org.dataloader.DataLoaderFactory
import org.springframework.stereotype.Component
import java.util.concurrent.CompletableFuture

Expand All @@ -29,7 +29,7 @@ class CompanyDataLoader(private val service: CompanyService) : KotlinDataLoader<
}

override val dataLoaderName = name
override fun getBatchLoader() = BatchLoader<Int, Company> { ids ->
override fun getDataLoader() = DataLoaderFactory.newDataLoader<Int, Company> { ids ->
CompletableFuture.supplyAsync { service.getCompanies(ids) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ val reactorExtensionsVersion: String by project

dependencies {
api(project(path = ":graphql-kotlin-dataloader"))
api("com.graphql-java:graphql-java:$graphQLJavaVersion")
api("com.graphql-java:graphql-java:$graphQLJavaVersion") {
exclude(group = "com.graphql-java", module = "java-dataloader")
}
testImplementation("io.projectreactor.kotlin:reactor-kotlin-extensions:$reactorExtensionsVersion")
testImplementation("io.projectreactor:reactor-core:$reactorVersion")
testImplementation("org.junit.jupiter:junit-jupiter-api:$junitVersion")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ import com.expediagroup.graphql.dataloader.instrumentation.fixture.domain.Planet
import com.expediagroup.graphql.dataloader.instrumentation.fixture.extensions.toListOfNullables
import com.expediagroup.graphql.dataloader.instrumentation.fixture.repository.AstronautRepository
import graphql.schema.DataFetchingEnvironment
import org.dataloader.BatchLoader
import org.dataloader.DataLoader
import org.dataloader.DataLoaderFactory
import org.dataloader.DataLoaderOptions
import org.dataloader.stats.SimpleStatisticsCollector
import java.util.Optional
Expand All @@ -34,15 +35,17 @@ data class AstronautServiceRequest(val id: Int)

class AstronautDataLoader : KotlinDataLoader<AstronautServiceRequest, Astronaut?> {
override val dataLoaderName: String = "AstronautDataLoader"
override fun getOptions(): DataLoaderOptions = DataLoaderOptions.newOptions().setStatisticsCollector { SimpleStatisticsCollector() }
override fun getBatchLoader(): BatchLoader<AstronautServiceRequest, Astronaut?> =
BatchLoader<AstronautServiceRequest, Astronaut?> { keys ->
AstronautRepository
.getAstronauts(keys.map(AstronautServiceRequest::id))
.collectList()
.map(List<Optional<Astronaut>>::toListOfNullables)
.toFuture()
}
override fun getDataLoader(): DataLoader<AstronautServiceRequest, Astronaut?> =
DataLoaderFactory.newDataLoader(
{ keys ->
AstronautRepository
.getAstronauts(keys.map(AstronautServiceRequest::id))
.collectList()
.map(List<Optional<Astronaut>>::toListOfNullables)
.toFuture()
},
DataLoaderOptions.newOptions().setStatisticsCollector(::SimpleStatisticsCollector)
)
}

class AstronautService {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import com.expediagroup.graphql.dataloader.instrumentation.fixture.domain.Missio
import com.expediagroup.graphql.dataloader.instrumentation.fixture.extensions.toListOfNullables
import com.expediagroup.graphql.dataloader.instrumentation.fixture.repository.MissionRepository
import graphql.schema.DataFetchingEnvironment
import org.dataloader.BatchLoader
import org.dataloader.DataLoader
import org.dataloader.DataLoaderFactory
import org.dataloader.DataLoaderOptions
import org.dataloader.stats.SimpleStatisticsCollector
import java.util.Optional
Expand All @@ -31,26 +32,28 @@ data class MissionServiceRequest(val id: Int, val astronautId: Int = -1)

class MissionDataLoader : KotlinDataLoader<MissionServiceRequest, Mission?> {
override val dataLoaderName: String = "MissionDataLoader"
override fun getOptions(): DataLoaderOptions = DataLoaderOptions.newOptions().setStatisticsCollector { SimpleStatisticsCollector() }
override fun getBatchLoader(): BatchLoader<MissionServiceRequest, Mission?> =
BatchLoader<MissionServiceRequest, Mission?> { keys ->
override fun getDataLoader(): DataLoader<MissionServiceRequest, Mission?> = DataLoaderFactory.newDataLoader(
{ keys ->
MissionRepository
.getMissions(keys.map(MissionServiceRequest::id))
.collectList()
.map(List<Optional<Mission>>::toListOfNullables)
.toFuture()
}
},
DataLoaderOptions.newOptions().setStatisticsCollector(::SimpleStatisticsCollector)
)
}

class MissionsByAstronautDataLoader : KotlinDataLoader<MissionServiceRequest, List<Mission>> {
override val dataLoaderName: String = "MissionsByAstronautDataLoader"
override fun getOptions(): DataLoaderOptions = DataLoaderOptions.newOptions().setStatisticsCollector { SimpleStatisticsCollector() }
override fun getBatchLoader(): BatchLoader<MissionServiceRequest, List<Mission>> =
BatchLoader<MissionServiceRequest, List<Mission>> { keys ->
override fun getDataLoader(): DataLoader<MissionServiceRequest, List<Mission>> = DataLoaderFactory.newDataLoader(
{ keys ->
MissionRepository
.getMissionsByAstronautIds(keys.map(MissionServiceRequest::astronautId))
.collectList().toFuture()
}
},
DataLoaderOptions.newOptions().setStatisticsCollector(::SimpleStatisticsCollector)
)
}

class MissionService {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ import com.expediagroup.graphql.dataloader.KotlinDataLoader
import com.expediagroup.graphql.dataloader.instrumentation.fixture.domain.Planet
import com.expediagroup.graphql.dataloader.instrumentation.fixture.repository.PlanetRepository
import graphql.schema.DataFetchingEnvironment
import org.dataloader.BatchLoader
import org.dataloader.DataLoader
import org.dataloader.DataLoaderFactory
import org.dataloader.DataLoaderOptions
import org.dataloader.stats.SimpleStatisticsCollector
import java.util.concurrent.CompletableFuture
Expand All @@ -29,14 +30,15 @@ data class PlanetServiceRequest(val id: Int, val missionId: Int = -1)

class PlanetsByMissionDataLoader : KotlinDataLoader<PlanetServiceRequest, List<Planet>> {
override val dataLoaderName: String = "PlanetsByMissionDataLoader"
override fun getOptions(): DataLoaderOptions = DataLoaderOptions.newOptions().setStatisticsCollector { SimpleStatisticsCollector() }
override fun getBatchLoader(): BatchLoader<PlanetServiceRequest, List<Planet>> =
BatchLoader<PlanetServiceRequest, List<Planet>> { keys ->
override fun getDataLoader(): DataLoader<PlanetServiceRequest, List<Planet>> = DataLoaderFactory.newDataLoader(
{ keys ->
PlanetRepository
.getPlanetsByMissionIds(keys.map(PlanetServiceRequest::missionId))
.collectList()
.toFuture()
}
},
DataLoaderOptions.newOptions().setStatisticsCollector(::SimpleStatisticsCollector)
)
}

class PlanetService {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,25 @@ import com.expediagroup.graphql.dataloader.instrumentation.fixture.domain.Produc
import com.expediagroup.graphql.dataloader.instrumentation.fixture.extensions.toListOfNullables
import com.expediagroup.graphql.dataloader.instrumentation.fixture.repository.ProductRepository
import graphql.schema.DataFetchingEnvironment
import org.dataloader.BatchLoader
import org.dataloader.DataLoader
import org.dataloader.DataLoaderFactory
import org.dataloader.DataLoaderOptions
import org.dataloader.stats.SimpleStatisticsCollector
import java.util.Optional
import java.util.concurrent.CompletableFuture

class ProductDataLoader : KotlinDataLoader<ProductServiceRequest, Product?> {
override val dataLoaderName: String = "ProductDataLoader"
override fun getOptions(): DataLoaderOptions = DataLoaderOptions.newOptions().setStatisticsCollector { SimpleStatisticsCollector() }
override fun getBatchLoader(): BatchLoader<ProductServiceRequest, Product?> =
BatchLoader<ProductServiceRequest, Product?> { requests ->
override fun getDataLoader(): DataLoader<ProductServiceRequest, Product?> = DataLoaderFactory.newDataLoader(
{ requests ->
ProductRepository
.getProducts(requests)
.collectList()
.map(List<Optional<Product>>::toListOfNullables)
.toFuture()
}
},
DataLoaderOptions.newOptions().setStatisticsCollector(::SimpleStatisticsCollector)
)
}

data class ProductServiceRequest(val id: Int, val fields: List<String>)
Expand Down
17 changes: 9 additions & 8 deletions executions/graphql-kotlin-dataloader/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ To help in the registration of `DataLoaders`, we have created a basic interface
```kotlin
interface KotlinDataLoader<K, V> {
val dataLoaderName: String
fun getBatchLoader(): BatchLoader<K, V>
fun getOptions(): DataLoaderOptions = DataLoaderOptions.newOptions()
fun getDataLoader(): DataLoader<K, V>
}
```

Expand All @@ -29,12 +28,14 @@ and its various configuration options.
```kotlin
class UserDataLoader : KotlinDataLoader<ID, User> {
override val dataLoaderName = "UserDataLoader"
override fun getBatchLoader() = BatchLoader<ID, User> { ids ->
CompletableFuture.supplyAsync {
ids.map { id -> userService.getUser(id) }
}
}
override fun getOptions() = DataLoaderOptions.newOptions().setCachingEnabled(false)
override fun getDataLoader() = DataLoaderFactory.newDataLoader<ID, User>(
{ ids ->
CompletableFuture.supplyAsync {
ids.map { id -> userService.getUser(id) }
}
},
DataLoaderOptions.newOptions().setCachingEnabled(false)
)
}
```

Expand Down
4 changes: 2 additions & 2 deletions executions/graphql-kotlin-dataloader/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ tasks {
limit {
counter = "INSTRUCTION"
value = "COVEREDRATIO"
minimum = "0.55".toBigDecimal()
minimum = "0.53".toBigDecimal()
}
limit {
counter = "BRANCH"
value = "COVEREDRATIO"
minimum = "0.55".toBigDecimal()
minimum = "0.53".toBigDecimal()
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,13 @@

package com.expediagroup.graphql.dataloader

import org.dataloader.BatchLoader
import org.dataloader.DataLoader
import org.dataloader.DataLoaderOptions

/**
* Configuration interface that will create a [DataLoader] instance,
* so we can have common logic around registering the data loaders
* Wrapper around the [DataLoader] class so we can have common logic around registering the loaders
* by return type and loading values in the data fetchers.
*/
interface KotlinDataLoader<K, V> {
val dataLoaderName: String
fun getBatchLoader(): BatchLoader<K, V>
fun getOptions(): DataLoaderOptions = DataLoaderOptions.newOptions()
fun getDataLoader(): DataLoader<K, V>
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

package com.expediagroup.graphql.dataloader

import org.dataloader.DataLoaderFactory
import org.dataloader.DataLoaderRegistry

/**
Expand All @@ -36,10 +35,7 @@ class KotlinDataLoaderRegistryFactory(
dataLoaders.forEach { dataLoader ->
registry.register(
dataLoader.dataLoaderName,
DataLoaderFactory.newDataLoader(
dataLoader.getBatchLoader(),
dataLoader.getOptions()
)
dataLoader.getDataLoader()
)
}
return KotlinDataLoaderRegistry(registry)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
package com.expediagroup.graphql.dataloader

import io.mockk.mockk
import org.dataloader.BatchLoader
import org.dataloader.DataLoader
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
Expand All @@ -39,7 +39,7 @@ class KotlinDataLoaderRegistryFactoryTest {
fun `generate registry with basic loader`() {
val mockLoader: KotlinDataLoader<String, String> = object : KotlinDataLoader<String, String> {
override val dataLoaderName: String = "MockDataLoader"
override fun getBatchLoader(): BatchLoader<String, String> = mockk()
override fun getDataLoader(): DataLoader<String, String> = mockk()
}

val registry = KotlinDataLoaderRegistryFactory(listOf(mockLoader)).generate()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@

package com.expediagroup.graphql.dataloader

import org.dataloader.BatchLoader
import org.dataloader.DataLoader
import org.dataloader.DataLoaderFactory
import org.junit.jupiter.api.Test
import reactor.kotlin.core.publisher.toFlux
import java.time.Duration
Expand All @@ -29,14 +30,14 @@ class KotlinDataLoaderRegistryTest {
fun `Decorator will keep track of DataLoaders futures`() {
val stringToUpperCaseDataLoader: KotlinDataLoader<String, String> = object : KotlinDataLoader<String, String> {
override val dataLoaderName: String = "ToUppercaseDataLoader"
override fun getBatchLoader(): BatchLoader<String, String> = BatchLoader<String, String> { keys ->
override fun getDataLoader(): DataLoader<String, String> = DataLoaderFactory.newDataLoader { keys ->
keys.toFlux().map(String::uppercase).collectList().delayElement(Duration.ofMillis(300)).toFuture()
}
}

val stringToLowerCaseDataLoader: KotlinDataLoader<String, String> = object : KotlinDataLoader<String, String> {
override val dataLoaderName: String = "ToLowercaseDataLoader"
override fun getBatchLoader(): BatchLoader<String, String> = BatchLoader<String, String> { keys ->
override fun getDataLoader(): DataLoader<String, String> = DataLoaderFactory.newDataLoader { keys ->
keys.toFlux().map(String::lowercase).collectList().delayElement(Duration.ofMillis(300)).toFuture()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ import graphql.schema.GraphQLSchema
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.runBlocking
import org.dataloader.BatchLoader
import org.dataloader.DataLoader
import org.dataloader.DataLoaderFactory
import org.junit.jupiter.api.Test
import java.util.concurrent.CompletableFuture
import kotlin.random.Random
Expand All @@ -62,7 +63,7 @@ class GraphQLRequestHandlerTest {
KotlinDataLoaderRegistryFactory(
object : KotlinDataLoader<Int, User> {
override val dataLoaderName: String = "UserDataLoader"
override fun getBatchLoader(): BatchLoader<Int, User> = BatchLoader {
override fun getDataLoader(): DataLoader<Int, User> = DataLoaderFactory.newDataLoader { _ ->
CompletableFuture.completedFuture(
listOf(
User(1, "John Doe"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import com.expediagroup.graphql.dataloader.KotlinDataLoader
import com.expediagroup.graphql.dataloader.KotlinDataLoaderRegistryFactory
import com.expediagroup.graphql.server.types.GraphQLRequest
import io.mockk.mockk
import org.dataloader.BatchLoader
import org.dataloader.DataLoader
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
Expand Down Expand Up @@ -64,7 +64,7 @@ class RequestExtensionsKtTest {
val dataLoaderRegistry = KotlinDataLoaderRegistryFactory(
object : KotlinDataLoader<String, String> {
override val dataLoaderName: String = "abc"
override fun getBatchLoader(): BatchLoader<String, String> = mockk()
override fun getDataLoader(): DataLoader<String, String> = mockk()
}
).generate()

Expand Down
Loading