Skip to content

Commit

Permalink
Merge branch 'master' into validation-extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
berngp committed Sep 28, 2021
2 parents de8f59e + 15995c2 commit 300eda0
Show file tree
Hide file tree
Showing 15 changed files with 769 additions and 166 deletions.
6 changes: 4 additions & 2 deletions graphql-dgs-client/build.gradle.kts
Expand Up @@ -25,14 +25,16 @@ dependencies {
implementation("org.springframework:spring-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
implementation("com.fasterxml.jackson.module:jackson-module-parameter-names")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jdk8")
implementation("com.graphql-java:graphql-java")

testImplementation("org.springframework.boot:spring-boot-starter-web")
testImplementation("org.springframework:spring-test")
testImplementation("org.springframework.boot:spring-boot-starter-webflux")
testImplementation("com.graphql-java:graphql-java-extended-scalars")
testImplementation("io.projectreactor:reactor-test")
testImplementation("com.squareup.okhttp3:mockwebserver")
testImplementation(project(":graphql-dgs-webflux-starter"))
testImplementation(project(":graphql-dgs-subscriptions-sse-autoconfigure"))
testImplementation(project(":graphql-dgs-spring-boot-oss-autoconfigure"))
testImplementation(project(":graphql-dgs-spring-webmvc-autoconfigure"))
}
149 changes: 28 additions & 121 deletions graphql-dgs-client/dependencies.lock

Large diffs are not rendered by default.

@@ -0,0 +1,45 @@
/*
* 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.client

/**
* Blocking implementation of a GraphQL client.
* The user is responsible for doing the actual HTTP request, making this pluggable with any HTTP client.
* For a more convenient option, use [WebClientGraphQLClient] instead.
*/
class CustomGraphQLClient(private val url: String, private val requestExecutor: RequestExecutor) : GraphQLClient {
override fun executeQuery(query: String): GraphQLResponse {
return executeQuery(query, emptyMap(), null)
}

override fun executeQuery(query: String, variables: Map<String, Any>): GraphQLResponse {
return executeQuery(query, variables, null)
}

override fun executeQuery(query: String, variables: Map<String, Any>, operationName: String?): GraphQLResponse {
val serializedRequest = GraphQLClients.objectMapper.writeValueAsString(
Request(
query,
variables,
operationName
)
)

val response = requestExecutor.execute(url, GraphQLClients.defaultHeaders, serializedRequest)
return GraphQLClients.handleResponse(response, serializedRequest, url)
}
}
@@ -0,0 +1,51 @@
/*
* 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.client

import reactor.core.publisher.Mono

/**
* Non-blocking implementation of a GraphQL client, based on the [Mono] type.
* The user is responsible for doing the actual HTTP request, making this pluggable with any HTTP client.
* For a more convenient option, use [WebClientGraphQLClient] instead.
*/
class CustomMonoGraphQLClient(private val url: String, private val monoRequestExecutor: MonoRequestExecutor) : MonoGraphQLClient {
override fun reactiveExecuteQuery(query: String): Mono<GraphQLResponse> {
return reactiveExecuteQuery(query, emptyMap(), null)
}

override fun reactiveExecuteQuery(query: String, variables: Map<String, Any>): Mono<GraphQLResponse> {
return reactiveExecuteQuery(query, variables, null)
}

override fun reactiveExecuteQuery(
query: String,
variables: Map<String, Any>,
operationName: String?
): Mono<GraphQLResponse> {
val serializedRequest = GraphQLClients.objectMapper.writeValueAsString(
Request(
query,
variables,
operationName
)
)
return monoRequestExecutor.execute(url, GraphQLClients.defaultHeaders, serializedRequest).map { response ->
GraphQLClients.handleResponse(response, serializedRequest, url)
}
}
}
Expand Up @@ -16,15 +16,13 @@

package com.netflix.graphql.dgs.client

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
import reactor.core.publisher.Mono

/**
* Default GraphQLClient implementation. Use this class to execute GraphQL queries against a standalone DGS or the gateway.
* Default [GraphQLClient] implementation. Use this class to execute GraphQL queries against a standalone DGS or the gateway.
* The value of this client is in it's JSON parsing of responses.
* The client is not tied to any particular HTTP client library. The actual HTTP request code is provided by the user.
* Note that if you want to use WebClient, there is the [WebClientGraphQLClient] available, which is simpler to use.
*
* Example:
*
Expand All @@ -40,23 +38,9 @@ import reactor.core.publisher.Mono
* return new HttpResponse(exchange.getStatusCodeValue(), exchange.getBody());
* });
*/
@Deprecated("This has been replaced by [CustomGraphQLClient], [CustomReactiveGraphQLClient] and [WebClientGraphQLClient]")
class DefaultGraphQLClient(private val url: String) : GraphQLClient, MonoGraphQLClient {

companion object {
private val objectMapper: ObjectMapper = try {
Class.forName("com.fasterxml.jackson.module.kotlin.KotlinModule\$Builder")
ObjectMapper().registerModule(KotlinModule.Builder().nullIsSameAsDefault(true).build())
} catch (ex: ClassNotFoundException) {
ObjectMapper().registerKotlinModule()
}
private val defaultHeaders = mapOf(
"Accept" to listOf("application/json"),
"Content-type" to listOf("application/json")
)

private data class Request(val query: String, val variables: Map<String, Any>, val operationName: String?)
}

/**
* Executes a query and returns a GraphQLResponse.
* The actual HTTP request is done by an implementation of RequestExecutor, which is user provided.
Expand All @@ -75,10 +59,21 @@ class DefaultGraphQLClient(private val url: String) : GraphQLClient, MonoGraphQL
operationName: String?,
requestExecutor: RequestExecutor
): GraphQLResponse {
val serializedRequest = objectMapper.writeValueAsString(Request(query, variables, operationName))
val serializedRequest = GraphQLClients.objectMapper.writeValueAsString(Request(query, variables, operationName))
val response = requestExecutor.execute(url, GraphQLClients.defaultHeaders, serializedRequest)
return GraphQLClients.handleResponse(response, serializedRequest, url)
}

override fun executeQuery(query: String): GraphQLResponse {
throw UnsupportedOperationException("Please move to [BlockingGraphQLClient] to use this method")
}

override fun executeQuery(query: String, variables: Map<String, Any>): GraphQLResponse {
throw UnsupportedOperationException("Please move to [BlockingGraphQLClient] to use this method")
}

val response = requestExecutor.execute(url, defaultHeaders, serializedRequest)
return handleResponse(response, serializedRequest)
override fun executeQuery(query: String, variables: Map<String, Any>, operationName: String?): GraphQLResponse {
throw UnsupportedOperationException("Please move to [BlockingGraphQLClient] to use this method")
}

/**
Expand All @@ -97,9 +92,26 @@ class DefaultGraphQLClient(private val url: String) : GraphQLClient, MonoGraphQL
variables: Map<String, Any>,
requestExecutor: RequestExecutor
): GraphQLResponse {
@Suppress("DEPRECATION", "BlockingMethodInNonBlockingContext")
return executeQuery(query, variables, null, requestExecutor)
}

override fun reactiveExecuteQuery(query: String): Mono<GraphQLResponse> {
throw UnsupportedOperationException("Please move to [CustomGraphQLClient] to use this method")
}

override fun reactiveExecuteQuery(query: String, variables: Map<String, Any>): Mono<GraphQLResponse> {
throw UnsupportedOperationException("Please move to [CustomGraphQLClient] to use this method")
}

override fun reactiveExecuteQuery(
query: String,
variables: Map<String, Any>,
operationName: String?
): Mono<GraphQLResponse> {
throw UnsupportedOperationException("Please move to [CustomGraphQLClient] to use this method")
}

/**
* Executes a query and returns a reactive Mono<GraphQLResponse>.
* The actual HTTP request is done by an implementation of RequestExecutor, which is user provided.
Expand Down Expand Up @@ -137,20 +149,9 @@ class DefaultGraphQLClient(private val url: String) : GraphQLClient, MonoGraphQL
operationName: String?,
requestExecutor: MonoRequestExecutor
): Mono<GraphQLResponse> {
val serializedRequest = objectMapper.writeValueAsString(Request(query, variables, operationName))

return requestExecutor.execute(url, defaultHeaders, serializedRequest).map { response ->
handleResponse(response, serializedRequest)
}
}

private fun handleResponse(response: HttpResponse, requestBody: String): GraphQLResponse {
val (statusCode, body) = response
val headers = response.headers
if (statusCode !in 200..299) {
throw GraphQLClientException(statusCode, url, body ?: "", requestBody)
val serializedRequest = GraphQLClients.objectMapper.writeValueAsString(Request(query, variables, operationName))
return requestExecutor.execute(url, GraphQLClients.defaultHeaders, serializedRequest).map { response ->
GraphQLClients.handleResponse(response, serializedRequest, url)
}

return GraphQLResponse(body ?: "", headers)
}
}

0 comments on commit 300eda0

Please sign in to comment.