Netflix / dgs-framework

GraphQL for Java with Spring Boot made easy.
https://netflix.github.io/dgs
Apache License 2.0
3.03k stars 286 forks source link

bug: client throws error when parsing response with error. #1815

Open DAVID9SE opened 4 months ago

DAVID9SE commented 4 months ago

Hi, I am experiencing an issue when using DGS for integration tests. I am using MonoGraphQLClient and I have my test queries in a file. Everything works fine until I try to test errors. The tests that are supposed to check if directives to check ranges work. The client throws an error instead of parsing the response.

This is what the tested endpoint returns to the client:

{
  "errors": [
    {
      "message": "The field topicsProjects must be between $1 and $100",
      "locations": [
        {
          "line": 3,
          "column": 3
        }
      ],
      "path": [
        "topicsProjectsQueries",
        "topicsProjects"
      ],
      "extensions": {
        "classification": {
          "type": "ExtendedValidationError",
          "validatedPath": [
            "topicsProjectsQueries",
            "topicsProjects",
            "paging",
            "size"
          ],
          "constraint": "@Range"
        }
      }
    }
  ],
  "data": null
}

This is the error I get.

com.jayway.jsonpath.spi.mapper.MappingException: java.lang.IllegalArgumentException: Cannot deserialize value of type `java.lang.String` from Object value (token `JsonToken.START_OBJECT`)
 at [Source: UNKNOWN; byte offset: #UNKNOWN] (through reference chain: java.util.ArrayList[0]->com.netflix.graphql.dgs.client.GraphQLError["extensions"]->com.netflix.graphql.dgs.client.GraphQLErrorExtensions["classification"])
    at com.jayway.jsonpath.spi.mapper.JacksonMappingProvider.map(JacksonMappingProvider.java:58)
    at com.jayway.jsonpath.internal.JsonContext.convert(JsonContext.java:121)
    at com.jayway.jsonpath.internal.JsonContext.read(JsonContext.java:103)
    at com.netflix.graphql.dgs.client.GraphQLResponse.<init>(GraphQLResponse.kt:52)
    at com.netflix.graphql.dgs.client.WebClientGraphQLClient.handleResponse(WebClientGraphQLClient.kt:146)
    at com.netflix.graphql.dgs.client.WebClientGraphQLClient.access$handleResponse(WebClientGraphQLClient.kt:42)
    at com.netflix.graphql.dgs.client.WebClientGraphQLClient$reactiveExecuteQuery$3.invoke(WebClientGraphQLClient.kt:136)
    at com.netflix.graphql.dgs.client.WebClientGraphQLClient$reactiveExecuteQuery$3.invoke(WebClientGraphQLClient.kt:136)
    at com.netflix.graphql.dgs.client.WebClientGraphQLClient.reactiveExecuteQuery$lambda$3(WebClientGraphQLClient.kt:136)
    at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:113)
    at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:129)
    at reactor.core.publisher.MonoFlatMap$FlatMapMain.secondComplete(MonoFlatMap.java:245)
    at reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:305)
    at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:122)
    at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onNext(FluxDefaultIfEmpty.java:122)
    at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79)
    at reactor.core.publisher.FluxOnAssembly$OnAssemblySubscriber.onNext(FluxOnAssembly.java:539)
    at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:129)
    at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onNext(FluxContextWrite.java:107)
    at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onNext(FluxMapFuseable.java:299)
    at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onNext(FluxFilterFuseable.java:337)
    at reactor.core.publisher.Operators$BaseFluxToMonoOperator.completePossiblyEmpty(Operators.java:2071)
    at reactor.core.publisher.MonoCollect$CollectSubscriber.onComplete(MonoCollect.java:145)
    at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144)
    at reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:260)
    at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144)
    at reactor.netty.channel.FluxReceive.onInboundComplete(FluxReceive.java:413)
    at reactor.netty.channel.ChannelOperations.onInboundComplete(ChannelOperations.java:431)
    at reactor.netty.channel.ChannelOperations.terminate(ChannelOperations.java:485)
    at reactor.netty.http.client.HttpClientOperations.onInboundNext(HttpClientOperations.java:712)
    at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:113)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
    at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
    at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436)
    at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:346)
    at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:318)
    at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
    at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
    at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
    at io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:800)
    at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:499)
    at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:397)
    at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
    at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.base/java.lang.Thread.run(Thread.java:840)
    Suppressed: java.lang.Exception: #block terminated with an error
        at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:102)
        at reactor.core.publisher.Mono.block(Mono.java:1712)
        at cz.cvut.fel.czm.temataprojectslogic.BaseCT.graphQlRequestFromFile(BaseCT.kt:67)

The method where the error is thrown.

fun graphQlRequestFromFile(file: String, variables: Map<String, Any> = emptyMap()): GraphQLResponse {
        val query = javaClass.getResource(file)?.readText()
            ?: throw IllegalStateException("Cannot read query")
        return graphQlTestClient.reactiveExecuteQuery(query, variables).block() ?: throw IllegalStateException(
            "Null response"
        )
    }

Expected behavior

I would expect the client to parse the response normally.

Actual behavior

The response is not parsed and an error is invoked.

Steps to reproduce

Use the client to request from an endpoint that returns an error(Like the one above.).

Thanks a lot

kilink commented 4 months ago

I believe this issue was fixed in v8.2.5 (see #1802). What version of DGS are you on?

DAVID9SE commented 4 months ago

Hey, we are on 7.XX. I will migrate (it will take some time since there are some breaking changes.) to 8.XX and see if the issue persists.