Distelli/graphql-apigen

MethodDataFetcher - Dynamic Proxy Of Argument Type Results In Incompatible List Elements

Opened this issue · 1 comments

If a Mutation (or a Query really) is invoked with an input argument containing a list of elements, the runtime generated argsProxy still carries over the LinkedHashMap type for the elements of the list (LinkedHashMap comes from the deserialization of the original GraphQLRequest). Meanwhile, the interface generated by the codegen for the args type uses the input type defined in the schema (clearly NOT LinkedHashMap). Out of the box, this code will not work at runtime since LinkedHashMap is the list element type of the argsProxy. Once you try to invoke a .get() on a list element and try to assign it to the input type the interface defined, you get a class cast exception. This appears to only be a problem when the request contains a list of elements.

I've written a custom DataFetcher, for that particular GraphQLFieldDefinition, that uses my own implementation of the input argument type. My DataFetcher is based off of MethodDataFetcher, but it doesn't need to create the dynamic proxy instance of the input argument. Instead, it constructs an instance of it and properly converts the arguments from the DataFetchingEnvironment to the correct input type for those list elements. This allows me to actually read those list elements inside my Mutation implementation as the data type originally defined in the interface. I've gotten all of this to work via some hackery with the way the codegen works. Here are my questions for anyone actually following the issues in this project:

  1. Is this a bug in MethodDataFetcher when the dynamic args proxy is generated when those args have a list of elements?
  2. How should a custom DataFetcher be wired into my service? Is this done via customizations in graphql-apigen.stg, programmatic customizations when I'm building my GraphQLSchema bean, or via some magic of interface implementation?

Lastly, just want to point out that this library does seem really solid for doing everything I want to do with GraphQL. The only issue I have is that described above.

Here is another update for anyone out there who might come across this issue -- As far as I can tell, the bug is in the MapInvocationHandler of the MethodDataFetcher. The problem with this code is that if a method is invoked on a mutation input argument that returns a List, no further proxy instances of the List elements are made. As a result, those List elements are returned as LinkedHashMaps. This breaks the contract of the Interface type of those List elements, thus incompatibility at Runtime. Seems to me that there needs to be a check for List.class.isAssignableFrom(result.getClass()), and if so, those List elements need to be iterated over and have proxies created based on the Methods generic return type. I've implemented it this way in my DataFetcher, and it seems like it's working fine now.