From 648fac2f5e91653935a1c4bb3daa22b3a02460a7 Mon Sep 17 00:00:00 2001 From: Oryan Date: Mon, 12 Oct 2026 15:01:20 -0400 Subject: [PATCH 1/8] Fix compiler warnings - Require org.jspecify (static) in module-info. graphql-java's @Nullable annotations were unreadable from the module, which Kotlin 2.4 turns into an error (KT-80247). This is what broke the Kotlin 2.4 update. - Hide introspection with BlockedFields instead of the deprecated NoIntrospectionGraphqlFieldVisibility, which does the same thing. Its suggested replacement, Introspection.enabledJvmWide, is JVM-wide. - Drop @ExperimentalCoroutinesApi from SchemaParserOptions.build() and defaultOptions(). Nothing in them is experimental anymore, and Kotlin callers no longer need to opt in. - Suppress the deprecation on the legacy directive argument values and the safe call on the schema, which is null in hand-built environments. - Use Kotlin class literals and drop a redundant cast; fix test warnings. Co-Authored-By: Claude Opus 5.5 --- .../graphql/kickstart/tools/SchemaClassScanner.kt | 10 +++++----- .../kotlin/graphql/kickstart/tools/SchemaParser.kt | 9 ++++++--- .../graphql/kickstart/tools/SchemaParserOptions.kt | 3 --- .../kotlin/graphql/kickstart/tools/TypeClassMatcher.kt | 2 +- .../kickstart/tools/resolver/FieldResolverScanner.kt | 4 ++-- .../kickstart/tools/resolver/MapFieldResolver.kt | 2 +- .../kickstart/tools/resolver/MethodFieldResolver.kt | 2 ++ src/main/kotlin/module-info.java | 2 ++ .../kotlin/graphql/kickstart/tools/EndToEndTest.kt | 8 ++++---- .../tools/MethodFieldResolverDataFetcherTest.kt | 10 ++++++---- .../graphql/kickstart/tools/MethodFieldResolverTest.kt | 10 +++++----- src/test/kotlin/graphql/kickstart/tools/TestUtils.kt | 2 +- .../graphql/kickstart/tools/util/PrimitivesTest.kt | 2 +- 13 files changed, 36 insertions(+), 30 deletions(-) diff --git a/src/main/kotlin/graphql/kickstart/tools/SchemaClassScanner.kt b/src/main/kotlin/graphql/kickstart/tools/SchemaClassScanner.kt index 8844ba6b..24eac040 100644 --- a/src/main/kotlin/graphql/kickstart/tools/SchemaClassScanner.kt +++ b/src/main/kotlin/graphql/kickstart/tools/SchemaClassScanner.kt @@ -198,8 +198,8 @@ internal class SchemaClassScanner( dictionary .filter { it.value.javaType != null - && it.value.typeClass() != java.lang.Object::class.java - && !java.util.Map::class.java.isAssignableFrom(it.value.typeClass()) + && it.value.typeClass() != Any::class.java + && !Map::class.java.isAssignableFrom(it.value.typeClass()) && it.key !is InputObjectTypeDefinition && it.key !is UnionTypeDefinition }.mapValuesTo(it) { it.value.javaType } @@ -313,7 +313,7 @@ internal class SchemaClassScanner( * Scan a new object for types that haven't been mapped yet. */ private fun scanQueueItemForPotentialMatches(item: QueueItem) { - val resolverInfo = if (item.clazz == Object::class.java) { + val resolverInfo = if (item.clazz == Any::class.java) { getResolverInfoFromTypeDictionary(item.type.name) ?: throw SchemaClassScannerError("The GraphQL schema type '${item.type.name}' maps to a field of type java.lang.Object however there is no matching entry for this type in the type dictionary. You may need to add this type to the dictionary before building the schema.") } else { @@ -349,7 +349,7 @@ internal class SchemaClassScanner( } private fun isResolverForSupertype(resolverInfo: NormalResolverInfo, dataClass: Class<*>) = - resolverInfo.dataClassType != Object::class.java && resolverInfo.dataClassType.isAssignableFrom(dataClass) + resolverInfo.dataClassType != Any::class.java && resolverInfo.dataClassType.isAssignableFrom(dataClass) private fun scanResolverInfoForPotentialMatches(type: ObjectTypeDefinition, resolverInfo: ResolverInfo) { type.getExtendedFieldDefinitions(extensionDefinitions).forEach { field -> @@ -386,7 +386,7 @@ internal class SchemaClassScanner( if (options.preferGraphQLResolver && realEntry.hasResolverRef()) { log.warn("The real entry ${realEntry.joinReferences()} is a GraphQLResolver so ignoring this one ${javaType.unwrap()} $reference") } else { - if (java.util.Map::class.java.isAssignableFrom(javaType.unwrap())) { + if (Map::class.java.isAssignableFrom(javaType.unwrap())) { throw SchemaClassScannerError("Two different property map classes used for type ${type.name}:\n${realEntry.joinReferences()}\n\n- ${javaType}:\n| ${reference.getDescription()}") } throw SchemaClassScannerError("Two different classes used for type ${type.name}:\n${realEntry.joinReferences()}\n\n- ${javaType.typeName}:\n| ${reference.getDescription()}") diff --git a/src/main/kotlin/graphql/kickstart/tools/SchemaParser.kt b/src/main/kotlin/graphql/kickstart/tools/SchemaParser.kt index 7417e54c..3ce9d50a 100644 --- a/src/main/kotlin/graphql/kickstart/tools/SchemaParser.kt +++ b/src/main/kotlin/graphql/kickstart/tools/SchemaParser.kt @@ -12,7 +12,7 @@ import graphql.language.* import graphql.schema.* import graphql.schema.idl.RuntimeWiring import graphql.schema.idl.ScalarInfo -import graphql.schema.visibility.NoIntrospectionGraphqlFieldVisibility +import graphql.schema.visibility.BlockedFields import org.slf4j.LoggerFactory import kotlin.reflect.KClass @@ -68,7 +68,9 @@ class SchemaParser internal constructor( */ fun parseSchemaObjects(): SchemaObjects { if (!options.introspectionEnabled) { - codeRegistryBuilder.fieldVisibility(NoIntrospectionGraphqlFieldVisibility.NO_INTROSPECTION_FIELD_VISIBILITY) + // What the deprecated NoIntrospectionGraphqlFieldVisibility does. Its suggested replacement, + // Introspection.enabledJvmWide, would also turn introspection off for every other schema in the JVM. + codeRegistryBuilder.fieldVisibility(BlockedFields.newBlock().addPattern("__.*").build()) } // this overrides the above introspection enabled setting obviously... todo: add documentation options.fieldVisibility?.let { codeRegistryBuilder.fieldVisibility(it) } @@ -427,6 +429,7 @@ class SchemaParser internal constructor( } // TODO remove this once directives are fully replaced with applied directives + @Suppress("DEPRECATION") // legacy directive argument values private fun buildDirectives( directives: List, directiveLocation: Introspection.DirectiveLocation @@ -529,7 +532,7 @@ class SchemaParser internal constructor( is NonNullType -> GraphQLNonNull(determineType(expectedType, typeDefinition.type, allowedTypeReferences, inputObjects)) is InputObjectTypeDefinition -> { log.info("Create input object") - createInputObject(typeDefinition, inputObjects, referencingInputObjects as MutableSet) + createInputObject(typeDefinition, inputObjects, referencingInputObjects) } is TypeName -> { val scalarType = customScalars[typeDefinition.name] diff --git a/src/main/kotlin/graphql/kickstart/tools/SchemaParserOptions.kt b/src/main/kotlin/graphql/kickstart/tools/SchemaParserOptions.kt index 8b0e0c60..d1c6a5e8 100644 --- a/src/main/kotlin/graphql/kickstart/tools/SchemaParserOptions.kt +++ b/src/main/kotlin/graphql/kickstart/tools/SchemaParserOptions.kt @@ -10,7 +10,6 @@ import graphql.schema.DataFetcher import graphql.schema.DataFetchingEnvironment import graphql.schema.visibility.GraphqlFieldVisibility import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.channels.ReceiveChannel import kotlinx.coroutines.reactive.publish import org.reactivestreams.Publisher @@ -45,7 +44,6 @@ data class SchemaParserOptions internal constructor( fun newOptions() = Builder() @JvmStatic - @ExperimentalCoroutinesApi fun defaultOptions() = Builder().build() } @@ -164,7 +162,6 @@ data class SchemaParserOptions internal constructor( this.allowUndeclaredDirectives = allowUndeclaredDirectives } - @ExperimentalCoroutinesApi fun build(): SchemaParserOptions { val coroutineContextProvider = coroutineContextProvider ?: DefaultCoroutineContextProvider(Dispatchers.Default) diff --git a/src/main/kotlin/graphql/kickstart/tools/TypeClassMatcher.kt b/src/main/kotlin/graphql/kickstart/tools/TypeClassMatcher.kt index b6e4096e..b24b7b04 100644 --- a/src/main/kotlin/graphql/kickstart/tools/TypeClassMatcher.kt +++ b/src/main/kotlin/graphql/kickstart/tools/TypeClassMatcher.kt @@ -68,7 +68,7 @@ internal class TypeClassMatcher(private val definitionsByName: Map && realType.isArray -> match(potentialMatch, graphQLType.type, realType.componentType) // e.g. a property map value: the element type is unknown, so match it as Object too - realType == Object::class.java -> + realType == Any::class.java -> match(potentialMatch, graphQLType.type, realType) else -> throw error(potentialMatch, "Java class is not a List or generic type information was lost: $realType") diff --git a/src/main/kotlin/graphql/kickstart/tools/resolver/FieldResolverScanner.kt b/src/main/kotlin/graphql/kickstart/tools/resolver/FieldResolverScanner.kt index d787ef6d..34e5b282 100644 --- a/src/main/kotlin/graphql/kickstart/tools/resolver/FieldResolverScanner.kt +++ b/src/main/kotlin/graphql/kickstart/tools/resolver/FieldResolverScanner.kt @@ -56,7 +56,7 @@ internal class FieldResolverScanner(val options: SchemaParserOptions) { } } - if (java.util.Map::class.java.isAssignableFrom(search.type.unwrap())) { + if (Map::class.java.isAssignableFrom(search.type.unwrap())) { return MapFieldResolver(field, search, options, search.type.unwrap()) } @@ -128,7 +128,7 @@ internal class FieldResolverScanner(val options: SchemaParserOptions) { .filter { !Modifier.isPrivate(it.modifiers) } // discard any methods that are coming off the root of the class hierarchy // to avoid issues with duplicate method declarations - .filter { it.declaringClass != Object::class.java } + .filter { it.declaringClass != Any::class.java } // subscription resolvers must return a publisher .filter { !search.isSubscription || resolverMethodReturnsPublisher(it) } .toList() diff --git a/src/main/kotlin/graphql/kickstart/tools/resolver/MapFieldResolver.kt b/src/main/kotlin/graphql/kickstart/tools/resolver/MapFieldResolver.kt index 70c6d41b..0eea1c1d 100644 --- a/src/main/kotlin/graphql/kickstart/tools/resolver/MapFieldResolver.kt +++ b/src/main/kotlin/graphql/kickstart/tools/resolver/MapFieldResolver.kt @@ -36,7 +36,7 @@ internal class MapFieldResolver( val resolvedType = TypeResolver().resolve(mapClass) val typeParameters = resolvedType.typeParametersFor(Map::class.java) - return typeParameters.elementAtOrElse(1) { Object::class.java } + return typeParameters.elementAtOrElse(1) { Any::class.java } } override fun createDataFetcher(): DataFetcher<*> { diff --git a/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt b/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt index 0a9e65d6..0ac8612c 100644 --- a/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt +++ b/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt @@ -152,6 +152,8 @@ internal class MethodFieldResolver( * because values can be coerced to multiple different Java types (eg. String, Long, UUID). All values of a non-concrete scalar * type must be converted to the target method parameter type. */ + // graphql-java marks the schema non-null, but it's null in hand-built environments, e.g. in tests + @Suppress("UNNECESSARY_SAFE_CALL") private fun isConcreteScalarType(environment: DataFetchingEnvironment, type: Type<*>, genericParameterType: JavaType): Boolean { return when (type) { is ListType -> List::class.java.isAssignableFrom(this.typeResolver.getRawClass(genericParameterType)) diff --git a/src/main/kotlin/module-info.java b/src/main/kotlin/module-info.java index 2ec2dd1b..90ef1592 100644 --- a/src/main/kotlin/module-info.java +++ b/src/main/kotlin/module-info.java @@ -5,6 +5,8 @@ requires kotlinx.coroutines.reactive; requires com.graphqljava; + // graphql-java's nullability annotations, only needed at compile time + requires static org.jspecify; requires com.fasterxml.jackson.core; requires com.fasterxml.jackson.kotlin; diff --git a/src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt b/src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt index 9539a7b1..ee022f8d 100644 --- a/src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt @@ -79,7 +79,7 @@ class EndToEndTest { .graphQLContext(mapOf("newItem" to newItem)) .variables(mapOf())) - val data = result.getData() as Publisher + val data = result.getData>()!! val latch = CountDownLatch(1) data.subscribe(object : Subscriber { override fun onNext(item: ExecutionResult?) { @@ -117,7 +117,7 @@ class EndToEndTest { .graphQLContext(mapOf("newItem" to newItem)) .variables(mapOf())) - val data = result.getData() as Publisher + val data = result.getData>()!! val latch = CountDownLatch(1) data.subscribe(object : Subscriber { override fun onNext(item: ExecutionResult?) { @@ -639,7 +639,7 @@ class EndToEndTest { .graphQLContext(mapOf("newItem" to newItem)) .variables(mapOf())) - val data = result.getData() as Publisher + val data = result.getData>()!! val subscriber = TestEnvironment().newManualSubscriber(data) val subscriberResult = subscriber.requestNextElement() as ExecutionResultImpl @@ -667,7 +667,7 @@ class EndToEndTest { .graphQLContext(mapOf("newItem" to newItem)) .variables(mapOf())) - val data = result.getData() as Publisher + val data = result.getData>()!! val subscriber = TestEnvironment().newManualSubscriber(data) val subscriberResult = subscriber.requestNextElement() as ExecutionResultImpl diff --git a/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverDataFetcherTest.kt b/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverDataFetcherTest.kt index f163ee6b..e539b0e3 100644 --- a/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverDataFetcherTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverDataFetcherTest.kt @@ -14,6 +14,7 @@ import graphql.schema.DataFetcher import graphql.schema.DataFetchingEnvironment import graphql.schema.DataFetchingEnvironmentImpl import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job @@ -124,6 +125,7 @@ class MethodFieldResolverDataFetcherTest { } @ExperimentalCoroutinesApi + @OptIn(DelicateCoroutinesApi::class) @Test fun `canceling subscription Publisher also cancels underlying Kotlin coroutine channel`() { // setup @@ -236,7 +238,7 @@ class MethodFieldResolverDataFetcherTest { @Test fun `data fetcher passes environment if method has extra argument`() { val resolver = createFetcher("active", object : GraphQLResolver { - fun isActive(dataClass: DataClass, env: DataFetchingEnvironment): Boolean = env is DataFetchingEnvironment + fun isActive(dataClass: DataClass, env: DataFetchingEnvironment?): Boolean = env != null }) assertEquals(resolver.get(createEnvironment(DataClass())), true) @@ -246,7 +248,7 @@ class MethodFieldResolverDataFetcherTest { fun `data fetcher passes environment if method has extra argument even if context is specified`() { val context = GraphQLContext.newContext().build() val resolver = createFetcher("active", resolver = object : GraphQLResolver { - fun isActive(dataClass: DataClass, env: DataFetchingEnvironment): Boolean = env is DataFetchingEnvironment + fun isActive(dataClass: DataClass, env: DataFetchingEnvironment?): Boolean = env != null }) assertEquals(resolver.get(createEnvironment(DataClass(), context = context)), true) @@ -320,7 +322,7 @@ class MethodFieldResolverDataFetcherTest { @Test fun `data fetcher passes environment to extension function if method has extra argument`() { val resolver = createFetcher("active", object : GraphQLResolver { - fun DataClass.isActive(env: DataFetchingEnvironment): Boolean = env is DataFetchingEnvironment + fun DataClass.isActive(env: DataFetchingEnvironment?): Boolean = env != null }) assertEquals(resolver.get(createEnvironment(DataClass())), true) @@ -411,7 +413,7 @@ class MethodFieldResolverDataFetcherTest { } private fun createEnvironment( - source: Any = Object(), + source: Any = Any(), arguments: Map = emptyMap(), context: GraphQLContext = GraphQLContext.newContext().build(), legacyContext: Any? = null diff --git a/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverTest.kt b/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverTest.kt index a59503df..a02ef092 100644 --- a/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverTest.kt @@ -51,7 +51,7 @@ class MethodFieldResolverTest { testNull(input: null) } """) - .root(Object())) + .root(Any())) assertEquals(result.getData(), mapOf( "testValue" to "Optional[test-value]", @@ -93,7 +93,7 @@ class MethodFieldResolverTest { testNull(input: null) } """) - .root(Object())) + .root(Any())) assertEquals(result.getData(), mapOf( "testValue" to "Optional[test-value]", @@ -128,7 +128,7 @@ class MethodFieldResolverTest { } """) .variables(mapOf("input" to "FooBar")) - .root(Object())) + .root(Any())) assertEquals(result.getData(), mapOf("test" to 6)) } @@ -159,7 +159,7 @@ class MethodFieldResolverTest { } """) .variables(mapOf("input" to listOf("Foo", "Bar"))) - .root(Object())) + .root(Any())) assertEquals(result.getData(), mapOf("test" to 6)) } @@ -206,7 +206,7 @@ class MethodFieldResolverTest { } """) .variables(mapOf("input" to listOf("Foo", "Bar"))) - .root(Object())) + .root(Any())) assertEquals(result.getData(), mapOf("test" to 6)) } diff --git a/src/test/kotlin/graphql/kickstart/tools/TestUtils.kt b/src/test/kotlin/graphql/kickstart/tools/TestUtils.kt index 33da8b0b..1180fc72 100644 --- a/src/test/kotlin/graphql/kickstart/tools/TestUtils.kt +++ b/src/test/kotlin/graphql/kickstart/tools/TestUtils.kt @@ -19,7 +19,7 @@ fun assertNoGraphQlErrors(gql: GraphQL, args: Map = mapOf(), contex throw AssertionError("GraphQL result contained errors!\n${result.errors.map { it.message }.joinToString("\n")}") } - return result.getData() as Map + return result.getData>()!! } fun assertEquals(actual: T, expected: T) { diff --git a/src/test/kotlin/graphql/kickstart/tools/util/PrimitivesTest.kt b/src/test/kotlin/graphql/kickstart/tools/util/PrimitivesTest.kt index 6856e838..1c227832 100644 --- a/src/test/kotlin/graphql/kickstart/tools/util/PrimitivesTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/util/PrimitivesTest.kt @@ -8,7 +8,7 @@ class PrimitivesTest { @Test fun `should wrap primitive Java class`() { val clazz = Primitives.wrap(Int::class.javaPrimitiveType) - Assert.assertEquals(Integer::class.java, clazz) + Assert.assertEquals(Int::class.javaObjectType, clazz) } @Test From b59578041c8d5c1ab417a3e400eae2044735de12 Mon Sep 17 00:00:00 2001 From: Oryan Date: Mon, 12 Oct 2026 15:01:20 -0400 Subject: [PATCH 2/8] Fail the build on compiler warnings Pass -Werror to kotlinc and failOnWarning to javac, so warnings show up in mvn verify instead of later in Sonar or a dependency update. javac now uses release 11, which also drops its "location of system modules" warning. Co-Authored-By: Claude Opus 5.5 --- pom.xml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c49370d1..98921e13 100644 --- a/pom.xml +++ b/pom.xml @@ -262,6 +262,11 @@ kotlin-maven-plugin org.jetbrains.kotlin ${kotlin.version} + + + -Werror + + compile @@ -286,8 +291,8 @@ maven-compiler-plugin 3.16.0 - 11 - 11 + 11 + true From 146f4d511dbfd32e79dfffb12a30f48dd59c2b0a Mon Sep 17 00:00:00 2001 From: Oryan Date: Mon, 12 Oct 2026 15:01:20 -0400 Subject: [PATCH 3/8] Build against graphql-java 25 and check the release bundle in pull requests - graphql-java-25: builds and tests against graphql-java 25.1, the oldest version this jar supports, e.g. to catch APIs that only exist in 26 like Directives.BUILT_IN_DIRECTIVES_MAP (6f78f35). - release-bundle: builds the Maven Central bundle with the release profile and Maven 3.9, like the release workflow, without uploading it. It fails if the bundle has files Central rejects (e.g. maven-metadata-local.xml under Maven 3.10) or is missing the pom, jar, sources or javadoc jar. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/pull-request.yml | 78 ++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 85149530..731badef 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -24,3 +24,81 @@ jobs: restore-keys: ${{ runner.os }}-m2 - name: Build with Maven run: mvn --batch-mode --update-snapshots verify + + # The same jar has to work with the oldest supported graphql-java, so build and test against it too + graphql-java-25: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v7 + - name: Setup java + uses: actions/setup-java@v6 + with: + java-version: '11' + distribution: 'temurin' + - name: Cache Maven packages + uses: actions/cache@v6 + with: + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2 + - name: Build with Maven + run: mvn --batch-mode --update-snapshots verify -Dgraphql-java.version=25.1 + + # Builds the Maven Central bundle like the release workflow does, without uploading it + release-bundle: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v7 + - name: Setup java + uses: actions/setup-java@v6 + with: + java-version: '11' + distribution: 'temurin' + server-id: central + server-username: MAVEN_USERNAME + server-password: MAVEN_PASSWORD + - name: Setup Maven 3.9 + # Same Maven as the release workflow + run: | + curl -sSfL https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.9.16/apache-maven-3.9.16-bin.tar.gz | tar xz -C "$RUNNER_TEMP" + echo "$RUNNER_TEMP/apache-maven-3.9.16/bin" >> "$GITHUB_PATH" + - name: Cache Maven packages + uses: actions/cache@v6 + with: + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2 + - name: Build release bundle + # Snapshots aren't bundled, so drop -SNAPSHOT like github-build.sh does. + # The upload URL is unreachable, so the build fails at the upload, after the bundle is written. + run: | + sed -i 's/-SNAPSHOT//' pom.xml + mvn --batch-mode -Prelease deploy -DskipTests -Dgpg.skip -DcentralBaseUrl=http://127.0.0.1:9 || true + env: + MAVEN_USERNAME: dummy + MAVEN_PASSWORD: dummy + - name: Check release bundle + # Central rejects bundles with files that don't belong to a pom, e.g. maven-metadata-local.xml under Maven 3.10 + run: | + bundle=target/central-publishing/central-bundle.zip + if [[ ! -f $bundle ]]; then + echo "::error::No release bundle was built, see the previous step" + exit 1 + fi + version=$(grep -m1 -o ".*$" pom.xml | awk -F'[><]' '{print $3}') + dir="io/github/graphql-java-kickstart/graphql-java-tools/${version}" + files=$(unzip -Z1 "$bundle") + echo "$files" + unexpected=$(grep -vE "^${dir}/graphql-java-tools-${version}(-[a-z]+)?\.(pom|jar)(\.(md5|sha1|sha256|sha512|asc))?$" <<< "$files" || true) + if [[ -n $unexpected ]]; then + echo "::error::Unexpected files in the release bundle: ${unexpected//$'\n'/, }" + exit 1 + fi + for file in graphql-java-tools-${version}.pom graphql-java-tools-${version}.jar graphql-java-tools-${version}-sources.jar graphql-java-tools-${version}-javadoc.jar; do + if ! grep -qxF "${dir}/${file}" <<< "$files"; then + echo "::error::${file} is missing from the release bundle" + exit 1 + fi + done From 060770fcbf6b57f0ea6aabb0f59d7f7dcbe31dd0 Mon Sep 17 00:00:00 2001 From: Oryan Date: Mon, 12 Oct 2026 15:04:00 -0400 Subject: [PATCH 4/8] Test disabling introspection Nothing covered introspectionEnabled(false), which now uses BlockedFields directly. The same test passes on master. Co-Authored-By: Claude Opus 5.5 --- .../kickstart/tools/IntrospectionTest.kt | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 src/test/kotlin/graphql/kickstart/tools/IntrospectionTest.kt diff --git a/src/test/kotlin/graphql/kickstart/tools/IntrospectionTest.kt b/src/test/kotlin/graphql/kickstart/tools/IntrospectionTest.kt new file mode 100644 index 00000000..fa18e90d --- /dev/null +++ b/src/test/kotlin/graphql/kickstart/tools/IntrospectionTest.kt @@ -0,0 +1,60 @@ +package graphql.kickstart.tools + +import graphql.GraphQL +import graphql.validation.ValidationError +import graphql.validation.ValidationErrorType +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Test + +class IntrospectionTest { + + private fun graphQL(introspectionEnabled: Boolean): GraphQL { + val schema = SchemaParser.newParser() + .schemaString( + """ + type Query { + name: String! + } + """) + .resolvers(object : GraphQLQueryResolver { + fun name() = "tools" + }) + .options(SchemaParserOptions.newOptions().introspectionEnabled(introspectionEnabled).build()) + .build() + .makeExecutableSchema() + return GraphQL.newGraphQL(schema).build() + } + + @Test + fun `introspection is enabled by default`() { + val data = assertNoGraphQlErrors(graphQL(introspectionEnabled = true)) { + "{ __schema { queryType { name } } }" + } + + assertEquals(mapOf("queryType" to mapOf("name" to "Query")), data["__schema"]) + } + + @Test + fun `disabling introspection hides the fields of introspection types`() { + val gql = graphQL(introspectionEnabled = false) + + listOf("{ __schema { queryType { name } } }", "{ __type(name: \"Query\") { name } }").forEach { query -> + val result = gql.execute(query) + + assertNull(result.getData()) + assertEquals( + listOf(ValidationErrorType.FieldUndefined), + result.errors.map { (it as ValidationError).validationErrorType }) + } + } + + @Test + fun `disabling introspection keeps __typename`() { + val data = assertNoGraphQlErrors(graphQL(introspectionEnabled = false)) { + "{ name __typename }" + } + + assertEquals(mapOf("name" to "tools", "__typename" to "Query"), data) + } +} From 8b16ee623558a76c24bd6d89e2c06cf8591bc3c9 Mon Sep 17 00:00:00 2001 From: Oryan Date: Fri, 16 Oct 2026 16:10:52 -0400 Subject: [PATCH 5/8] Drop the release bundle check from pull requests It pins the same Maven as the release workflow, so it can't catch the Maven 3.10 problem it was added for. What's left (javadoc errors, a missing sources or javadoc jar) is rare and fails the release before anything is published or tagged. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/pull-request.yml | 58 ------------------------------ 1 file changed, 58 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 731badef..7ceaf9fe 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -44,61 +44,3 @@ jobs: restore-keys: ${{ runner.os }}-m2 - name: Build with Maven run: mvn --batch-mode --update-snapshots verify -Dgraphql-java.version=25.1 - - # Builds the Maven Central bundle like the release workflow does, without uploading it - release-bundle: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v7 - - name: Setup java - uses: actions/setup-java@v6 - with: - java-version: '11' - distribution: 'temurin' - server-id: central - server-username: MAVEN_USERNAME - server-password: MAVEN_PASSWORD - - name: Setup Maven 3.9 - # Same Maven as the release workflow - run: | - curl -sSfL https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.9.16/apache-maven-3.9.16-bin.tar.gz | tar xz -C "$RUNNER_TEMP" - echo "$RUNNER_TEMP/apache-maven-3.9.16/bin" >> "$GITHUB_PATH" - - name: Cache Maven packages - uses: actions/cache@v6 - with: - path: ~/.m2 - key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} - restore-keys: ${{ runner.os }}-m2 - - name: Build release bundle - # Snapshots aren't bundled, so drop -SNAPSHOT like github-build.sh does. - # The upload URL is unreachable, so the build fails at the upload, after the bundle is written. - run: | - sed -i 's/-SNAPSHOT//' pom.xml - mvn --batch-mode -Prelease deploy -DskipTests -Dgpg.skip -DcentralBaseUrl=http://127.0.0.1:9 || true - env: - MAVEN_USERNAME: dummy - MAVEN_PASSWORD: dummy - - name: Check release bundle - # Central rejects bundles with files that don't belong to a pom, e.g. maven-metadata-local.xml under Maven 3.10 - run: | - bundle=target/central-publishing/central-bundle.zip - if [[ ! -f $bundle ]]; then - echo "::error::No release bundle was built, see the previous step" - exit 1 - fi - version=$(grep -m1 -o ".*$" pom.xml | awk -F'[><]' '{print $3}') - dir="io/github/graphql-java-kickstart/graphql-java-tools/${version}" - files=$(unzip -Z1 "$bundle") - echo "$files" - unexpected=$(grep -vE "^${dir}/graphql-java-tools-${version}(-[a-z]+)?\.(pom|jar)(\.(md5|sha1|sha256|sha512|asc))?$" <<< "$files" || true) - if [[ -n $unexpected ]]; then - echo "::error::Unexpected files in the release bundle: ${unexpected//$'\n'/, }" - exit 1 - fi - for file in graphql-java-tools-${version}.pom graphql-java-tools-${version}.jar graphql-java-tools-${version}-sources.jar graphql-java-tools-${version}-javadoc.jar; do - if ! grep -qxF "${dir}/${file}" <<< "$files"; then - echo "::error::${file} is missing from the release bundle" - exit 1 - fi - done From 82094750423b7bcb690f7a379ab4a789923ae66a Mon Sep 17 00:00:00 2001 From: Oryan Date: Fri, 16 Oct 2026 16:32:11 -0400 Subject: [PATCH 6/8] Add a TODO to deprecate introspectionEnabled Co-Authored-By: Claude Opus 5.5 --- src/main/kotlin/graphql/kickstart/tools/SchemaParser.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/kotlin/graphql/kickstart/tools/SchemaParser.kt b/src/main/kotlin/graphql/kickstart/tools/SchemaParser.kt index 3ce9d50a..745196f8 100644 --- a/src/main/kotlin/graphql/kickstart/tools/SchemaParser.kt +++ b/src/main/kotlin/graphql/kickstart/tools/SchemaParser.kt @@ -70,6 +70,7 @@ class SchemaParser internal constructor( if (!options.introspectionEnabled) { // What the deprecated NoIntrospectionGraphqlFieldVisibility does. Its suggested replacement, // Introspection.enabledJvmWide, would also turn introspection off for every other schema in the JVM. + // TODO deprecate introspectionEnabled and remove this in 15.0.0 (#858) codeRegistryBuilder.fieldVisibility(BlockedFields.newBlock().addPattern("__.*").build()) } // this overrides the above introspection enabled setting obviously... todo: add documentation From b9b3bafa4a2bf9e8706acfbe762b6bb911ab80a4 Mon Sep 17 00:00:00 2001 From: Oryan Date: Fri, 16 Oct 2026 16:40:22 -0400 Subject: [PATCH 7/8] Remove coroutine opt-ins and non-null assertions from tests SchemaParserOptions.build() isn't experimental anymore, so the tests don't need to opt in. Co-Authored-By: Claude Opus 5.5 --- src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt | 8 ++++---- .../graphql/kickstart/tools/FieldResolverScannerTest.kt | 2 -- .../kickstart/tools/MethodFieldResolverDataFetcherTest.kt | 5 ----- .../graphql/kickstart/tools/MethodFieldResolverTest.kt | 2 -- .../graphql/kickstart/tools/MissingFieldResolverTest.kt | 2 -- src/test/kotlin/graphql/kickstart/tools/ReactiveTest.kt | 2 -- .../graphql/kickstart/tools/SchemaClassScannerTest.kt | 2 -- .../kotlin/graphql/kickstart/tools/SchemaParserTest.kt | 2 -- src/test/kotlin/graphql/kickstart/tools/TestUtils.kt | 2 +- .../graphql/kickstart/tools/TypeClassMatcherTest.kt | 2 -- 10 files changed, 5 insertions(+), 24 deletions(-) diff --git a/src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt b/src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt index ee022f8d..85197516 100644 --- a/src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt @@ -79,7 +79,7 @@ class EndToEndTest { .graphQLContext(mapOf("newItem" to newItem)) .variables(mapOf())) - val data = result.getData>()!! + val data = checkNotNull(result.getData>()) val latch = CountDownLatch(1) data.subscribe(object : Subscriber { override fun onNext(item: ExecutionResult?) { @@ -117,7 +117,7 @@ class EndToEndTest { .graphQLContext(mapOf("newItem" to newItem)) .variables(mapOf())) - val data = result.getData>()!! + val data = checkNotNull(result.getData>()) val latch = CountDownLatch(1) data.subscribe(object : Subscriber { override fun onNext(item: ExecutionResult?) { @@ -639,7 +639,7 @@ class EndToEndTest { .graphQLContext(mapOf("newItem" to newItem)) .variables(mapOf())) - val data = result.getData>()!! + val data = checkNotNull(result.getData>()) val subscriber = TestEnvironment().newManualSubscriber(data) val subscriberResult = subscriber.requestNextElement() as ExecutionResultImpl @@ -667,7 +667,7 @@ class EndToEndTest { .graphQLContext(mapOf("newItem" to newItem)) .variables(mapOf())) - val data = result.getData>()!! + val data = checkNotNull(result.getData>()) val subscriber = TestEnvironment().newManualSubscriber(data) val subscriberResult = subscriber.requestNextElement() as ExecutionResultImpl diff --git a/src/test/kotlin/graphql/kickstart/tools/FieldResolverScannerTest.kt b/src/test/kotlin/graphql/kickstart/tools/FieldResolverScannerTest.kt index db917658..01c83fb1 100644 --- a/src/test/kotlin/graphql/kickstart/tools/FieldResolverScannerTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/FieldResolverScannerTest.kt @@ -12,11 +12,9 @@ import graphql.relay.Connection import graphql.relay.DefaultConnection import graphql.relay.DefaultPageInfo import graphql.schema.DataFetchingEnvironment -import kotlinx.coroutines.ExperimentalCoroutinesApi import org.junit.Test import java.util.* -@OptIn(ExperimentalCoroutinesApi::class) class FieldResolverScannerTest { private val options = defaultOptions() diff --git a/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverDataFetcherTest.kt b/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverDataFetcherTest.kt index e539b0e3..ff71750f 100644 --- a/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverDataFetcherTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverDataFetcherTest.kt @@ -16,7 +16,6 @@ import graphql.schema.DataFetchingEnvironmentImpl import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.ReceiveChannel @@ -27,11 +26,9 @@ import java.util.concurrent.CancellationException import java.util.concurrent.CompletableFuture import kotlin.coroutines.coroutineContext -@OptIn(ExperimentalCoroutinesApi::class) class MethodFieldResolverDataFetcherTest { @Test - @ExperimentalCoroutinesApi fun `data fetcher executes suspend function on coroutineContext defined by options`() { // setup val suspendClass = SuspendClass() @@ -48,7 +45,6 @@ class MethodFieldResolverDataFetcherTest { private val dispatcher = Dispatchers.IO private val job = Job() - @ExperimentalCoroutinesApi val options = SchemaParserOptions.Builder() .coroutineContext(dispatcher + job) .build() @@ -124,7 +120,6 @@ class MethodFieldResolverDataFetcherTest { } } - @ExperimentalCoroutinesApi @OptIn(DelicateCoroutinesApi::class) @Test fun `canceling subscription Publisher also cancels underlying Kotlin coroutine channel`() { diff --git a/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverTest.kt b/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverTest.kt index a02ef092..70200a8e 100644 --- a/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverTest.kt @@ -11,14 +11,12 @@ import graphql.schema.Coercing import graphql.schema.CoercingParseLiteralException import graphql.schema.CoercingSerializeException import graphql.schema.GraphQLScalarType -import kotlinx.coroutines.ExperimentalCoroutinesApi import org.junit.Test import java.lang.reflect.InvocationHandler import java.lang.reflect.Method import java.lang.reflect.Proxy import java.util.* -@OptIn(ExperimentalCoroutinesApi::class) class MethodFieldResolverTest { @Test diff --git a/src/test/kotlin/graphql/kickstart/tools/MissingFieldResolverTest.kt b/src/test/kotlin/graphql/kickstart/tools/MissingFieldResolverTest.kt index 2f5321d2..c3727952 100644 --- a/src/test/kotlin/graphql/kickstart/tools/MissingFieldResolverTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/MissingFieldResolverTest.kt @@ -6,11 +6,9 @@ import graphql.kickstart.tools.resolver.MissingResolverDataFetcherProvider import graphql.language.FieldDefinition import graphql.schema.DataFetcher import graphql.schema.DataFetchingEnvironment -import kotlinx.coroutines.ExperimentalCoroutinesApi import org.junit.Test import java.util.* -@OptIn(ExperimentalCoroutinesApi::class) class MissingFieldResolverTest { @Test(expected = FieldResolverError::class) diff --git a/src/test/kotlin/graphql/kickstart/tools/ReactiveTest.kt b/src/test/kotlin/graphql/kickstart/tools/ReactiveTest.kt index c35e51b3..9531260c 100644 --- a/src/test/kotlin/graphql/kickstart/tools/ReactiveTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/ReactiveTest.kt @@ -7,7 +7,6 @@ import graphql.execution.AsyncExecutionStrategy import graphql.kickstart.tools.SchemaParser.Companion.newParser import graphql.kickstart.tools.SchemaParserOptions.Companion.newOptions import graphql.kickstart.tools.SchemaParserOptions.GenericWrapper -import kotlinx.coroutines.ExperimentalCoroutinesApi import org.junit.Test import java.util.* import java.util.concurrent.CompletableFuture @@ -17,7 +16,6 @@ import java.util.concurrent.FutureTask //import io.reactivex.Single; //import io.reactivex.internal.operators.single.SingleJust; //import static io.reactivex.Maybe.just; -@OptIn(ExperimentalCoroutinesApi::class) class ReactiveTest { @Test diff --git a/src/test/kotlin/graphql/kickstart/tools/SchemaClassScannerTest.kt b/src/test/kotlin/graphql/kickstart/tools/SchemaClassScannerTest.kt index 24f0e662..c8b0af77 100644 --- a/src/test/kotlin/graphql/kickstart/tools/SchemaClassScannerTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/SchemaClassScannerTest.kt @@ -4,12 +4,10 @@ import graphql.GraphQLContext import graphql.execution.CoercedVariables import graphql.language.Value import graphql.schema.* -import kotlinx.coroutines.ExperimentalCoroutinesApi import org.junit.Test import java.util.* import java.util.concurrent.CompletableFuture -@OptIn(ExperimentalCoroutinesApi::class) class SchemaClassScannerTest { @Test diff --git a/src/test/kotlin/graphql/kickstart/tools/SchemaParserTest.kt b/src/test/kotlin/graphql/kickstart/tools/SchemaParserTest.kt index e5e2ba30..43f49a3b 100644 --- a/src/test/kotlin/graphql/kickstart/tools/SchemaParserTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/SchemaParserTest.kt @@ -6,7 +6,6 @@ import graphql.kickstart.tools.resolver.FieldResolverError import graphql.schema.* import graphql.schema.idl.SchemaDirectiveWiring import graphql.schema.idl.SchemaDirectiveWiringEnvironment -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.reactive.publish import org.junit.Assert.assertThrows @@ -22,7 +21,6 @@ import java.util.concurrent.CompletableFuture.completedStage import java.util.concurrent.CompletionStage import java.util.concurrent.Future -@OptIn(ExperimentalCoroutinesApi::class) class SchemaParserTest { private lateinit var builder: SchemaParserBuilder diff --git a/src/test/kotlin/graphql/kickstart/tools/TestUtils.kt b/src/test/kotlin/graphql/kickstart/tools/TestUtils.kt index 1180fc72..7aa9a396 100644 --- a/src/test/kotlin/graphql/kickstart/tools/TestUtils.kt +++ b/src/test/kotlin/graphql/kickstart/tools/TestUtils.kt @@ -19,7 +19,7 @@ fun assertNoGraphQlErrors(gql: GraphQL, args: Map = mapOf(), contex throw AssertionError("GraphQL result contained errors!\n${result.errors.map { it.message }.joinToString("\n")}") } - return result.getData>()!! + return checkNotNull(result.getData>()) } fun assertEquals(actual: T, expected: T) { diff --git a/src/test/kotlin/graphql/kickstart/tools/TypeClassMatcherTest.kt b/src/test/kotlin/graphql/kickstart/tools/TypeClassMatcherTest.kt index d30f0b33..4fcb4b34 100644 --- a/src/test/kotlin/graphql/kickstart/tools/TypeClassMatcherTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/TypeClassMatcherTest.kt @@ -6,7 +6,6 @@ import graphql.kickstart.tools.resolver.FieldResolverScanner import graphql.kickstart.tools.util.ParameterizedTypeImpl import graphql.language.* import graphql.language.Type -import kotlinx.coroutines.ExperimentalCoroutinesApi import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized @@ -23,7 +22,6 @@ import java.util.concurrent.Future TypeClassMatcherTest.Suit3::class, TypeClassMatcherTest.Suit4::class ) -@OptIn(ExperimentalCoroutinesApi::class) class TypeClassMatcherTest { companion object { From c996dd847a4078f263b9f15be0f74a735bd9a623 Mon Sep 17 00:00:00 2001 From: Oryan Date: Fri, 16 Oct 2026 16:50:28 -0400 Subject: [PATCH 8/8] Give the data fetcher test environments a schema graphql-java always sets the schema on the environment, so the safe call on it was only there for this test. Co-Authored-By: Claude Opus 5.5 --- .../kickstart/tools/resolver/MethodFieldResolver.kt | 4 +--- .../kickstart/tools/MethodFieldResolverDataFetcherTest.kt | 7 +++++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt b/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt index 0ac8612c..3c222bc7 100644 --- a/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt +++ b/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt @@ -152,14 +152,12 @@ internal class MethodFieldResolver( * because values can be coerced to multiple different Java types (eg. String, Long, UUID). All values of a non-concrete scalar * type must be converted to the target method parameter type. */ - // graphql-java marks the schema non-null, but it's null in hand-built environments, e.g. in tests - @Suppress("UNNECESSARY_SAFE_CALL") private fun isConcreteScalarType(environment: DataFetchingEnvironment, type: Type<*>, genericParameterType: JavaType): Boolean { return when (type) { is ListType -> List::class.java.isAssignableFrom(this.typeResolver.getRawClass(genericParameterType)) && isConcreteScalarType(environment, type.type, this.typeResolver.resolve(genericParameterType)) - is TypeName -> environment.graphQLSchema?.getType(type.name!!)?.let { isScalar(it) && type.name != "ID" } + is TypeName -> environment.graphQLSchema.getType(type.name!!)?.let { isScalar(it) && type.name != "ID" } ?: false is NonNullType -> isConcreteScalarType(environment, type.type, genericParameterType) diff --git a/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverDataFetcherTest.kt b/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverDataFetcherTest.kt index ff71750f..14ff8b48 100644 --- a/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverDataFetcherTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverDataFetcherTest.kt @@ -2,6 +2,7 @@ package graphql.kickstart.tools import graphql.ExecutionResult import graphql.GraphQLContext +import graphql.Scalars import graphql.execution.* import graphql.execution.instrumentation.SimplePerformantInstrumentation import graphql.kickstart.tools.resolver.FieldResolverError @@ -13,6 +14,8 @@ import graphql.language.TypeName import graphql.schema.DataFetcher import graphql.schema.DataFetchingEnvironment import graphql.schema.DataFetchingEnvironmentImpl +import graphql.schema.GraphQLObjectType +import graphql.schema.GraphQLSchema import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers @@ -429,7 +432,11 @@ class MethodFieldResolverDataFetcherTest { } } val executionId = ExecutionId.from("executionId123") + val schema = GraphQLSchema.newSchema() + .query(GraphQLObjectType.newObject().name("Query").field { it.name("active").type(Scalars.GraphQLBoolean) }) + .build() return ExecutionContextBuilder.newExecutionContextBuilder() + .graphQLSchema(schema) .graphQLContext(context) .instrumentation(SimplePerformantInstrumentation.INSTANCE) .executionId(executionId)