Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot set ID of graphql.scalar to null #3432

Open
AlexandreMaul opened this issue Feb 2, 2024 · 3 comments
Open

Cannot set ID of graphql.scalar to null #3432

AlexandreMaul opened this issue Feb 2, 2024 · 3 comments
Labels
keep-open Tells Stale Bot to keep PRs and issues open

Comments

@AlexandreMaul
Copy link

Describe the bug
I'm trying to response to a request with ID nullable. I'm in kotlin.

When i set my ID to null, i've got this error :


(n.g.e.ExecutionStrategy) WARN  Can't serialize value (/my/object[0]/kotlin[0]/id) : Expected type 'ID' but was 'ID'.
graphql.schema.CoercingSerializeException: Expected type 'ID' but was 'ID'.
	at graphql.scalar.GraphqlIDCoercing.serializeImpl(GraphqlIDCoercing.java:56)
	at graphql.scalar.GraphqlIDCoercing.serialize(GraphqlIDCoercing.java:99)
	at graphql.execution.ExecutionStrategy.completeValueForScalar(ExecutionStrategy.java:630)
	at graphql.execution.ExecutionStrategy.completeValue(ExecutionStrategy.java:471)
	at graphql.execution.ExecutionStrategy.completeField(ExecutionStrategy.java:435)
	at graphql.execution.ExecutionStrategy.lambda$resolveFieldWithInfo$1(ExecutionStrategy.java:215)
	at java.base/java.util.concurrent.CompletableFuture.uniApplyNow(CompletableFuture.java:684)
	at java.base/java.util.concurrent.CompletableFuture.uniApplyStage(CompletableFuture.java:662)
	at java.base/java.util.concurrent.CompletableFuture.thenApply(CompletableFuture.java:2168)
	at graphql.execution.ExecutionStrategy.resolveFieldWithInfo(ExecutionStrategy.java:214)
	at graphql.execution.AsyncExecutionStrategy.execute(AsyncExecutionStrategy.java:55)
	at graphql.execution.ExecutionStrategy.completeValueForObject(ExecutionStrategy.java:702)
	at graphql.execution.ExecutionStrategy.completeValue(ExecutionStrategy.java:484)
	at graphql.execution.ExecutionStrategy.completeValueForList(ExecutionStrategy.java:586)
	at graphql.execution.ExecutionStrategy.completeValueForList(ExecutionStrategy.java:543)
	at graphql.execution.ExecutionStrategy.completeValue(ExecutionStrategy.java:469)
	at graphql.execution.ExecutionStrategy.completeField(ExecutionStrategy.java:435)
	at graphql.execution.ExecutionStrategy.lambda$resolveFieldWithInfo$1(ExecutionStrategy.java:215)
	at java.base/java.util.concurrent.CompletableFuture.uniApplyNow(CompletableFuture.java:684)
	at java.base/java.util.concurrent.CompletableFuture.uniApplyStage(CompletableFuture.java:662)
	at java.base/java.util.concurrent.CompletableFuture.thenApply(CompletableFuture.java:2168)
	at graphql.execution.ExecutionStrategy.resolveFieldWithInfo(ExecutionStrategy.java:214)
	at graphql.execution.AsyncExecutionStrategy.execute(AsyncExecutionStrategy.java:55)
	at graphql.execution.ExecutionStrategy.completeValueForObject(ExecutionStrategy.java:702)
	at graphql.execution.ExecutionStrategy.completeValue(ExecutionStrategy.java:484)
	at graphql.execution.ExecutionStrategy.completeValueForList(ExecutionStrategy.java:586)
	at graphql.execution.ExecutionStrategy.completeValueForList(ExecutionStrategy.java:543)
	at graphql.execution.ExecutionStrategy.completeValue(ExecutionStrategy.java:469)
	at graphql.execution.ExecutionStrategy.completeField(ExecutionStrategy.java:435)
	at graphql.execution.ExecutionStrategy.lambda$resolveFieldWithInfo$1(ExecutionStrategy.java:215)
	at java.base/java.util.concurrent.CompletableFuture.uniApplyNow(CompletableFuture.java:684)
	at java.base/java.util.concurrent.CompletableFuture.uniApplyStage(CompletableFuture.java:662)
	at java.base/java.util.concurrent.CompletableFuture.thenApply(CompletableFuture.java:2168)
	at graphql.execution.ExecutionStrategy.resolveFieldWithInfo(ExecutionStrategy.java:214)
	at graphql.execution.AsyncExecutionStrategy.execute(AsyncExecutionStrategy.java:55)
	at graphql.execution.ExecutionStrategy.completeValueForObject(ExecutionStrategy.java:702)
	at graphql.execution.ExecutionStrategy.completeValue(ExecutionStrategy.java:484)
	at graphql.execution.ExecutionStrategy.completeField(ExecutionStrategy.java:435)
	at graphql.execution.ExecutionStrategy.lambda$resolveFieldWithInfo$1(ExecutionStrategy.java:215)
	at java.base/java.util.concurrent.CompletableFuture.uniApplyNow(CompletableFuture.java:684)
	at java.base/java.util.concurrent.CompletableFuture.uniApplyStage(CompletableFuture.java:662)
	at java.base/java.util.concurrent.CompletableFuture.thenApply(CompletableFuture.java:2168)
	at graphql.execution.ExecutionStrategy.resolveFieldWithInfo(ExecutionStrategy.java:214)
	at graphql.execution.AsyncExecutionStrategy.execute(AsyncExecutionStrategy.java:55)
	at graphql.execution.Execution.executeOperation(Execution.java:161)
	at graphql.execution.Execution.execute(Execution.java:103)
	at graphql.GraphQL.execute(GraphQL.java:565)
	at graphql.GraphQL.lambda$parseValidateAndExecute$12(GraphQL.java:484)
	at java.base/java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:1187)
	at java.base/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:2309)
	at graphql.GraphQL.parseValidateAndExecute(GraphQL.java:479)
	at graphql.GraphQL.executeAsync(GraphQL.java:438)
	at graphql.GraphQL.execute(GraphQL.java:365)
	at graphql.GraphQL.execute(GraphQL.java:335)
	at graphql.MonProjet.invoke(MonProjet.kt:189)
	at graphql.MonProjet.invoke(MonProjet.kt:78)
	at org.http4k.routing.GraphQLExtensionsKt$graphQL$4.invoke(graphQLExtensions.kt:38)
	at org.http4k.routing.GraphQLExtensionsKt$graphQL$4.invoke(graphQLExtensions.kt:36)
	at http.AuthTokenKey2AuthContextFilter$invoke$2$1.invoke(AuthTokenKey2AuthContextFilter.kt:29)
	at http.AuthTokenKey2AuthContextFilter$invoke$2$1.invoke(AuthTokenKey2AuthContextFilter.kt:27)
	at org.http4k.filter.ServerFilters$InitialiseRequestContext$invoke$1$1.invoke(ServerFilters.kt:345)
	at org.http4k.filter.ServerFilters$InitialiseRequestContext$invoke$1$1.invoke(ServerFilters.kt:342)
	at org.http4k.core.Http4kKt$then$2$1.invoke(Http4k.kt:15)
	at org.http4k.core.Http4kKt$then$2$1.invoke(Http4k.kt:15)
	at org.http4k.filter.ResponseFilters$ReportHttpTransaction$invoke$2$1.invoke(ResponseFilters.kt:48)
	at org.http4k.filter.ResponseFilters$ReportHttpTransaction$invoke$2$1.invoke(ResponseFilters.kt:46)
	at org.http4k.filter.ResponseFilters$GZip$invoke$1$1.invoke(ResponseFilters.kt:108)
	at org.http4k.filter.ResponseFilters$GZip$invoke$1$1.invoke(ResponseFilters.kt:107)
	at org.http4k.filter.ServerFilters$CatchLensFailure$2$1.invoke(ServerFilters.kt:229)
	at org.http4k.filter.ServerFilters$CatchLensFailure$2$1.invoke(ServerFilters.kt:227)
	at org.http4k.filter.ServerFilters$Cors$invoke$1$1.invoke(ServerFilters.kt:56)
	at org.http4k.filter.ServerFilters$Cors$invoke$1$1.invoke(ServerFilters.kt:55)
	at org.http4k.core.Http4kKt$then$2$1.invoke(Http4k.kt:15)
	at org.http4k.core.Http4kKt$then$2$1.invoke(Http4k.kt:15)
	at org.http4k.filter.DebuggingFilters$PrintResponse$invoke$1$1.invoke(DebuggingFilters.kt:40)
	at org.http4k.filter.DebuggingFilters$PrintResponse$invoke$1$1.invoke(DebuggingFilters.kt:38)
	at org.http4k.filter.RequestFilters$Tap$invoke$1$1.invoke(RequestFilters.kt:22)
	at org.http4k.filter.RequestFilters$Tap$invoke$1$1.invoke(RequestFilters.kt:20)
	at org.http4k.core.Http4kKt$then$2$1.invoke(Http4k.kt:15)
	at org.http4k.core.Http4kKt$then$2$1.invoke(Http4k.kt:15)
	at org.http4k.routing.TemplateRouter$match$1.invoke(Router.kt:154)
	at org.http4k.routing.TemplateRouter$match$1.invoke(Router.kt:154)
	at org.http4k.routing.RouterMatch$MatchingHandler.invoke(Router.kt:54)
	at org.http4k.routing.RouterMatch$MatchingHandler.invoke(Router.kt:53)
	at org.http4k.routing.RouterBasedHttpHandler.invoke(RouterBasedHttpHandler.kt:19)
	at org.http4k.routing.RouterBasedHttpHandler.invoke(RouterBasedHttpHandler.kt:13)
	at org.http4k.filter.ServerFilters$CatchAll$invoke$2$1.invoke(ServerFilters.kt:284)
	at org.http4k.filter.ServerFilters$CatchAll$invoke$2$1.invoke(ServerFilters.kt:282)
	at org.http4k.core.Http4kKt$then$2$1.invoke(Http4k.kt:15)
	at org.http4k.core.Http4kKt$then$2$1.invoke(Http4k.kt:15)
	at org.http4k.server.Http4kUndertowHttpHandler.handleRequest(Http4kUndertowHttpHandler.kt:41)
	at io.undertow.server.Connectors.executeRootHandler(Connectors.java:393)
	at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:859)
	at org.jboss.threads.ContextHandler$1.runWith(ContextHandler.java:18)
	at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2513)
	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1538)
	at org.xnio.XnioWorker$WorkerThreadFactory$1$1.run(XnioWorker.java:1282)
	at java.base/java.lang.Thread.run(Thread.java:833)
@dondonz dondonz added the keep-open Tells Stale Bot to keep PRs and issues open label Feb 11, 2024
@sperochon
Copy link

Same problem. I have a mutation that returns a Payload which contains nullable fields. One of them is of type ID

@bbakerman
Copy link
Member

On the original problem we have the error message

(n.g.e.ExecutionStrategy) WARN  Can't serialize value (/my/object[0]/kotlin[0]/id) : Expected type 'ID' but was 'ID'.
graphql.schema.CoercingSerializeException: Expected type 'ID' but was 'ID'.

Looking at the code it has

    private String serializeImpl(Object input, @NotNull Locale locale) {
        String result = String.valueOf(input);
        if (result == null) {
            throw new CoercingSerializeException(
                    "Expected type 'ID' but was '" + typeName(input) + "'."
            );
        }

   public static String typeName(Object input) {
        if (input == null) {
            return "null";
        }

        return input.getClass().getSimpleName();
    }

So in order to get Expected type 'ID' but was 'ID' then we must have a non null object - some object where String result = String.valueOf(input); end up returned null but the input was not null. The name of the class must be ID too.

This means that the ID class has a toString() method that returns null.

This is the Java String.valueOf() method

  public static String valueOf(Object obj) {
        return (obj == null) ? "null" : obj.toString();
    }

So somehow this ID class is returning null when .toString() is called on it

The Scalar ID will only be called on non null obejcts. If the id field was truly null at the data fetcher level, then the engine itself will produce a null value and will not call the ID scalar code to serlialise.

So in summary

  • the non null value is being returned
  • its of a class with a simple name of ID
  • its .toString() is return null

I think to fix it - this null returning ID value should not be used and rather a true null should be returned

@sperochon
Copy link

@bbakerman thanks for your quick and great answer.

My app is in Kotlin. Here is my GraphQL payload type:

data class EventCreatePayload(
  val recordId: ID?,
  ...
)

The "ID" type is this one:
https://github.com/ExpediaGroup/graphql-kotlin/blob/master/generator/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/scalars/ID.kt

@JvmInline
value class ID(val value: String) {
    override fun toString(): String = value
}

So, in this case, do you mean that this issue is more a "graphql-kotlin" lib issue (that should send a null rather than a somehow ID(null)) than a "graphql-java" issue?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
keep-open Tells Stale Bot to keep PRs and issues open
Projects
None yet
Development

No branches or pull requests

5 participants
@bbakerman @sperochon @dondonz @AlexandreMaul and others