Netflix / dgs-framework

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

bug: NPE in integration test when not using DgsAutoConfiguration #1105

Closed Georgian closed 1 year ago

Georgian commented 2 years ago

Background

I'm updating DGS libraries from 4.9.25 to 5.0.2, then running a @SpringBootTest integration test which uses DgsQueryExecutor directly to fire queries to the running test server.

Expected behavior

No major code changes should be needed (hopefully).

Actual behavior

I'm getting java.lang.NullPointerException: null cannot be cast to non-null type org.springframework.web.context.request.NativeWebRequest in HandlerMethodArgumentResolverAdapter (introduced in 4.10.3) which handles my custom @RequestHeader; more specifically at line 51.

Steps to reproduce

I've conveniently forked this repo from a previous bug. Simply run the doMath test.

With @SpringBootTest(classes = [DgsAutoConfiguration::class]) it works fine.

Workarounds

  1. Downgrade to a pre - 4.10.3 version (hardly an option, since this is strictly a test problem)
  2. Declare DgsAutoConfiguration class in test (inconvenient for me, because the test is HUGE and pulls in lots of components)
matejuh commented 2 years ago

Same issue here. Makes DgsQueryExecutor unusable with newer DSG versions. It's possible to pass fake request, but not to all methods...

berngp commented 2 years ago

Thanks again for reporting. We haven't been able to look into this issue yet.

fripoli commented 2 years ago

any updates on this? I just used the sample fork mentioned by @Georgian with the latest version of the framework and the error is the same ...

java.lang.NullPointerException: null cannot be cast to non-null type org.springframework.web.context.request.NativeWebRequest
com.netflix.graphql.dgs.exceptions.QueryException: java.lang.NullPointerException: null cannot be cast to non-null type org.springframework.web.context.request.NativeWebRequest
AlexRiedler commented 1 year ago

I worked around this locally using the following until it is fixed upstream (sorry for those that don't use kotlin)

inline fun <reified T> DgsQueryExecutor.executeAndExtractJsonPathAsObject(
    @Language("GraphQL") query: String,
    @Language("JSONPath") jsonPath: String,
    variables: Map<String, Any>,
    headers: HttpHeaders?
): T {
    // - new part -
    val request = MockHttpServletRequest()
    headers?.forEach { header, headerValues ->
        request.addHeader(header, headerValues)
    }
    // - end of new part -
    val jsonResult = this.getJsonResult(query, variables, headers, ServletWebRequest(request))
    return try {
        BaseDgsQueryExecutor.parseContext.parse(jsonResult).read(jsonPath, T::class.java)
    } catch (ex: MappingException) {
        throw DgsQueryExecutionDataExtractionException(ex, jsonResult, jsonPath, T::class.java)
    }
}

// redefinition because it is a private method
fun DgsQueryExecutor.getJsonResult(query: String, variables: Map<String, Any>, headers: HttpHeaders? = null, servletWebRequest: WebRequest? = null): String {
    val executionResult = execute(query, variables, null, headers, null, servletWebRequest)

    if (executionResult.errors.size > 0) {
        throw QueryException(executionResult.errors)
    }

    return BaseDgsQueryExecutor.objectMapper.writeValueAsString(executionResult.toSpecification())
}

I believe we need to build the servletWebRequest instead of defaulting to null ? not sure how easy it is to do.