Как сделать преобразователи уровня поля, используя graphql-kotlin с Ktor Framework - PullRequest
0 голосов
/ 31 мая 2019

как сделать преобразователи уровня поля (не root) с помощью gaphql-kotlin? Мой код работает (date_range в API1Response имеет значение null, так как я не знаю, как вызвать мою вторую функцию преобразователя getAPI2Response (), когда date_range существует в запросе) , когда каждый запрос вызывается отдельно, так как у меня есть две функции приостановки с именами соответствующих запросов , но я хочу вызвать две функции приостановки с одним запросом на основе проверки поля (не root) в запросе. Например, в приведенном ниже коде (service), если мой запрос «getAPI1Response» с полями «name», «id», «date_range», то graphql должен вызвать getAPI1Response, а также getAPI2Response и сопоставить его с моделью данных «API1Response», поэтому пользователи могут использовать один запрос вместо двух запросов.

Проверено несколько образцов из выборок graphql-kotlin, но все они относятся к Springboot, который я не использую.

//DATA MODEL

data class API1Response(
    val name: String?,
    val id: Int?,
    val date_range: API2Response?
)

data class API2Response(
    val max_date_range: ZonedDateTime?,
    val min_date_range: ZonedDateTime?
)
//SERVICE
class Query() {
    private val httpClient = HttpClient()

// when query name is "getAPI1Response", this function get triggers
    suspend fun getAPI1Response(): API1Response {
        // call API 1 and map response to API1Response model.
        return resp.content.toInputStream().use {
                    jackson.readValue(it, API1Response::class.java)
                }
    }

//// when query name is "getAPI2Response", this function get triggers
    suspend fun getAPI2Response(): API2Response {
        // call API 2 and map response to API2Response model.
        return resp.content.toInputStream().use {
                    jackson.readValue(it, API2Response::class.java)
                }
    }
}
// GRAPHQL HANDLER

package com.my.package.graphql

import com.expedia.graphql.SchemaGeneratorConfig
import com.expedia.graphql.TopLevelObject
import com.expedia.graphql.hooks.SchemaGeneratorHooks
import com.expedia.graphql.toSchema
import com.my.package.errors.ErrorType
import com.my.package.http.*
import com.my.package.json.Jackson
import graphql.ExecutionInput
import graphql.GraphQL
import graphql.language.StringValue
import graphql.schema.*
import graphql.schema.idl.SchemaPrinter
import io.ktor.application.ApplicationCall
import io.ktor.application.call
import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode
import io.ktor.response.respondText
import io.ktor.routing.Route
import io.ktor.routing.get
import io.ktor.routing.post
import java.time.LocalDate
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import kotlin.reflect.KClass
import kotlin.reflect.KType

class GraphQLHandler(
    mutations: List<Any>,
    queries: List<Any>
) : Handler {
    private val schema = toSchema(
        config = SchemaGeneratorConfig(
            supportedPackages = listOf("com.my.package"),
            hooks = CustomSchemaGeneratorHooks(),
            topLevelQueryName = "Query",
            topLevelMutationName = "Mutation"
        ),
        mutations = mutations.map { TopLevelObject(it) },
        queries = queries.map { TopLevelObject(it) }
    )

    private val graphQL = GraphQL.newGraphQL(schema).build()

    override val path = "/graphql"

    override val routes: Route.() -> Unit = {
        post {
            postQuery(call)
        }
    }

    private suspend fun postQuery(call: ApplicationCall) {
        val reqBody = call.receiveJson(Map::class)
        val query = reqBody["query"] as? String
        if (query == null) {
            call.respondError(
                HttpStatusCode.BadRequest,
                ErrorType.bad_request,
                "missing or invalid query field in json"
            )
            return
        }

        @Suppress("UNCHECKED_CAST")
        val variables = reqBody["variables"] as? Map<String, Any> ?: emptyMap()

        handleQuery(call, query, variables)
    }

// main function which calls my concern suspend funciton mapping query name to function name
    private suspend fun handleQuery(call: ApplicationCall, query: String, variables: Map<String, Any>?) {
        val executionInput = ExecutionInput(query, null, call.request.authContext, null, variables)
        val result = graphQL.execute(executionInput).toSpecification()
        val statusCode = if (result.containsKey("errors")) HttpStatusCode.InternalServerError else HttpStatusCode.OK
        call.respondJson(statusCode, result, Jackson.all)
    }
}

class CustomSchemaGeneratorHooks : SchemaGeneratorHooks {
    override fun willGenerateGraphQLType(type: KType): GraphQLType? = when (type.classifier as? KClass<*>) {
        ZonedDateTime::class -> graphqlZonedDateTimeType
        LocalDate::class -> graphqlLocalDateType
        else -> null
    }
}

val graphqlLocalDateType = GraphQLScalarType("LocalDate",
    " ISO date format without an offset, such as '2011-12-03' ",
    object : Coercing<LocalDate, String> {
        //override few functions here
    }
)

val graphqlZonedDateTimeType = GraphQLScalarType("ZonedDateTime",
    " ISO date-time format with an offset, such as '2011-12-03T10:15:30+01:00' ",
    object : Coercing<ZonedDateTime, String> {
        //override few functions here
    }
)

Ответы [ 2 ]

0 голосов
/ 07 июня 2019

Этот вопрос был опубликован в наших выпусках: https://github.com/ExpediaDotCom/graphql-kotlin/issues/230

Мы ответили там, но лучший способ получить доступ к данным из полей выше - через DataFetcherEnvironment.Здесь было добавлено: https://github.com/ExpediaDotCom/graphql-kotlin/pull/173

Тогда внутри API1Response может быть функция, которая будет запускаться при вызове из схемы GraphQL

data class API1Response(
    val name: String?,
    val id: Int?
) {
    // Functions are also exposed as fields.
    // environment will not show up in the schema but be available at runtime.
    fun date_range(environment: DataFetchingEnvironment): API2Response? {
        // Somehow get dates from other args or further up the query chain
        return API2Response()
    }
}
0 голосов
/ 07 июня 2019

Ответ на этот вопрос в связанной проблеме github: https://github.com/ExpediaDotCom/graphql-kotlin/issues/230

Пример использования Spring: https://github.com/ExpediaDotCom/graphql-kotlin/blob/master/example/src/main/kotlin/com/expedia/graphql/sample/query/SubQueries.kt

Так как graphql-kotlin рефлексивно генерирует распознаватели, у вас будетчтобы сделать что-то похожее на приведенный выше пример, или использовать KGraphQL или graphql-java напрямую

Другой пример получения данных из цепочки запросов - использование DataFetcherEnvironment.Здесь было добавлено: # 173

...