All Downloads are FREE. Search and download functionalities are using the official Maven repository.

main.com.netflix.graphql.dgs.webflux.handlers.DefaultDgsWebfluxHttpHandler.kt Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2021 Netflix, Inc.
 *
 * 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 com.netflix.graphql.dgs.webflux.handlers

import com.fasterxml.jackson.core.JsonParseException
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.exc.MismatchedInputException
import com.fasterxml.jackson.module.kotlin.readValue
import com.netflix.graphql.dgs.DgsExecutionResult
import com.netflix.graphql.dgs.reactive.DgsReactiveQueryExecutor
import graphql.ExecutionResult
import org.intellij.lang.annotations.Language
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.web.reactive.function.server.ServerRequest
import org.springframework.web.reactive.function.server.ServerResponse
import reactor.core.publisher.Mono

class DefaultDgsWebfluxHttpHandler(
    private val dgsQueryExecutor: DgsReactiveQueryExecutor,
    private val objectMapper: ObjectMapper,
) : DgsWebfluxHttpHandler {
    override fun graphql(request: ServerRequest): Mono {
        @Suppress("UNCHECKED_CAST")
        val executionResult: Mono =

            request
                .bodyToMono(String::class.java)
                .flatMap { body ->
                    if (GraphQLMediaTypes.isApplicationGraphQL(request)) {
                        Mono.just(QueryInput(body))
                    } else {
                        Mono.fromCallable {
                            val readValue = objectMapper.readValue>(body)
                            val query: String? =
                                when (val iq = readValue["query"]) {
                                    is String -> iq
                                    else -> null
                                }
                            val operationName: String =
                                when (val iq = readValue["operationName"]) {
                                    is String -> iq
                                    else -> ""
                                }
                            QueryInput(
                                query,
                                (readValue["variables"] ?: emptyMap()) as Map,
                                (readValue["extensions"] ?: emptyMap()) as Map,
                                operationName,
                            )
                        }
                    }
                }.flatMap { queryInput ->
                    logger.debug("Parsed variables: {}", queryInput.queryVariables)
                    dgsQueryExecutor.execute(
                        queryInput.query,
                        queryInput.queryVariables,
                        queryInput.extensions,
                        request.headers().asHttpHeaders(),
                        queryInput.operationName,
                        request,
                    )
                }

        return executionResult
            .flatMap { result ->
                val dgsExecutionResult =
                    when (result) {
                        is DgsExecutionResult -> result
                        else -> DgsExecutionResult.builder().executionResult(result).build()
                    }

                ServerResponse
                    .status(dgsExecutionResult.status)
                    .headers { it.addAll(dgsExecutionResult.headers()) }
                    .bodyValue(dgsExecutionResult.toSpecification())
            }.onErrorResume { ex ->
                when (ex) {
                    is JsonParseException ->
                        ServerResponse
                            .badRequest()
                            .bodyValue("Invalid query - ${ex.message ?: "no details found in the error message"}.")
                    is MismatchedInputException ->
                        ServerResponse
                            .badRequest()
                            .bodyValue("Invalid query - No content to map to input.")
                    else ->
                        ServerResponse
                            .badRequest()
                            .bodyValue("Invalid query - ${ex.message ?: "no additional details found"}.")
                }
            }
    }

    companion object {
        private val logger: Logger = LoggerFactory.getLogger(DefaultDgsWebfluxHttpHandler::class.java)
    }
}

private data class QueryInput(
    @Language("graphql") val query: String?,
    val queryVariables: Map = emptyMap(),
    val extensions: Map = emptyMap(),
    val operationName: String = "",
)




© 2015 - 2025 Weber Informatics LLC | Privacy Policy