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

@ShareableDirective causes schema generation error when applied to class used as both input and output #1938

Open
jdsatlin opened this issue Mar 13, 2024 · 0 comments
Labels
type: bug Something isn't working

Comments

@jdsatlin
Copy link

jdsatlin commented Mar 13, 2024

Hi GraphQL Kotlin devs. I appreciate your work, and thanks for taking a look at this issue. Please let me know if there is any more details or information that would be helpful to include.

Library Version
tested and reproduced in graphql-kotlin-spring-server:6.6.0 & graphql-kotlin-spring-server:7.0.2 (latest at time of issue creation)

Describe the bug
Using GraphQL Kotlin Spring Server and a Federation V2 model, schema generation fails when a type with the @ShareableDiretive is referenced as both input and output of a GraphQL operation. When GraphQL Kotlin is generating the schema for that type used, for example like

@ShareableDirective
data class ExampleTypeRepro(val latitude: Double, val longitude: Double)

@Component
class ExampleQueryRepro: Query {

    suspend fun exampleQueryRepro(exampleTypeAsInput: ExampleTypeRepro): ExampleTypeRepro {
        return ExampleTypeRepro(0.0, 0.0)
    }
}

it automatically generates both the input and output types, and attempts to apply the @shareable directive to both input and output type, resulting in an invalid schema like

type ExampleTypeRepro @shareable {
    latitude: Float!
    longitude: Float!
}

input ExampleTypeReproInput @shareable {
    latitude: Float!
    longitude: Float!
}

where @shareable can only be applied to output types and output fields.

As a workaround the user can explicitly define both the input and output types as separate Kotlin classes, but that creates an otherwise unnecessary proliferation of types where without the shareable directive GraphQL Kotlin would be able to automatically create the appropriate and valid GraphQL types from a single Kotlin class.

To Reproduce
Clone https://github.com/jdsatlin/shareable-bug-repro featuring both a working workaround example and the bug case already configured and attempt to run it.
Alternatively: Set up a new GraphQL Kotlin Spring server with federation enabled, and these types defined under the package configured for schema generation

@ShareableDirective
data class ExampleTypeRepro(val latitude: Double, val longitude: Double)

@Component
class ExampleQueryRepro: Query {

    suspend fun exampleQueryRepro(exampleTypeAsInput: ExampleTypeRepro): ExampleTypeRepro {
        return ExampleTypeRepro(0.0, 0.0)
    }
}

and run that.
Result: see an error during schema generation that has an ultimate root cause exception along the lines of:

Caused by: com.expediagroup.graphql.generator.exceptions.InvalidDirectiveLocationException: Directive shareable was specified in unsupported INPUT_OBJECT location (class com.graphql.kotlin.bug.repro.shareablebugrepro.schema.ExampleTypeRepro). This directive can only be applied on one of the supported locations: FIELD_DEFINITION, OBJECT.
	at com.expediagroup.graphql.generator.internal.types.GenerateDirectiveKt.generateDirectives(generateDirective.kt:51) ~[graphql-kotlin-schema-generator-7.0.2.jar:7.0.2]
	at com.expediagroup.graphql.generator.internal.types.GenerateDirectiveKt.generateDirectives$default(generateDirective.kt:36) ~[graphql-kotlin-schema-generator-7.0.2.jar:7.0.2]
	at com.expediagroup.graphql.generator.internal.types.GenerateInputObjectKt.generateInputObject(generateInputObject.kt:43) ~[graphql-kotlin-schema-generator-7.0.2.jar:7.0.2]
	at com.expediagroup.graphql.generator.internal.types.GenerateGraphQLTypeKt.getGraphQLType(generateGraphQLType.kt:105) ~[graphql-kotlin-schema-generator-7.0.2.jar:7.0.2]
	at com.expediagroup.graphql.generator.internal.types.GenerateGraphQLTypeKt.access$getGraphQLType(generateGraphQLType.kt:1) ~[graphql-kotlin-schema-generator-7.0.2.jar:7.0.2]
	at com.expediagroup.graphql.generator.internal.types.GenerateGraphQLTypeKt$objectFromReflection$1.invoke(generateGraphQLType.kt:67) ~[graphql-kotlin-schema-generator-7.0.2.jar:7.0.2]
	at com.expediagroup.graphql.generator.internal.types.GenerateGraphQLTypeKt$objectFromReflection$1.invoke(generateGraphQLType.kt:66) ~[graphql-kotlin-schema-generator-7.0.2.jar:7.0.2]
	at com.expediagroup.graphql.generator.internal.state.TypesCache.buildIfNotUnderConstruction$graphql_kotlin_schema_generator(TypesCache.kt:150) ~[graphql-kotlin-schema-generator-7.0.2.jar:7.0.2]
	at com.expediagroup.graphql.generator.internal.types.GenerateGraphQLTypeKt.objectFromReflection(generateGraphQLType.kt:66) ~[graphql-kotlin-schema-generator-7.0.2.jar:7.0.2]
	at com.expediagroup.graphql.generator.internal.types.GenerateGraphQLTypeKt.generateGraphQLType(generateGraphQLType.kt:45) ~[graphql-kotlin-schema-generator-7.0.2.jar:7.0.2]
	at com.expediagroup.graphql.generator.internal.types.GenerateArgumentKt.generateArgument(generateArgument.kt:51) ~[graphql-kotlin-schema-generator-7.0.2.jar:7.0.2]
	at com.expediagroup.graphql.generator.internal.types.GenerateFunctionKt.generateFunction(generateFunction.kt:50) ~[graphql-kotlin-schema-generator-7.0.2.jar:7.0.2]
	at com.expediagroup.graphql.generator.internal.types.GenerateFunctionKt.generateFunction$default(generateFunction.kt:34) ~[graphql-kotlin-schema-generator-7.0.2.jar:7.0.2]
	at com.expediagroup.graphql.generator.internal.types.GenerateQueryKt.generateQueries(generateQuery.kt:43) ~[graphql-kotlin-schema-generator-7.0.2.jar:7.0.2]
	at com.expediagroup.graphql.generator.SchemaGenerator.generateSchema(SchemaGenerator.kt:80) ~[graphql-kotlin-schema-generator-7.0.2.jar:7.0.2]
	at com.expediagroup.graphql.generator.federation.FederatedSchemaGenerator.generateSchema(FederatedSchemaGenerator.kt:45) ~[graphql-kotlin-federation-7.0.2.jar:7.0.2]
	at com.expediagroup.graphql.generator.SchemaGenerator.generateSchema$default(SchemaGenerator.kt:58) ~[graphql-kotlin-schema-generator-7.0.2.jar:7.0.2]
	at com.expediagroup.graphql.generator.federation.ToFederatedSchemaKt.toFederatedSchema(toFederatedSchema.kt:44) ~[graphql-kotlin-federation-7.0.2.jar:7.0.2]
	at com.expediagroup.graphql.server.spring.FederatedSchemaAutoConfiguration.schema(FederatedSchemaAutoConfiguration.kt:95) ~[graphql-kotlin-spring-server-7.0.2.jar:7.0.2]
	at com.expediagroup.graphql.server.spring.FederatedSchemaAutoConfiguration$$SpringCGLIB$$0.CGLIB$schema$0(<generated>) ~[graphql-kotlin-spring-server-7.0.2.jar:7.0.2]
	at com.expediagroup.graphql.server.spring.FederatedSchemaAutoConfiguration$$SpringCGLIB$$FastClass$$1.invoke(<generated>) ~[graphql-kotlin-spring-server-7.0.2.jar:7.0.2]
	at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) ~[spring-core-6.1.4.jar:6.1.4]
	at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) ~[spring-context-6.1.4.jar:6.1.4]
	at com.expediagroup.graphql.server.spring.FederatedSchemaAutoConfiguration$$SpringCGLIB$$0.schema(<generated>) ~[graphql-kotlin-spring-server-7.0.2.jar:7.0.2]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) ~[spring-beans-6.1.4.jar:6.1.4]

Expected behavior
GraphQL Kotlin should be able to generate schema input and output types from a single Kotlin class or field of a Kotlin class annotated with @ShareableDirective and only apply @shareable in the schema on the appropriate locations of the output type.

@jdsatlin jdsatlin added the type: bug Something isn't working label Mar 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug Something isn't working
Development

No branches or pull requests

1 participant