ExpediaGroup / graphql-kotlin

Libraries for running GraphQL in Kotlin
https://opensource.expediagroup.com/graphql-kotlin/
Apache License 2.0
1.73k stars 345 forks source link

`toSchema()` failing with `ScanResult` error #1879

Closed vaibhavrdbhat closed 7 months ago

vaibhavrdbhat commented 10 months ago

Library Version 7.0.2 Describe the bug I'm upgrading my application from version 6.5.+ of this library to 7.0.2. My unit tests that build and test my ktor application fail when running through the setup steps of my ktor module, specifically the toSchema() call while making my GraphQL object. Here is the stack trace:

Caused by: java.lang.IllegalArgumentException: Cannot use a ScanResult after it has been closed
    at io.github.classgraph.ScanResult.getClassInfo(ScanResult.java:839)
    at com.expediagroup.graphql.generator.internal.state.ClassScanner.getSubTypesOf(ClassScanner.kt:57)
    at com.expediagroup.graphql.generator.ClasspathTypeResolver.getSubTypesOf(GraphQLTypeResolver.kt:53)
    at com.expediagroup.graphql.generator.internal.types.GenerateUnionKt.generateUnionFromKClass(generateUnion.kt:74)
    at com.expediagroup.graphql.generator.internal.types.GenerateUnionKt.generateUnion(generateUnion.kt:39)
    at com.expediagroup.graphql.generator.internal.types.GenerateGraphQLTypeKt.getGraphQLType(generateGraphQLType.kt:98)
    at com.expediagroup.graphql.generator.internal.types.GenerateGraphQLTypeKt.access$getGraphQLType(generateGraphQLType.kt:1)
    at com.expediagroup.graphql.generator.internal.types.GenerateGraphQLTypeKt$objectFromReflection$1.invoke(generateGraphQLType.kt:67)
    at com.expediagroup.graphql.generator.internal.types.GenerateGraphQLTypeKt$objectFromReflection$1.invoke(generateGraphQLType.kt:66)
    at com.expediagroup.graphql.generator.internal.state.TypesCache.buildIfNotUnderConstruction$graphql_kotlin_schema_generator(TypesCache.kt:150)
    at com.expediagroup.graphql.generator.internal.types.GenerateGraphQLTypeKt.objectFromReflection(generateGraphQLType.kt:66)
    at com.expediagroup.graphql.generator.internal.types.GenerateGraphQLTypeKt.generateGraphQLType(generateGraphQLType.kt:45)
    at com.expediagroup.graphql.generator.internal.types.GeneratePropertyKt.generateProperty(generateProperty.kt:38)
    at com.expediagroup.graphql.generator.internal.types.GenerateInterfaceKt.generateInterface(generateInterface.kt:58)
    at com.expediagroup.graphql.generator.internal.types.GenerateGraphQLTypeKt.getGraphQLType(generateGraphQLType.kt:104)
    at com.expediagroup.graphql.generator.internal.types.GenerateGraphQLTypeKt.access$getGraphQLType(generateGraphQLType.kt:1)
    at com.expediagroup.graphql.generator.internal.types.GenerateGraphQLTypeKt$objectFromReflection$1.invoke(generateGraphQLType.kt:67)
    at com.expediagroup.graphql.generator.internal.types.GenerateGraphQLTypeKt$objectFromReflection$1.invoke(generateGraphQLType.kt:66)
    at com.expediagroup.graphql.generator.internal.state.TypesCache.buildIfNotUnderConstruction$graphql_kotlin_schema_generator(TypesCache.kt:150)
    at com.expediagroup.graphql.generator.internal.types.GenerateGraphQLTypeKt.objectFromReflection(generateGraphQLType.kt:66)
    at com.expediagroup.graphql.generator.internal.types.GenerateGraphQLTypeKt.generateGraphQLType(generateGraphQLType.kt:45)
    at com.expediagroup.graphql.generator.internal.types.GenerateListKt.generateList(generateList.kt:25)
    at com.expediagroup.graphql.generator.internal.types.GenerateGraphQLTypeKt.getGraphQLType(generateGraphQLType.kt:97)
    at com.expediagroup.graphql.generator.internal.types.GenerateGraphQLTypeKt.access$getGraphQLType(generateGraphQLType.kt:1)
    at com.expediagroup.graphql.generator.internal.types.GenerateGraphQLTypeKt$objectFromReflection$1.invoke(generateGraphQLType.kt:67)
    at com.expediagroup.graphql.generator.internal.types.GenerateGraphQLTypeKt$objectFromReflection$1.invoke(generateGraphQLType.kt:66)
    at com.expediagroup.graphql.generator.internal.state.TypesCache.buildIfNotUnderConstruction$graphql_kotlin_schema_generator(TypesCache.kt:140)
    at com.expediagroup.graphql.generator.internal.types.GenerateGraphQLTypeKt.objectFromReflection(generateGraphQLType.kt:66)
    at com.expediagroup.graphql.generator.internal.types.GenerateGraphQLTypeKt.generateGraphQLType(generateGraphQLType.kt:45)
    at com.expediagroup.graphql.generator.internal.types.GenerateFunctionKt.generateFunction(generateFunction.kt:56)
    at com.expediagroup.graphql.generator.internal.types.GenerateFunctionKt.generateFunction$default(generateFunction.kt:34)
    at com.expediagroup.graphql.generator.internal.types.GenerateQueryKt.generateQueries(generateQuery.kt:43)
    at com.expediagroup.graphql.generator.SchemaGenerator.generateSchema(SchemaGenerator.kt:80)
    at com.expediagroup.graphql.generator.SchemaGenerator.generateSchema$default(SchemaGenerator.kt:58)
    at com.expediagroup.graphql.generator.ToSchemaKt.toSchema(toSchema.kt:43)
    at com.expediagroup.graphql.generator.ToSchemaKt.toSchema$default(toSchema.kt:34)
    at com.{COMPANY NAME}.hedwig_notification_service.network.graphql.KtorGraphQL.getGraphQLObject(KtorGraphQL.kt:38)
    at com.{COMPANY NAME}.hedwig_notification_service.ApplicationKt$singletonsModule$1$12.invoke(Application.kt:112)
    at com.{COMPANY NAME}.hedwig_notification_service.ApplicationKt$singletonsModule$1$12.invoke(Application.kt:112)
    at org.koin.core.instance.InstanceFactory.create(InstanceFactory.kt:50)
    ... 45 more

To Reproduce The getGraphQLObject() method referenced 4 lines from the bottom of the stack trace is this:

    fun getGraphQLObject(
        messageService: MessageService,
        redisPubSubListener: RedisPubSubListener,
    ): GraphQL {
        // IMPORTANT: update these lists every time a new query/mutation/subscription class is added
        val mutations = listOf(
            TopLevelObject(MessageMutation(messageService)),
        )
        val queries = listOf(
            TopLevelObject(MessageQuery(messageService)),
        )
        val subscriptions = listOf(
            TopLevelObject(MessageSubscription(messageService, redisPubSubListener)),
        )

        val schema: GraphQLSchema = toSchema( // <<< FAILING HERE
            config = config,
            queries = queries,
            mutations = mutations,
            subscriptions = subscriptions,
        )

        return GraphQL.newGraphQL(schema)
            .valueUnboxer(IDValueUnboxer())
            .instrumentation(GraphQLOperationMetricsInstrumentation())
            .subscriptionExecutionStrategy(FlowSubscriptionExecutionStrategy())
            .build()
    }

Expected behavior I mean.... I feel like this should function just as it did prior to the upgrade, but there's probably some subtle bug somewhere in that long list of calls in the expedia library.

vaibhavrdbhat commented 10 months ago

Two things of note: 1) As I stated, this only happens when running the unit tests that run through this code, such as through gradle build or in IntelliJ. If I build without the tests and then do gradle run, the application runs successfully! super weird. 2) I tried isolating the error by only passing in queries, then only mutations, then only subscriptions. None of them worked.

dariuszkuc commented 10 months ago

Hello 👋 Please provide a link to repository that reproduces the error.

In general Cannot use a ScanResult after it has been closed would imply you are trying to reuse same classcanner instance for two different tests (e.g. trying to use same instance of SchemaGenerator to scan different packages).

dariuszkuc commented 7 months ago

Closing due to inactivity. If this is still an issue please provide a link to repository that reproduces the error.