diff --git a/.github/workflows/default-ci.yml b/.github/workflows/default-ci.yml index 3ecf5ea..882c0b2 100644 --- a/.github/workflows/default-ci.yml +++ b/.github/workflows/default-ci.yml @@ -11,7 +11,7 @@ jobs: - uses: actions/setup-java@v2 with: distribution: 'zulu' - java-version: '8' + java-version: '17' - name: Build with Gradle run: ./gradlew :server:build @@ -25,6 +25,6 @@ jobs: - uses: actions/setup-java@v2 with: distribution: 'zulu' - java-version: '8' + java-version: '17' - name: Run server tests run: ./gradlew :server:test diff --git a/.github/workflows/on-release.yml b/.github/workflows/on-release.yml index 0d6f36b..c750b8a 100644 --- a/.github/workflows/on-release.yml +++ b/.github/workflows/on-release.yml @@ -11,7 +11,7 @@ jobs: - uses: actions/setup-java@v2 with: distribution: 'zulu' - java-version: '8' + java-version: '17' - name: Build with Gradle run: ./gradlew :server:build @@ -25,7 +25,7 @@ jobs: - uses: actions/setup-java@v2 with: distribution: 'zulu' - java-version: '8' + java-version: '17' - name: Run server tests run: ./gradlew :server:test @@ -39,7 +39,7 @@ jobs: - uses: actions/setup-java@v2 with: distribution: 'zulu' - java-version: '8' + java-version: '17' - name: Prepare environment env: GRADLE_PROPERTIES: ${{secrets.GRADLE_PROPERTIES}} @@ -58,7 +58,7 @@ jobs: - uses: actions/setup-java@v2 with: distribution: 'zulu' - java-version: '8' + java-version: '17' - name: Prepare environment env: GRADLE_PROPERTIES: ${{secrets.GRADLE_PROPERTIES}} diff --git a/build.gradle.kts b/build.gradle.kts index 99a5747..19b5bd5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,14 +1,14 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { - kotlin("jvm") version "1.5.30" + kotlin("jvm") version "1.8.20" signing `maven-publish` id("io.github.gradle-nexus.publish-plugin") version "1.0.0" } group = "com.arianegraphql" -version = "0.0.6" +version = "0.1.0" repositories { mavenCentral() @@ -17,9 +17,9 @@ repositories { subprojects { tasks.withType { kotlinOptions { - jvmTarget = "1.8" - languageVersion = "1.5" - apiVersion = "1.5" + jvmTarget = "17" + languageVersion = "1.8" + apiVersion = "1.8" } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7c08e4f..1b298ae 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists \ No newline at end of file diff --git a/sample/src/main/kotlin/com/arianegraphql/sample/main.kt b/sample/src/main/kotlin/com/arianegraphql/sample/main.kt index 5461403..9b8c864 100644 --- a/sample/src/main/kotlin/com/arianegraphql/sample/main.kt +++ b/sample/src/main/kotlin/com/arianegraphql/sample/main.kt @@ -12,6 +12,7 @@ import com.arianegraphql.server.listener.ServerListener import com.arianegraphql.server.listener.SubscriptionListener import com.arianegraphql.server.request.HttpRequest import graphql.ExecutionInput +import graphql.GraphQLContext import io.ktor.application.* import io.ktor.features.* import io.ktor.server.engine.* @@ -74,40 +75,40 @@ fun main() { println("[$sessionId] onNewConnection()") } - override fun onConnected(sessionId: String, context: Any?) { + override fun onConnected(sessionId: String, context: GraphQLContext) { println("[$sessionId] onConnected()") } - override fun onStartSubscription(sessionId: String, context: Any?, operationId: String, graphQLRequest: GraphQLRequest) { + override fun onStartSubscription(sessionId: String, context: GraphQLContext, operationId: String, graphQLRequest: GraphQLRequest) { println("[$sessionId] onStartSubscription($operationId, ${graphQLRequest.operationName})") } - override fun onStopSubscription(sessionId: String, context: Any?, operationId: String) { + override fun onStopSubscription(sessionId: String, context: GraphQLContext, operationId: String) { println("[$sessionId] onStopSubscription($operationId)") } - override fun onCloseConnection(sessionId: String, context: Any?) { + override fun onCloseConnection(sessionId: String, context: GraphQLContext) { println("[$sessionId] onCloseConnection()") } } resolvers { Query { - resolve("movies") { _, _: Any?, _: Any?, _ -> + resolve("movies") { _, _: Any?, _: GraphQLContext, _ -> movies } - resolve("directors") { _, _: Any?, _: Any?, _ -> + resolve("directors") { _, _: Any?, _: GraphQLContext, _ -> movies.map { it.director }.distinctBy { it.name } } - resolve("movie") { args, _: Any?, _: Any?, _ -> + resolve("movie") { args, _: Any?, _: GraphQLContext, _ -> movies.find { it.title == args["title"] } } } Mutation { - resolve("addMovie") { args, parent: Any?, context: Any?, info -> + resolve("addMovie") { args, parent: Any?, context: GraphQLContext, info -> val movie = Movie(args["title"], Director(args["director"])) movies.add(movie) @@ -117,7 +118,7 @@ fun main() { } type("Director") { - resolve("movies") { args: Any, parent: Director, context: Any?, info -> + resolve("movies") { args: Any, parent: Director, context: GraphQLContext, info -> movies.filter { it.director.name == parent.name } } } diff --git a/server-ktor/build.gradle.kts b/server-ktor/build.gradle.kts index e7050e0..416a37c 100644 --- a/server-ktor/build.gradle.kts +++ b/server-ktor/build.gradle.kts @@ -1,11 +1,12 @@ group = project.rootProject.group version = project.rootProject.version -val ktorVersion = "1.6.3" +val ktorVersion = "2.3.4" dependencies { implementation(project(":server")) implementation("io.ktor:ktor-server-cio:$ktorVersion") - implementation("io.ktor:ktor-websockets:$ktorVersion") - implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.12.5") + implementation("io.ktor:ktor-server-websockets:$ktorVersion") + implementation("io.ktor:ktor-server-cors:$ktorVersion") + implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.15.2") } diff --git a/server-ktor/src/main/kotlin/com/arianegraphql/server/ktor/dsl/ArianeKtorServerBuilder.kt b/server-ktor/src/main/kotlin/com/arianegraphql/server/ktor/dsl/ArianeKtorServerBuilder.kt index 973e16e..f5449dd 100644 --- a/server-ktor/src/main/kotlin/com/arianegraphql/server/ktor/dsl/ArianeKtorServerBuilder.kt +++ b/server-ktor/src/main/kotlin/com/arianegraphql/server/ktor/dsl/ArianeKtorServerBuilder.kt @@ -4,8 +4,8 @@ import com.arianegraphql.ktx.GraphQLSchemaDslMarker import com.arianegraphql.ktx.makeExecutableSchema import com.arianegraphql.server.dsl.ArianeServerBuilder import graphql.GraphQL -import io.ktor.application.* import java.lang.IllegalStateException +import io.ktor.server.application.Application @GraphQLSchemaDslMarker class ArianeKtorServerBuilder : ArianeServerBuilder() { diff --git a/server-ktor/src/main/kotlin/com/arianegraphql/server/ktor/dsl/ArianeKtorServerConfiguration.kt b/server-ktor/src/main/kotlin/com/arianegraphql/server/ktor/dsl/ArianeKtorServerConfiguration.kt index 371e477..413a298 100644 --- a/server-ktor/src/main/kotlin/com/arianegraphql/server/ktor/dsl/ArianeKtorServerConfiguration.kt +++ b/server-ktor/src/main/kotlin/com/arianegraphql/server/ktor/dsl/ArianeKtorServerConfiguration.kt @@ -14,7 +14,7 @@ class ArianeKtorServerConfiguration( path: String, isPlaygroundEnabled: Boolean, enableCORS: Boolean, - contextResolver: ContextResolver<*>, + contextResolver: ContextResolver, serverListener: ServerListener?, requestListener: RequestListener?, subscriptionListener: SubscriptionListener?, diff --git a/server-ktor/src/main/kotlin/com/arianegraphql/server/ktor/dsl/KtorPlugin.kt b/server-ktor/src/main/kotlin/com/arianegraphql/server/ktor/dsl/KtorPlugin.kt index 91faf94..5cc346c 100644 --- a/server-ktor/src/main/kotlin/com/arianegraphql/server/ktor/dsl/KtorPlugin.kt +++ b/server-ktor/src/main/kotlin/com/arianegraphql/server/ktor/dsl/KtorPlugin.kt @@ -1,6 +1,6 @@ package com.arianegraphql.server.ktor.dsl -import io.ktor.application.* +import io.ktor.server.application.Application interface KtorPlugin{ fun invoke(application: Application) diff --git a/server-ktor/src/main/kotlin/com/arianegraphql/server/ktor/handleGraphQLRequest.kt b/server-ktor/src/main/kotlin/com/arianegraphql/server/ktor/handleGraphQLRequest.kt index 567e419..1dd8510 100644 --- a/server-ktor/src/main/kotlin/com/arianegraphql/server/ktor/handleGraphQLRequest.kt +++ b/server-ktor/src/main/kotlin/com/arianegraphql/server/ktor/handleGraphQLRequest.kt @@ -2,10 +2,10 @@ package com.arianegraphql.server.ktor import com.arianegraphql.server.ArianeServer import com.arianegraphql.server.request.HttpRequest -import io.ktor.application.* +import io.ktor.server.application.* import io.ktor.http.* -import io.ktor.request.* -import io.ktor.response.* +import io.ktor.server.request.* +import io.ktor.server.response.* import io.ktor.util.* import io.ktor.util.pipeline.* diff --git a/server-ktor/src/main/kotlin/com/arianegraphql/server/ktor/handleGraphQLSubscription.kt b/server-ktor/src/main/kotlin/com/arianegraphql/server/ktor/handleGraphQLSubscription.kt index 665b9cb..b2ba68a 100644 --- a/server-ktor/src/main/kotlin/com/arianegraphql/server/ktor/handleGraphQLSubscription.kt +++ b/server-ktor/src/main/kotlin/com/arianegraphql/server/ktor/handleGraphQLSubscription.kt @@ -2,12 +2,10 @@ package com.arianegraphql.server.ktor import com.arianegraphql.server.ArianeServer import com.arianegraphql.server.request.WebSocketRequest -import io.ktor.http.cio.websocket.* +import io.ktor.server.websocket.* import io.ktor.util.* import io.ktor.websocket.* -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch -import java.lang.Exception import java.util.* suspend fun WebSocketServerSession.handleGraphQLSubscription(arianeServer: ArianeServer) { diff --git a/server-ktor/src/main/kotlin/com/arianegraphql/server/ktor/launcher.kt b/server-ktor/src/main/kotlin/com/arianegraphql/server/ktor/launcher.kt index 7a5a67e..9e639a6 100644 --- a/server-ktor/src/main/kotlin/com/arianegraphql/server/ktor/launcher.kt +++ b/server-ktor/src/main/kotlin/com/arianegraphql/server/ktor/launcher.kt @@ -1,16 +1,15 @@ package com.arianegraphql.server.ktor -import com.arianegraphql.server.config.ArianeServerConfiguration import com.arianegraphql.server.config.newArianeServer import com.arianegraphql.server.dsl.ArianeServerBuilder import com.arianegraphql.server.ktor.dsl.ArianeKtorServerConfiguration import com.arianegraphql.server.ktor.dsl.arianeServer -import io.ktor.application.* -import io.ktor.features.* -import io.ktor.routing.* import io.ktor.server.cio.* import io.ktor.server.engine.* -import io.ktor.websocket.* +import io.ktor.server.application.* +import io.ktor.server.routing.* +import io.ktor.server.websocket.* +import io.ktor.server.plugins.cors.routing.CORS fun ArianeKtorServerConfiguration.launch(wait: Boolean = true) { val arianeServer = newArianeServer(JacksonSerializer) diff --git a/server/build.gradle.kts b/server/build.gradle.kts index 25be669..f7d07c0 100644 --- a/server/build.gradle.kts +++ b/server/build.gradle.kts @@ -2,10 +2,10 @@ group = project.rootProject.group version = project.rootProject.version dependencies { - api("com.graphql-java:graphql-java:17.2") - api("org.slf4j:slf4j-api:1.7.32") - api( "ch.qos.logback:logback-classic:1.2.5") + api("com.graphql-java:graphql-java:21.1") + api("org.slf4j:slf4j-api:2.0.9") + api("ch.qos.logback:logback-classic:1.4.11") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactive:1.5.1") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactive:1.7.3") } \ No newline at end of file diff --git a/server/src/main/kotlin/com/arianegraphql/ktx/Resolver.kt b/server/src/main/kotlin/com/arianegraphql/ktx/Resolver.kt index 19035c9..2c6d92a 100644 --- a/server/src/main/kotlin/com/arianegraphql/ktx/Resolver.kt +++ b/server/src/main/kotlin/com/arianegraphql/ktx/Resolver.kt @@ -1,26 +1,28 @@ package com.arianegraphql.ktx +import graphql.GraphQLContext import graphql.schema.DataFetcher import kotlinx.coroutines.runBlocking -interface Resolver { - suspend fun resolve(arguments: Argument, source: S, context: C?, info: Info): Any? +interface Resolver { + suspend fun resolve(arguments: Argument, source: S, context: GraphQLContext, info: Info): Any? } -@JvmInline value class FunctionalResolver( - private val lambda: suspend (arguments: Argument, source: S, context: C?, info: Info) -> Any? -) : Resolver { +@JvmInline value class FunctionalResolver( + private val lambda: suspend (arguments: Argument, source: S, context: GraphQLContext, info: Info) -> Any? +) : Resolver { override suspend fun resolve( arguments: Argument, source: S, - context: C?, + context: GraphQLContext, info: Info ) = lambda(arguments, source, context, info) } -internal fun Resolver.toDataFetcher(): DataFetcher = DataFetcher { env -> +internal fun Resolver.toDataFetcher(): DataFetcher = DataFetcher { env -> runBlocking { - resolve(DataFetchingArgument(env), env.getSource(), env.getContext(), env) + env.graphQlContext + resolve(DataFetchingArgument(env), env.getSource(), env.graphQlContext, env) } } \ No newline at end of file diff --git a/server/src/main/kotlin/com/arianegraphql/ktx/SubscriptionFilter.kt b/server/src/main/kotlin/com/arianegraphql/ktx/SubscriptionFilter.kt index dbe4f08..1fe51bc 100644 --- a/server/src/main/kotlin/com/arianegraphql/ktx/SubscriptionFilter.kt +++ b/server/src/main/kotlin/com/arianegraphql/ktx/SubscriptionFilter.kt @@ -1,19 +1,21 @@ package com.arianegraphql.ktx -interface SubscriptionFilter { +import graphql.GraphQLContext - suspend fun test(arguments: Argument, source: S, context: C?, info: Info, item: T): Boolean +interface SubscriptionFilter { + + suspend fun test(arguments: Argument, source: S, context: GraphQLContext, info: Info, item: T): Boolean } @JvmInline -value class FunctionalSubscriptionFilter( - private val lambda: suspend (arguments: Argument, source: S, context: C?, info: Info, item: T) -> Boolean -) : SubscriptionFilter { +value class FunctionalSubscriptionFilter( + private val lambda: suspend (arguments: Argument, source: S, context: GraphQLContext, info: Info, item: T) -> Boolean +) : SubscriptionFilter { override suspend fun test( arguments: Argument, source: S, - context: C?, + context: GraphQLContext, info: Info, item: T ) = lambda(arguments, source, context, info, item) diff --git a/server/src/main/kotlin/com/arianegraphql/ktx/SubscriptionTypeResolverBuilder.kt b/server/src/main/kotlin/com/arianegraphql/ktx/SubscriptionTypeResolverBuilder.kt index bfbe0bd..f0a9a23 100644 --- a/server/src/main/kotlin/com/arianegraphql/ktx/SubscriptionTypeResolverBuilder.kt +++ b/server/src/main/kotlin/com/arianegraphql/ktx/SubscriptionTypeResolverBuilder.kt @@ -1,5 +1,6 @@ package com.arianegraphql.ktx +import graphql.GraphQLContext import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.reactive.asPublisher @@ -12,8 +13,8 @@ class SubscriptionTypeResolverBuilder : TypeResolverBuilder() { fun resolve(field: String, flow: Flow) = resolve(field, flow.asPublisher()) - fun resolve(field: String, flow: Flow, predicate: SubscriptionFilter) = - resolve(field) { arguments: Argument, source: S, context: C?, info: Info -> + fun resolve(field: String, flow: Flow, predicate: SubscriptionFilter) = + resolve(field) { arguments: Argument, source: S, context: GraphQLContext, info: Info -> flow.filter { predicate.test(arguments, source, context, info, it) }.asPublisher() @@ -22,6 +23,6 @@ class SubscriptionTypeResolverBuilder : TypeResolverBuilder() { fun resolve( field: String, flow: Flow, - predicate: suspend (arguments: Argument, source: S, context: C?, info: Info, item: T) -> Boolean + predicate: suspend (arguments: Argument, source: S, context: GraphQLContext, info: Info, item: T) -> Boolean ) = resolve(field, flow, FunctionalSubscriptionFilter(predicate)) } \ No newline at end of file diff --git a/server/src/main/kotlin/com/arianegraphql/ktx/TypeResolverBuilder.kt b/server/src/main/kotlin/com/arianegraphql/ktx/TypeResolverBuilder.kt index 0ce762a..9419588 100644 --- a/server/src/main/kotlin/com/arianegraphql/ktx/TypeResolverBuilder.kt +++ b/server/src/main/kotlin/com/arianegraphql/ktx/TypeResolverBuilder.kt @@ -1,17 +1,18 @@ package com.arianegraphql.ktx +import graphql.GraphQLContext import graphql.schema.idl.TypeRuntimeWiring @GraphQLSchemaDslMarker open class TypeResolverBuilder { - internal val typeResolver = mutableMapOf>() + internal val typeResolver = mutableMapOf>() - fun resolve(field: String, resolver: Resolver) { + fun resolve(field: String, resolver: Resolver) { typeResolver[field] = resolver } - fun resolve(field: String, resolver: suspend (arguments: Argument, source: S, context: C?, info: Info) -> Any?) { + fun resolve(field: String, resolver: suspend (arguments: Argument, source: S, context: GraphQLContext, info: Info) -> Any?) { typeResolver[field] = FunctionalResolver(resolver) } diff --git a/server/src/main/kotlin/com/arianegraphql/server/ArianeServerImpl.kt b/server/src/main/kotlin/com/arianegraphql/server/ArianeServerImpl.kt index 206303e..9de6580 100644 --- a/server/src/main/kotlin/com/arianegraphql/server/ArianeServerImpl.kt +++ b/server/src/main/kotlin/com/arianegraphql/server/ArianeServerImpl.kt @@ -17,7 +17,7 @@ import kotlinx.coroutines.flow.* class ArianeServerImpl( private val schema: GraphQL, private val isPlaygroundEnabled: Boolean, - private val contextResolver: ContextResolver<*>, + private val contextResolver: ContextResolver, private val requestListener: RequestListener?, private val subscriptionListener: SubscriptionListener?, jsonSerializer: JsonSerializer, diff --git a/server/src/main/kotlin/com/arianegraphql/server/async/SubscriptionHandler.kt b/server/src/main/kotlin/com/arianegraphql/server/async/SubscriptionHandler.kt index 3fc17a9..026e636 100644 --- a/server/src/main/kotlin/com/arianegraphql/server/async/SubscriptionHandler.kt +++ b/server/src/main/kotlin/com/arianegraphql/server/async/SubscriptionHandler.kt @@ -6,7 +6,7 @@ import com.arianegraphql.server.request.WebSocketRequest import kotlinx.coroutines.flow.Flow interface SubscriptionHandler { - suspend fun initSubscription(wsRequest: WebSocketRequest, contextResolver: ContextResolver<*>): Flow + suspend fun initSubscription(wsRequest: WebSocketRequest, contextResolver: ContextResolver): Flow suspend fun startSubscription(wsRequest: WebSocketRequest, wsPayload: WebSocketPayload, ): Flow diff --git a/server/src/main/kotlin/com/arianegraphql/server/async/SubscriptionHandlerImpl.kt b/server/src/main/kotlin/com/arianegraphql/server/async/SubscriptionHandlerImpl.kt index c39ecdd..795af4a 100644 --- a/server/src/main/kotlin/com/arianegraphql/server/async/SubscriptionHandlerImpl.kt +++ b/server/src/main/kotlin/com/arianegraphql/server/async/SubscriptionHandlerImpl.kt @@ -25,7 +25,10 @@ class SubscriptionHandlerImpl( private val operationStore: OperationStore = OperationStoreImpl() ) : SubscriptionHandler, JsonSerializer by jsonSerializer, RequestPerformer by requestPerformer { - override suspend fun initSubscription(wsRequest: WebSocketRequest, contextResolver: ContextResolver<*>): Flow { + override suspend fun initSubscription( + wsRequest: WebSocketRequest, + contextResolver: ContextResolver + ): Flow { subscriptionListener?.onNewConnection(wsRequest.sessionId) val context = contextResolver.resolveContext(wsRequest) sessionStore.saveContext(wsRequest.sessionId, context) @@ -41,7 +44,11 @@ class SubscriptionHandlerImpl( return flow { operationStore.startOperation(wsRequest.sessionId, operationId) { - val context = sessionStore.getContext(wsRequest.sessionId) + val context = sessionStore.getContext(wsRequest.sessionId).getOrElse { + it.printStackTrace() + return@startOperation + } + subscriptionListener?.onStartSubscription(wsRequest.sessionId, context, operationId, graphQLRequest) performRequest(graphQLRequest, context, requestListener) @@ -56,15 +63,21 @@ class SubscriptionHandlerImpl( val operationId = wsPayload.id ?: return flowOf(serializeWebSocketPayload(connectionError)) operationStore.stopOperation(wsRequest.sessionId, operationId) - val context = sessionStore.getContext(wsRequest.sessionId) - subscriptionListener?.onStopSubscription(wsRequest.sessionId, context, operationId) + sessionStore.getContext(wsRequest.sessionId).fold(onSuccess = { context -> + subscriptionListener?.onStopSubscription(wsRequest.sessionId, context, operationId) + }, onFailure = { + it.printStackTrace() + }) return flowOf(serializeWebSocketPayload(connectionComplete(operationId))) } override suspend fun endSubscription(sessionId: String) { if (operationStore.hasOperationForSessionId(sessionId)) { - val context = sessionStore.getContext(sessionId) + val context = sessionStore.getContext(sessionId).getOrElse { + it.printStackTrace() + return + } subscriptionListener?.onCloseConnection(sessionId, context) sessionStore.clearContext(sessionId) diff --git a/server/src/main/kotlin/com/arianegraphql/server/config/ArianeServerConfiguration.kt b/server/src/main/kotlin/com/arianegraphql/server/config/ArianeServerConfiguration.kt index 210fd65..38a07a3 100644 --- a/server/src/main/kotlin/com/arianegraphql/server/config/ArianeServerConfiguration.kt +++ b/server/src/main/kotlin/com/arianegraphql/server/config/ArianeServerConfiguration.kt @@ -15,7 +15,7 @@ open class ArianeServerConfiguration( val path: String, val isPlaygroundEnabled: Boolean, val enableCORS: Boolean, - val contextResolver: ContextResolver<*>, + val contextResolver: ContextResolver, val serverListener: ServerListener?, val requestListener: RequestListener?, val subscriptionListener: SubscriptionListener? diff --git a/server/src/main/kotlin/com/arianegraphql/server/context/ContextResolver.kt b/server/src/main/kotlin/com/arianegraphql/server/context/ContextResolver.kt index 292ca32..363b0be 100644 --- a/server/src/main/kotlin/com/arianegraphql/server/context/ContextResolver.kt +++ b/server/src/main/kotlin/com/arianegraphql/server/context/ContextResolver.kt @@ -1,14 +1,15 @@ package com.arianegraphql.server.context import com.arianegraphql.server.request.IncomingRequest +import graphql.GraphQLContext -interface ContextResolver { - suspend fun resolveContext(request: IncomingRequest): R? +interface ContextResolver { + suspend fun resolveContext(request: IncomingRequest): GraphQLContext } -@JvmInline value class FunctionalContextResolver( - private val lambda: suspend (request: IncomingRequest) -> R? -) : ContextResolver { +@JvmInline value class FunctionalContextResolver( + private val lambda: suspend (request: IncomingRequest) -> GraphQLContext +) : ContextResolver { - override suspend fun resolveContext(request: IncomingRequest): R? = lambda(request) + override suspend fun resolveContext(request: IncomingRequest): GraphQLContext = lambda(request) } \ No newline at end of file diff --git a/server/src/main/kotlin/com/arianegraphql/server/dsl/ArianeServerBuilder.kt b/server/src/main/kotlin/com/arianegraphql/server/dsl/ArianeServerBuilder.kt index 7cc0706..a7a801a 100644 --- a/server/src/main/kotlin/com/arianegraphql/server/dsl/ArianeServerBuilder.kt +++ b/server/src/main/kotlin/com/arianegraphql/server/dsl/ArianeServerBuilder.kt @@ -8,6 +8,7 @@ import com.arianegraphql.server.listener.RequestListener import com.arianegraphql.server.listener.ServerListener import com.arianegraphql.server.listener.SubscriptionListener import com.arianegraphql.server.request.IncomingRequest +import graphql.GraphQLContext @GraphQLSchemaDslMarker open class ArianeServerBuilder : RuntimeWiringBuilder() { @@ -24,15 +25,15 @@ open class ArianeServerBuilder : RuntimeWiringBuilder() { var requestListener: RequestListener? = null var subscriptionListener: SubscriptionListener? = null - var contextResolver: ContextResolver<*> = object : ContextResolver { - override suspend fun resolveContext(request: IncomingRequest): Any? = null + var contextResolver: ContextResolver = object : ContextResolver { + override suspend fun resolveContext(request: IncomingRequest): GraphQLContext = GraphQLContext.of(emptyMap()) } - fun context(contextResolver: ContextResolver) { + fun context(contextResolver: ContextResolver) { this.contextResolver = contextResolver } - fun context(contextResolver: suspend (request: IncomingRequest) -> R?) { + fun context(contextResolver: suspend (request: IncomingRequest) -> GraphQLContext) { this.contextResolver = FunctionalContextResolver(contextResolver) } diff --git a/server/src/main/kotlin/com/arianegraphql/server/listener/SubscriptionListener.kt b/server/src/main/kotlin/com/arianegraphql/server/listener/SubscriptionListener.kt index fdd1e31..b7de361 100644 --- a/server/src/main/kotlin/com/arianegraphql/server/listener/SubscriptionListener.kt +++ b/server/src/main/kotlin/com/arianegraphql/server/listener/SubscriptionListener.kt @@ -1,16 +1,17 @@ package com.arianegraphql.server.listener import com.arianegraphql.server.graphql.GraphQLRequest +import graphql.GraphQLContext interface SubscriptionListener { fun onNewConnection(sessionId: String) {} - fun onConnected(sessionId: String, context: Any?) {} + fun onConnected(sessionId: String, context: GraphQLContext) {} - fun onStartSubscription(sessionId: String, context: Any?, operationId: String, graphQLRequest: GraphQLRequest) {} + fun onStartSubscription(sessionId: String, context: GraphQLContext, operationId: String, graphQLRequest: GraphQLRequest) {} - fun onStopSubscription(sessionId: String, context: Any?, operationId: String) {} //TODO NOT CALLED + fun onStopSubscription(sessionId: String, context: GraphQLContext, operationId: String) {} //TODO NOT CALLED - fun onCloseConnection(sessionId: String, context: Any?) {} //TODO NOT CALLED + fun onCloseConnection(sessionId: String, context: GraphQLContext) {} //TODO NOT CALLED } \ No newline at end of file diff --git a/server/src/main/kotlin/com/arianegraphql/server/store/SessionStore.kt b/server/src/main/kotlin/com/arianegraphql/server/store/SessionStore.kt index 6b86b51..a752064 100644 --- a/server/src/main/kotlin/com/arianegraphql/server/store/SessionStore.kt +++ b/server/src/main/kotlin/com/arianegraphql/server/store/SessionStore.kt @@ -1,10 +1,12 @@ package com.arianegraphql.server.store +import graphql.GraphQLContext + interface SessionStore { - suspend fun saveContext(sessionId: String, context: Any?) + suspend fun saveContext(sessionId: String, context: GraphQLContext) - suspend fun getContext(sessionId: String): Any? + suspend fun getContext(sessionId: String): Result suspend fun clearContext(sessionId: String) } \ No newline at end of file diff --git a/server/src/main/kotlin/com/arianegraphql/server/store/SessionStoreImpl.kt b/server/src/main/kotlin/com/arianegraphql/server/store/SessionStoreImpl.kt index f37c19e..1d55894 100644 --- a/server/src/main/kotlin/com/arianegraphql/server/store/SessionStoreImpl.kt +++ b/server/src/main/kotlin/com/arianegraphql/server/store/SessionStoreImpl.kt @@ -1,17 +1,19 @@ package com.arianegraphql.server.store +import graphql.GraphQLContext import java.util.concurrent.ConcurrentHashMap class SessionStoreImpl( - private val contextStore: MutableMap = ConcurrentHashMap() -): SessionStore { + private val contextStore: MutableMap = ConcurrentHashMap() +) : SessionStore { - override suspend fun saveContext(sessionId: String, context: Any?) { - context?.let { contextStore[sessionId] = context } + override suspend fun saveContext(sessionId: String, context: GraphQLContext) { + contextStore[sessionId] = context } - override suspend fun getContext(sessionId: String): Any? { - return contextStore[sessionId] + override suspend fun getContext(sessionId: String): Result { + return contextStore[sessionId]?.let { Result.success(it) } + ?: Result.failure(IllegalStateException("Session with id '$sessionId' not found.")) } override suspend fun clearContext(sessionId: String) { diff --git a/server/src/test/kotlin/com/arianegraphql/server/store/SessionStoreImplTest.kt b/server/src/test/kotlin/com/arianegraphql/server/store/SessionStoreImplTest.kt index fa6ce91..e9bde8b 100644 --- a/server/src/test/kotlin/com/arianegraphql/server/store/SessionStoreImplTest.kt +++ b/server/src/test/kotlin/com/arianegraphql/server/store/SessionStoreImplTest.kt @@ -1,5 +1,6 @@ package com.arianegraphql.server.store +import graphql.GraphQLContext import io.github.serpro69.kfaker.Faker import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.BeforeEach @@ -9,7 +10,7 @@ import org.junit.jupiter.api.Assertions.* internal class SessionStoreImplTest { - private val contextStore = mutableMapOf() + private val contextStore = mutableMapOf() private lateinit var sessionStore: SessionStoreImpl @BeforeEach @@ -21,32 +22,29 @@ internal class SessionStoreImplTest { fun `saveContext() should store context into hashmap`() = runBlocking { contextStore.clear() val sessionId = "foobar" - val context = FakeContext() - sessionStore.saveContext(sessionId, context) + val fakeContext = GraphQLContext.of( + mutableMapOf( + "favoriteGalaxy" to Faker().space.galaxy() + ) + ) - assertEquals(context, contextStore[sessionId]) - } - - @Test - fun `saveContext() should not store null context`() = runBlocking { - contextStore.clear() - val sessionId = "barfoo" + sessionStore.saveContext(sessionId, fakeContext) - sessionStore.saveContext(sessionId, null) - assertEquals(0, contextStore.size) + assertEquals(fakeContext, contextStore[sessionId]) } @Test fun `getContext() should return the context`() = runBlocking { val sessionId = "FOO_BAR" + val fakeContext = GraphQLContext.of( + mutableMapOf( + "favoriteGalaxy" to Faker().space.galaxy() + ) + ) - val context = FakeContext() - - contextStore[sessionId] = context + contextStore[sessionId] = fakeContext val actual = sessionStore.getContext(sessionId) - assertEquals(context, actual) + assertEquals(Result.success(fakeContext), actual) } -} - -data class FakeContext(val favoriteGalaxy: String = Faker().space.galaxy()) \ No newline at end of file +} \ No newline at end of file