dgs-framework icon indicating copy to clipboard operation
dgs-framework copied to clipboard

bug: Complex InputArgument serialized as Optional List of Complex Type

Open berngp opened this issue 2 years ago • 8 comments

Context

Given the following GraphQL Schema...

type Query {
    hello(person:[Person]): String
}
            
input Person {
    name:String
}

And a Data Fetcher that serializes to a Optional<List<Person>>, as below

@DgsData(parentType = "Query", field = "hello")
fun someFetcher(@InputArgument person: Optional<List<Person>>): String {
    return "Hello, ${person.get().joinToString(", ")}"
}

Expectation

I should see that the optional arguments have a list of Person references, assuming we have a simple Person pojo.

Actual behavior

The following error ensues

java.util.LinkedHashMap cannot be cast to com.netflix.graphql.dgs.internal.kotlin.test.Person

Steps to reproduce

Test case.

    @Test
    fun `@InputArgument on an optional list of input types, with no input argument name`() {
        val schema = """
            type Query {
                hello(person:[Person]): String
            }
            
            input Person {
                name:String
            }
        """.trimIndent()

        val fetcher = object {
            @DgsData(parentType = "Query", field = "hello")
            fun someFetcher(@InputArgument person: Optional<List<Person>>): String {
                assertThat(person)
                    .isNotEmpty
                    .get()
                    .extracting { ls -> ls.map { it.name } }.asList()
                    .containsOnly("tester 1", "tester 2")
                return "Hello, ${person.get().joinToString(", ")}"
            }
        }

        withComponents("helloFetcher" to fetcher)

        val build = GraphQL.newGraphQL(provider.schema(schema)).build()
        val executionResult = build.execute("""{hello(person: [{name: "tester 1"}, {name: "tester 2"}])}""")!!

        assertThat(executionResult).isNotNull
        assertThat(executionResult.errors).isEmpty()
        assertThat(executionResult.isDataPresent).isTrue

        val data = executionResult.getData<Map<String, *>>()
        assertThat(data["hello"]).isEqualTo("Hello, tester 1, tester 2")
    }

berngp avatar May 31 '22 03:05 berngp

Hi @berngp i was tracking this bug for more then a month. Is it going to be fixed in next releases. when we can expect for a new release

k2k1422 avatar Aug 02 '22 04:08 k2k1422

Hi @berngp can you help me how to resolve this issue, or by pass with any other thing. I have using this optional field for a pojo.

if we don't have solutions is we are going to resolve in subsequent releases

regards, kiran kumar

k2k1422 avatar Sep 14 '22 05:09 k2k1422

Hi @k2k1422, we haven't been able to address this yet. As a workaround you can have a List as an argument and expect it to be null or empty. If you need to know if it was set, you can define a DataFetchingEnvironment as input to your data fetcher, and through it understand if the field was set or not in the request. e.g.

@DgsData(parentType = "Query", field = "hello")
    fun someFetcher(@InputArgument persons: List<String>, dfe: DataFetchingEnvironment): String {
        val optList =
            if (dfe.containsArgument("persons"))
                Optional.ofNullable(persons)
            else
                Optional.empty()
        // ....
    }

berngp avatar Sep 15 '22 16:09 berngp

Hi @berngp,

I ran into another issue that may be the same as the above issue, but may be a different issue and just related to this. I thought I'd post here first and get your thoughts.

Essentially it appears that the DefaultInputObjectMapper class has issues with correctly mapping parameterized types with either an upper bound on a ? operator or a List<T> field. With the upper bounded wildcard it is unable to find the implementing class, and further when trying to use a concrete wrapper implementation to work around the above issue, it ignores the List type and attempts to map the list to a single instance of the parameterized type.

For more details I have put together a sample project that demonstrates the problem (which appears to be a bug in the getFieldType method of the DefaultInputObjectMapper class). I have included a ReadMe and java docs with more details.

https://github.com/lbusch25/lawson-busch-input-demo

lbusch25 avatar Sep 27 '22 22:09 lbusch25

This is fixed with #1254 @lbusch25

antholeole avatar Oct 07 '22 02:10 antholeole

@antholeole - thank you!

Curious if you know when that will be released? Looks like its not included in 5.3.0 and testing my example with that version verifies the issue persists. I'm excited to verify so that we can improve the schema workaround we implemented as a result.

lbusch25 avatar Oct 11 '22 16:10 lbusch25

You can use latest master or wait until the next release. It’s currently on master but it has not been made into a release yet. For now if you’d like you can point to the repo using git

antholeole avatar Oct 11 '22 17:10 antholeole

I plan on release later this week or early next.

srinivasankavitha avatar Oct 11 '22 20:10 srinivasankavitha