Please make sure that a spring GraphQL GraphQlSourceBuilderCustomizer that registers a Resource for the schema doesn't cause an error if the same schema is discovered in scanning

Comment From: sdeleuze

I suspect we miss a if (AotDetector.useGeneratedArtifacts()) check to avoid double processing of GraphQlSourceBuilderCustomizer in AOT mode as done on https://github.com/spring-projects/spring-boot/blob/b78e7b5ac72a2a8351e954f7b07e7ee02d530355/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/client/TestRestTemplateContextCustomizer.java#L56. May impact other customizers too.

Comment From: mhalbritter

This double-executing was the cause of https://github.com/spring-projects/spring-boot/issues/32859, too.

Comment From: philwebb

@joshlong Have you a sample the replicates the problem? Or a stacktrace? Is it AOT specific?

Comment From: sdeleuze

I think the repro is https://github.com/joshlong/spring-boot-3-aot-graphql.

Comment From: mhalbritter

This is not related to AOT.

In the repro project, there's a

    @Bean
    GraphQlSourceBuilderCustomizer graphQlSourceBuilderCustomizer() {
        return builder -> builder.schemaResources(SCHEMA_RESOURCE);
    }

which registers /graphql/schema.graphqls. The same schema is picked up by the automatic scanning in GraphQlAutoConfiguration#graphQlSource, essentially registering it twice. The startup then fails with:

Caused by: SchemaProblem{errors=['Customer' type [@1:1] tried to redefine existing 'Customer' type [@1:1], 'Query' type [@6:1] tried to redefine existing 'Query' type [@6:1]]}
    at graphql.schema.idl.TypeDefinitionRegistry.merge(TypeDefinitionRegistry.java:106)
    at java.base/java.util.stream.ReduceOps$2ReducingSink.accept(ReduceOps.java:123)
    at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
    at java.base/java.util.Iterator.forEachRemaining(Iterator.java:133)
    at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1845)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
    at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.base/java.util.stream.ReferencePipeline.reduce(ReferencePipeline.java:662)
    at org.springframework.graphql.execution.DefaultSchemaResourceGraphQlSourceBuilder.initGraphQlSchema(DefaultSchemaResourceGraphQlSourceBuilder.java:98)
    at org.springframework.graphql.execution.AbstractGraphQlSourceBuilder.build(AbstractGraphQlSourceBuilder.java:93)
    at org.springframework.boot.autoconfigure.graphql.GraphQlAutoConfiguration.graphQlSource(GraphQlAutoConfiguration.java:94)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:139)
    ... 159 more

The automatic scanning for resources can be disabled by setting spring.graphql.schema.locations=.

We have to decide if we want to deduplicate the resources registered on the SchemaResourceBuilder or if this is considered a user made error.

What's your take on this, @bclozel?

Comment From: mhalbritter

We discussed it today and we don't think we should deduplicate the resource files.