-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
22a7229
commit 5b7db4f
Showing
30 changed files
with
638 additions
and
2 deletions.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# BFF | ||
|
||
NITO の BFF のディレクトリです。 | ||
|
||
```shell | ||
./gradlew :bff:run | ||
``` | ||
|
||
## アーキテクチャ構成 | ||
|
||
- [Ktor](https://ktor.io/) | ||
- [GraphQL Kotlin](https://github.com/ExpediaGroup/graphql-kotlin) |
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,40 @@ | ||
import com.expediagroup.graphql.plugin.gradle.config.GraphQLSerializer | ||
|
||
plugins { | ||
kotlin("jvm") | ||
id("io.ktor.plugin") version libs.versions.ktor | ||
id("org.jetbrains.kotlin.plugin.serialization") version libs.versions.kotlin | ||
id("com.expediagroup.graphql") version libs.versions.graphqlKotlin | ||
} | ||
|
||
group = "club.nito" | ||
version = "0.1.0" | ||
|
||
application { | ||
mainClass.set("nito.club.bff.ApplicationKt") | ||
|
||
val isDevelopment: Boolean = project.ext.has("development") | ||
applicationDefaultJvmArgs = listOf("-Dio.ktor.development=$isDevelopment") | ||
} | ||
|
||
dependencies { | ||
implementation(libs.ktorServerCore) | ||
implementation(libs.ktorServerNetty) | ||
implementation(libs.ktorServerCors) | ||
implementation(libs.ktorServerWebsockets) | ||
implementation("io.ktor:ktor-serialization-kotlinx-json-jvm") | ||
implementation(libs.logback) | ||
implementation(libs.graphqlKotlinKtorServer) | ||
testImplementation("io.ktor:ktor-server-tests-jvm") | ||
testImplementation(kotlin("test")) | ||
// testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version") | ||
} | ||
|
||
graphql { | ||
schema { | ||
packages = listOf("nito.club.bff") | ||
} | ||
client { | ||
serializer = GraphQLSerializer.KOTLINX | ||
} | ||
} |
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,3 @@ | ||
package nito.club.bff | ||
|
||
fun main(args: Array<String>): Unit = io.ktor.server.netty.EngineMain.main(args) |
25 changes: 25 additions & 0 deletions
25
bff/src/main/kotlin/nito/club/bff/CustomGraphQLContextFactory.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,25 @@ | ||
package nito.club.bff | ||
|
||
import com.expediagroup.graphql.generator.extensions.plus | ||
import com.expediagroup.graphql.server.ktor.DefaultKtorGraphQLContextFactory | ||
import graphql.GraphQLContext | ||
import io.ktor.server.request.ApplicationRequest | ||
import nito.club.bff.schema.models.User | ||
|
||
class CustomGraphQLContextFactory : DefaultKtorGraphQLContextFactory() { | ||
override suspend fun generateContext(request: ApplicationRequest): GraphQLContext = | ||
super.generateContext(request).plus( | ||
mutableMapOf<Any, Any>( | ||
"user" to User( | ||
email = "[email protected]", | ||
firstName = "Someone", | ||
lastName = "You Don't know", | ||
universityId = 4, | ||
), | ||
).also { map -> | ||
request.headers["my-custom-header"]?.let { customHeader -> | ||
map["customHeader"] = customHeader | ||
} | ||
}, | ||
) | ||
} |
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,76 @@ | ||
package nito.club.bff | ||
|
||
import com.expediagroup.graphql.dataloader.KotlinDataLoaderRegistryFactory | ||
import com.expediagroup.graphql.server.ktor.GraphQL | ||
import com.expediagroup.graphql.server.ktor.graphQLGetRoute | ||
import com.expediagroup.graphql.server.ktor.graphQLPostRoute | ||
import com.expediagroup.graphql.server.ktor.graphQLSDLRoute | ||
import com.expediagroup.graphql.server.ktor.graphQLSubscriptionsRoute | ||
import com.expediagroup.graphql.server.ktor.graphiQLRoute | ||
import io.ktor.serialization.kotlinx.KotlinxWebsocketSerializationConverter | ||
import io.ktor.server.application.Application | ||
import io.ktor.server.application.install | ||
import io.ktor.server.plugins.cors.routing.CORS | ||
import io.ktor.server.routing.Routing | ||
import io.ktor.server.websocket.WebSockets | ||
import io.ktor.server.websocket.pingPeriod | ||
import kotlinx.serialization.json.Json | ||
import nito.club.bff.schema.BookQueryService | ||
import nito.club.bff.schema.CourseQueryService | ||
import nito.club.bff.schema.ExampleSubscriptionService | ||
import nito.club.bff.schema.HelloQueryService | ||
import nito.club.bff.schema.LoginMutationService | ||
import nito.club.bff.schema.UniversityQueryService | ||
import nito.club.bff.schema.dataloaders.BookDataLoader | ||
import nito.club.bff.schema.dataloaders.CourseDataLoader | ||
import nito.club.bff.schema.dataloaders.UniversityDataLoader | ||
import java.time.Duration | ||
|
||
@Suppress("unused") | ||
fun Application.graphQLModule() { | ||
val format = Json { | ||
ignoreUnknownKeys = true | ||
} | ||
install(WebSockets) { | ||
pingPeriod = Duration.ofSeconds(1) | ||
|
||
contentConverter = KotlinxWebsocketSerializationConverter(format) | ||
} | ||
install(CORS) { | ||
anyHost() | ||
} | ||
install(GraphQL) { | ||
schema { | ||
packages = listOf("nito.club.bff") | ||
queries = listOf( | ||
HelloQueryService(), | ||
BookQueryService(), | ||
CourseQueryService(), | ||
UniversityQueryService(), | ||
) | ||
mutations = listOf( | ||
LoginMutationService(), | ||
) | ||
subscriptions = listOf( | ||
ExampleSubscriptionService(), | ||
) | ||
} | ||
engine { | ||
dataLoaderRegistryFactory = KotlinDataLoaderRegistryFactory( | ||
UniversityDataLoader, | ||
CourseDataLoader, | ||
BookDataLoader, | ||
) | ||
} | ||
server { | ||
contextFactory = CustomGraphQLContextFactory() | ||
} | ||
} | ||
install(Routing) { | ||
graphQLGetRoute() | ||
graphQLPostRoute() | ||
graphQLSubscriptionsRoute() | ||
graphiQLRoute() | ||
graphQLSDLRoute() | ||
} | ||
} |
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,56 @@ | ||
package nito.club.bff | ||
|
||
import com.expediagroup.graphql.server.ktor.GraphQL | ||
import com.expediagroup.graphql.server.ktor.KtorGraphQLServer | ||
import io.ktor.http.ContentType | ||
import io.ktor.http.HttpStatusCode | ||
import io.ktor.serialization.kotlinx.serialization | ||
import io.ktor.server.application.ApplicationCall | ||
import io.ktor.server.application.call | ||
import io.ktor.server.application.install | ||
import io.ktor.server.application.plugin | ||
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation | ||
import io.ktor.server.response.respond | ||
import io.ktor.server.routing.Route | ||
import io.ktor.server.routing.application | ||
import io.ktor.server.routing.get | ||
import io.ktor.server.routing.post | ||
import kotlinx.serialization.StringFormat | ||
|
||
fun Route.graphQLGetRoute( | ||
endpoint: String = "graphql", | ||
format: StringFormat, | ||
): Route { | ||
val graphQLPlugin = this.application.plugin(GraphQL) | ||
return get(endpoint) { | ||
graphQLPlugin.server.executeRequest(call) | ||
}.apply { | ||
install(ContentNegotiation) { | ||
serialization(ContentType.Application.Json, format) | ||
} | ||
} | ||
} | ||
|
||
fun Route.graphQLPostRoute( | ||
endpoint: String = "graphql", | ||
format: StringFormat, | ||
): Route { | ||
val graphQLPlugin = this.application.plugin(GraphQL) | ||
return post(endpoint) { | ||
graphQLPlugin.server.executeRequest(call) | ||
}.apply { | ||
install(ContentNegotiation) { | ||
serialization(ContentType.Application.Json, format) | ||
} | ||
} | ||
} | ||
|
||
private suspend inline fun KtorGraphQLServer.executeRequest(call: ApplicationCall) = try { | ||
execute(call.request)?.let { | ||
call.respond(it) | ||
} ?: call.respond(HttpStatusCode.BadRequest) | ||
} catch (e: UnsupportedOperationException) { | ||
call.respond(HttpStatusCode.MethodNotAllowed) | ||
} catch (e: Exception) { | ||
call.respond(HttpStatusCode.BadRequest) | ||
} |
7 changes: 7 additions & 0 deletions
7
bff/src/main/kotlin/nito/club/bff/graphql/query/HelloQuery.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,7 @@ | ||
package nito.club.bff.graphql.query | ||
|
||
import com.expediagroup.graphql.server.operations.Query | ||
|
||
object HelloQuery : Query { | ||
fun hello() = "Hello World!" | ||
} |
6 changes: 6 additions & 0 deletions
6
bff/src/main/kotlin/nito/club/bff/graphql/server/KtorGraphQLContextFactory.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,6 @@ | ||
package nito.club.bff.graphql.server | ||
|
||
import com.expediagroup.graphql.server.execution.GraphQLContextFactory | ||
import io.ktor.server.request.ApplicationRequest | ||
|
||
class KtorGraphQLContextFactory : GraphQLContextFactory<ApplicationRequest> |
21 changes: 21 additions & 0 deletions
21
bff/src/main/kotlin/nito/club/bff/graphql/server/KtorGraphQLRequestParser.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,21 @@ | ||
package nito.club.bff.graphql.server | ||
|
||
import com.expediagroup.graphql.server.execution.GraphQLRequestParser | ||
import com.expediagroup.graphql.server.types.GraphQLServerRequest | ||
import com.fasterxml.jackson.databind.ObjectMapper | ||
import io.ktor.server.request.ApplicationRequest | ||
import io.ktor.server.request.receiveText | ||
import io.ktor.utils.io.errors.IOException | ||
|
||
class KtorGraphQLRequestParser( | ||
private val mapper: ObjectMapper, | ||
) : GraphQLRequestParser<ApplicationRequest> { | ||
override suspend fun parseRequest(request: ApplicationRequest): GraphQLServerRequest? { | ||
try { | ||
val rawRequest = request.call.receiveText() | ||
return mapper.readValue(rawRequest, GraphQLServerRequest::class.java) | ||
} catch (e: IOException) { | ||
throw IOException("Unable to parse GraphQL payload.") | ||
} | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
bff/src/main/kotlin/nito/club/bff/graphql/server/KtorGraphQLSchema.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,28 @@ | ||
package nito.club.bff.graphql.server | ||
|
||
import com.expediagroup.graphql.generator.SchemaGeneratorConfig | ||
import com.expediagroup.graphql.generator.TopLevelObject | ||
import com.expediagroup.graphql.generator.scalars.IDValueUnboxer | ||
import com.expediagroup.graphql.generator.toSchema | ||
import graphql.GraphQL | ||
import nito.club.bff.graphql.query.HelloQuery | ||
|
||
object KtorGraphQLSchema { | ||
private val config = SchemaGeneratorConfig( | ||
supportedPackages = listOf("club.nito.graphql"), | ||
) | ||
private val queries = listOf( | ||
TopLevelObject(HelloQuery), | ||
) | ||
private val mutations = listOf<TopLevelObject>() | ||
|
||
private val graphQLSchema = toSchema( | ||
config = config, | ||
queries = queries, | ||
mutations = mutations, | ||
) | ||
|
||
fun getGraphQLObject(): GraphQL = GraphQL.newGraphQL(graphQLSchema) | ||
.valueUnboxer(IDValueUnboxer()) | ||
.build() | ||
} |
34 changes: 34 additions & 0 deletions
34
bff/src/main/kotlin/nito/club/bff/graphql/server/KtorGraphQLServer.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,34 @@ | ||
package nito.club.bff.graphql.server | ||
|
||
import com.expediagroup.graphql.server.execution.GraphQLRequestHandler | ||
import com.expediagroup.graphql.server.execution.GraphQLServer | ||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper | ||
import io.ktor.http.HttpStatusCode | ||
import io.ktor.server.application.ApplicationCall | ||
import io.ktor.server.request.ApplicationRequest | ||
import io.ktor.server.response.respond | ||
|
||
object KtorGraphQLServer { | ||
private val mapper = jacksonObjectMapper() | ||
private val ktorGraphQLServer = getGraphQLServer() | ||
|
||
suspend fun handle(applicationCall: ApplicationCall) { | ||
val result = ktorGraphQLServer.execute(applicationCall.request) | ||
|
||
if (result != null) { | ||
val json = mapper.writeValueAsString(result) | ||
applicationCall.response.call.respond(json) | ||
} else { | ||
applicationCall.response.call.respond(HttpStatusCode.BadRequest, "Invalid request") | ||
} | ||
} | ||
|
||
private fun getGraphQLServer(): GraphQLServer<ApplicationRequest> { | ||
val requestParser = KtorGraphQLRequestParser(mapper) | ||
val contextFactory = KtorGraphQLContextFactory() | ||
val graphQL = KtorGraphQLSchema.getGraphQLObject() | ||
val requestHandler = GraphQLRequestHandler(graphQL) | ||
|
||
return GraphQLServer(requestParser, contextFactory, requestHandler) | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
bff/src/main/kotlin/nito/club/bff/schema/BookQueryService.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,18 @@ | ||
package nito.club.bff.schema | ||
|
||
import com.expediagroup.graphql.generator.annotations.GraphQLDescription | ||
import com.expediagroup.graphql.server.operations.Query | ||
import graphql.schema.DataFetchingEnvironment | ||
import nito.club.bff.schema.dataloaders.BookDataLoader | ||
import nito.club.bff.schema.models.Book | ||
import java.util.concurrent.CompletableFuture | ||
|
||
class BookQueryService : Query { | ||
@GraphQLDescription("Return list of books based on BookSearchParameter options") | ||
@Suppress("unused") | ||
fun searchBooks(params: BookSearchParameters, dfe: DataFetchingEnvironment): CompletableFuture<List<Book>> = | ||
dfe.getDataLoader<Int, Book>(BookDataLoader.dataLoaderName) | ||
.loadMany(params.ids) | ||
} | ||
|
||
data class BookSearchParameters(val ids: List<Int>) |
15 changes: 15 additions & 0 deletions
15
bff/src/main/kotlin/nito/club/bff/schema/CourseQueryService.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,15 @@ | ||
package nito.club.bff.schema | ||
|
||
import com.expediagroup.graphql.server.operations.Query | ||
import graphql.schema.DataFetchingEnvironment | ||
import nito.club.bff.schema.dataloaders.CourseDataLoader | ||
import nito.club.bff.schema.models.Course | ||
import java.util.concurrent.CompletableFuture | ||
|
||
class CourseQueryService : Query { | ||
fun searchCourses(params: CourseSearchParameters, dfe: DataFetchingEnvironment): CompletableFuture<List<Course>> = | ||
dfe.getDataLoader<Int, Course>(CourseDataLoader.dataLoaderName) | ||
.loadMany(params.ids) | ||
} | ||
|
||
data class CourseSearchParameters(val ids: List<Int>) |
Oops, something went wrong.