graphql-platform icon indicating copy to clipboard operation
graphql-platform copied to clipboard

Support F# list type conversion

Open Leaxe opened this issue 3 years ago • 5 comments

Is your feature request related to a problem?

I have been writing an F# wrapper for HotChocolate. It has been going pretty well but I recently ran into an issue when trying to read an argument that is type list. This issue is reproducible with the following query definition, written in F#:

type QueryObject() =
    inherit ObjectType<obj>()
    with
        override self.Configure(descriptor: IObjectTypeDescriptor<obj>) =
            descriptor.Field("testField")
                .Argument("testArg", fun a -> a.Type<ListType<StringType>>() |> ignore)
                .Resolve<list<string>>(fun (context: IResolverContext) -> context.ArgumentValue<list<string>>("testArg"))
                .Type<ListType<StringType>>()
            |> ignore

This produces the following runtime error when attempting to query the field:

System.NotSupportedException: Unable to convert type from `List`1` to `FSharpList`1`
   at HotChocolate.Utilities.DefaultTypeConverter.Convert(Type from, Type to, Object source)
   at HotChocolate.Utilities.DictionaryToObjectConverter.VisitList(IReadOnlyList`1 list, ConverterContext context)
   at HotChocolate.Utilities.DictionaryVisitor`1.Visit(Object value, TContext context)
   at HotChocolate.Utilities.DictionaryToObjectConverter.Convert(Object from, Type to)
   at HotChocolate.Types.InputParser.ConvertValue(Type requestedType, Object value)
   at HotChocolate.Types.InputParser.ParseLiteral(IValueNode value, IInputFieldInfo field, Type targetType)
   at HotChocolate.Execution.Processing.MiddlewareContext.CoerceArgumentValue[T](ArgumentValue argument)
   at HotChocolate.Execution.Processing.MiddlewareContext.ArgumentValue[T](NameString name)
   at Schema.Pipe #2 input at line [email protected](IResolverContext context)

It is possible to write a custom type converter:

let configureServices (services: IServiceCollection) =
    services.AddGraphQLServer()
        .ModifyRequestOptions(fun opt -> opt.IncludeExceptionDetails <- true)
        .AddQueryType<QueryObject>()
        .AddTypeConverter<System.Collections.Generic.List<string>, list<string>>(fun l -> List.ofSeq l)
        .InitializeOnStartup()
    |> ignore

But this would need to be done for each list type, which I haven't found a good way to generalize it for my wrapper.

The solution you'd like

Support for converting C# lists to F# lists. Thanks for your awesome program!

Product

Hot Chocolate

Leaxe avatar Aug 26 '22 22:08 Leaxe

Lets create an f# package that handles it generically ... F# support is on our list but we are lacking people from the community that help us :)

michaelstaib avatar Aug 31 '22 21:08 michaelstaib

Strange ... it implements IREadOnlyList which we should be able to handle ... let me check

michaelstaib avatar Aug 31 '22 21:08 michaelstaib

Would you be willing to contribute some unit tests? This would help us make it better for F#

The issue is in the type conversion. You could fix it by providing a IChangeTypeProvider which we use to compile converters at runtime for generics. However, if you contribute a F# unit tests I will patch it for you in the core.

michaelstaib avatar Aug 31 '22 21:08 michaelstaib

Hey Michael, thanks for the response!

I found out that FSharpList was not being converted because it doesn't implement a default constructor.

I put together some unit tests and my attempt at an IChangeTypeProvider on my fork: https://github.com/Leaxe/hotchocolate/tree/fsharp-list-converters

This is my first time working with C# so let me know if it looks reasonable and if I should open a pull request. I am also interested in adding a converter for C# Nullable to F# Option.

Leaxe avatar Sep 06 '22 06:09 Leaxe