graphql-java-kickstart / graphql-java-tools

A schema-first tool for graphql-java inspired by graphql-tools for JS
https://www.graphql-java-kickstart.com/tools/
MIT License
808 stars 175 forks source link

it is possible to have a mutation with nested inputs? #326

Open LucasLopesr opened 4 years ago

LucasLopesr commented 4 years ago

I get the following exception:

com.coxautodev.graphql.tools.SchemaError: Expected type 'VigenciaInput' to be a GraphQLInputType, but it wasn't! Was a type only allowed for object types incorrectly used as an input type, or vice versa?

example schema:

type Mutation {
    createFoo(input: FooInput): String!
}

input FooInput {
    bar: BarInput!
}

input BarInput {
    message: String!
}
LucasLopesr commented 4 years ago

I get the following exception:

com.coxautodev.graphql.tools.SchemaError: Expected type 'VigenciaInput' to be a GraphQLInputType, but it wasn't! Was a type only allowed for object types incorrectly used as an input type, or vice versa?

example schema:

type Mutation { createFoo(input: FooInput): String! }

input FooInput { bar: BarInput! }

input BarInput { message: String! }

Resolved with code.


    @Bean
    public SchemaParserDictionary schemaParserDictionary() {
        return new SchemaParserDictionary().add(BarInput.class);
    }

I believe it is not ideal, any suggestions?

oliemansm commented 4 years ago

So the scanner isn't trying to find nested input types apparently. I'll mark this as an enhancement then. For now I'm afraid there's no way around applying that workaround you've done now.

xetra11 commented 4 years ago

Well for me it does work somehow already? Is this still a needed enhancement then?

dbharti2000 commented 3 years ago

@LucasLopesr I have run into same issue. I tried your solution but it's not working for me. Did you have to do for all nested classes? Please help! e.g.

@Bean public SchemaParserDictionary schemaParserDictionary() { return new SchemaParserDictionary().add(FooInput.class).add(BarInput.class); }

I am using below versions -

graphql-spring-boot-starter
        <version>5.0.2</version>
    </dependency>
    <dependency>
        <groupId>com.graphql-java</groupId>
        <artifactId>graphql-java-tools</artifactId>
        <version>5.2.4</version>
    </dependency>
    <dependency>
        <groupId>com.graphql-java</groupId>
        <artifactId>graphiql-spring-boot-starter</artifactId>
        <version>5.0.2</version>

dbharti2000 commented 3 years ago

@oliemansm i am having this issue when i am trying to do mutation with nested inputs.

schema { mutation: Mutation query: Query }

type Mutation { createARecord(input: AbcInput): String! }

input AbcInput { domain: XyzInput! age: Int }

input XyzInput { transactionId: String! }

It works if i don't have nested inputs e.g.

schema { mutation: Mutation query: Query }

type Mutation { createARecord(input: AbcInput): Xyz! }

input AbcInput { transactionId: String! }

Please help!

oliemansm commented 3 years ago

@dbharti2000 You're on very old and outdated versions of these libraries. Make sure to try this with the latest versions first (see the readme's). If it then still doesn't work then this is still an active issue that needs to be resolved/implemented. Our time is very limited, since we also just do this on the side, so any contributions for this to help fix it are very welcome.

plaurina commented 3 years ago

I had the same basic issue - I'll see if I can explain the underlying cause and a workaround some may find useful, and my initial thought on how I can resolve this in the code if a PR would be accepted for it.

Using the OP's example: BarInput is not used in a mutation directly - which causes it to be considered as "unused". You might see this in the logs:

Schema type was defined but can never be accessed, and can be safely deleted: BarInput

Hackiest Workaround - not my favorite, but it might get some people unstuck: Create a mutation that uses BarInput directly.

type Mutation {
    createFoo(input: FooInput): String!
    updateBar(input: BarInput): String
}

If you intend to implement it anyway, this will get you unstuck, but if you do not want it to have its own mutation you're in a bind. You can always throw a "NotImplementedException", which is definitely hacky and not nice to consumers of the API. - but again, might get someone unstuck (especially if their API is private)

While debugging, I am able to see the cause in: src/main/kotlin/graphql/kickstart/tools/SchemaParser.kt

around line 36:

private val definitions = scanResult.definitions

the resulting list does not include include the nested input types, and instead it is in unusedDefinitions, which means the inputObjectDefinitions list does not include it when trying to resolved the input types. around line: 46

    private val inputObjectDefinitions = (definitions.filterIsInstance<InputObjectTypeDefinition>() - inputExtensionDefinitions)

one slightly less hacky solution would be to filter unused list and include it into the inputObjectDefinitions:

    private val inputObjectDefinitions = (definitions.filterIsInstance<InputObjectTypeDefinition>() - inputExtensionDefinitions + unusedDefinitions.filterIsInstance<InputObjectTypeDefinition>())

This "works" - but leaves the warning message in the logs saying the type isn't used and is safe to delete, etc.

Ideally we should improve the scanner to be smarter about discovering used types and have it include nested types (resursively). I have 20 years of Java experience and about 1 hour of Kotlin experience at this point. Right now, I'm using v6.1.0 - so I have implemented the hacky code fix, recompiled and used it for project - but recognize it's not a good universal fix and will not submit a PR for it unless the admin's specifically want it. I may try my hand at some Kotlin and see if I can make scanner that can look within nested fields - but at first glance it might be a bit involved to make it recursively discover used types.