spring-boot
spring-boot copied to clipboard
GraphQL schema files are not found when a root containing a graphql package appears on the classpath before the root that contains the schema files
Version Spring Boot 2.7.1
Description
We created a new itest
source set in Gradle and when we run the test using @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
, seems like the autoconfigure GraphQL components are not provisioned.
How to reproduce it This project contains a pretty simple GraphQL controller, with two tests:
- One inside the
test
source set, using RestAssured to call the GraphQL endpoint, which works fine (HTTP 200) - One inside the
itest
source set, using RestAssured to call the GraphQL endpoint, which doesn't work (HTTP 404)
Clues
I did some debugging and found that ConditionalOnGraphQlSchema
returns true for the test
source set, and false for the itest
source set, it seems like this line on DefaultGraphQlSchemaCondition
cannot find the GraphQL schema, and therefore, Spring Boot doesn't contribute the Graphql autoconfiguration.
My guess is this issue might be for Spring Boot and not Gradle configuration, as other resources work fine, like liquibase changelogs and application.yml files.
It's a classpath ordering problem. Your itest
task has the project's main resources on its classpath after all of its dependencies whereas the test
task has the project's main resources on its classpath before all of its dependencies. This ordering matters because the graphql-java
jar contains a graphql
package. When it's on the classpath before the project's main resources, this package prevents the graphql/schema.graphqls
file from being found as it ends up looking inside graphql-java
for it as that's the first occurrence of graphql/
on the classpath.
You can fix the problem by creating your itest
source set like this:
sourceSets {
create("itest") {
compileClasspath = sourceSets.main.get().output + compileClasspath
runtimeClasspath = sourceSets.main.get().output + runtimeClasspath
}
}
I'm going to leave this issue open as I think we should consider a different default schema location. "classpath*:graphql/**/"
would be more robust, although it may be quite slow depending on the size of the classpath.
What do you think, @bclozel and @rstoyanchev?
This might be related to https://github.com/spring-projects/spring-graphql/issues/308#issuecomment-1058141073
Thanks for the link, Brian. That's the same problem indeed, just with a different source of the unwanted graphql
package that stops the file from being found.
That's the same problem indeed, just with a different source of the unwanted graphql package that stops the file from being found.
As of https://github.com/spring-projects/spring-graphql/issues/338, we look in different places for schema vs test request vs client request files, which eliminates a very basic reason to shadowing graphql/
under main and test sources that most apps would run into otherwise.
For the rest, using classpath*:
by default will find all possible candidates out of the box, including some that may not be intended for inclusion, while classpath:
requires an explicit action to decide how to deal with such shadowing. I tend to think the more explicit opt-in option is preferable. I've already made an adjustment to the Spring for GraphQL docs to make that easier to spot.
Thanks Rossen, I'll add a similar note in the Spring Boot reference docs, I'll turn this into a documentation issue then.