fsprojects / FSharp.Data.GraphQL

FSharp implementation of Facebook GraphQL query language.
http://fsprojects.github.io/FSharp.Data.GraphQL/
MIT License
395 stars 72 forks source link

"Object must implement IConvertible" when using built-in ID #306

Open mjarosie opened 3 years ago

mjarosie commented 3 years ago

Description

When using built-in ID GraphQL type to represent the Guid .NET type I'm getting Object must implement IConvertible error back from the executor.

Repro steps

open FSharp.Data.GraphQL
open FSharp.Data.GraphQL.Types

type Person =
    { Id: System.Guid
      Name: string }

let people: List<Person> = [ 
    { Id = System.Guid.Parse("d6c684d9-aaaa-4e88-bbb2-0bb584f1661d"); Name = "Person A" }
    { Id = System.Guid.Parse("d6c684d9-bbbb-4e88-bbb2-0bb584f1661d"); Name = "Person B" } ]

let ProjectType: ObjectDef<Person> = Define.Object(
    name = "Person",
    fields = [
        Define.AutoField("id", ID)
        Define.AutoField("name", String)
    ])

let QueryRoot = Define.Object("Query", [
    Define.Field("people", ListOf ProjectType, fun ctx () -> 
        printfn "Querying for people"
        people
    )
])

let schema = Schema(query = QueryRoot)

let executor = Executor(schema = schema)

let query = "{people{id}}"
let result = executor.AsyncExecute(query) |> Async.RunSynchronously
printfn "%A" result

Expected behavior

I'm getting a list of people IDs back.

Actual behavior

I'm receiving the following error:

System.InvalidCastException: Object must implement IConvertible.\r\n   at System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)\r\n   at System.Convert.ChangeType(Object value, Type conversionType)\r\n   at FSharp.Data.GraphQL.Types.SchemaDefinitions.coerceIDValue[t](Object x)\r\n   at FSharp.Data.GraphQL.Types.SchemaDefinitions.ID@2581-1.Invoke(Object x)\r\n   at FSharp.Data.GraphQL.Types.ScalarDefinition`1.FSharp-Data-GraphQL-Types-ScalarDef-CoerceValue(Object value)\r\n   at FSharp.Data.GraphQL.Execution.direct(OutputDef returnDef, ResolveFieldContext ctx, FSharpList`1 path, Object parent, Object value)\r\n   at FSharp.Data.GraphQL.Execution.executeObjectFields(FSharpList`1 fields, String objName, ObjectDef objDef, ResolveFieldContext ctx, FSharpList`1 path, Object value)\r\n   at Microsoft.FSharp.Collections.ArrayModule.MapIndexed[T,TResult](FSharpFunc`2 mapping, T[] array) in F:\\workspace\\_work\\1\\s\\src\\fsharp\\FSharp.Core\\array.fs:line 349\r\n   at FSharp.Data.GraphQL.Execution.direct$cont@329-1(ResolveFieldContext ctx, FSharpList`1 path, Object value, String name, OutputDef innerDef, Unit unitVar)\r\n   at FSharp.Data.GraphQL.Execution.direct$cont@312(OutputDef returnDef, ResolveFieldContext ctx, FSharpList`1 path, Object parent, Object value, String name, Unit unitVar)\r\n   at FSharp.Data.GraphQL.Execution.executeRootOperation@563(ExecutionContext ctx, ObjectDef objdef, Object rootValue, String tupledArg0, ExecutionInfo tupledArg1)\r\n   at FSharp.Data.GraphQL.Execution.executeQueryOrMutation(Tuple`2[] resultSet, ExecutionContext ctx, ObjectDef objdef, Object rootValue)\r\n   at <StartupCode$FSharp-Data-GraphQL-Server>.$Executor.clo@110-4.Invoke(Unit unitVar)\r\n   at Microsoft.FSharp.Control.AsyncPrimitives.CallThenInvoke[T,TResult](AsyncActivation`1 ctxt, TResult result1, FSharpFunc`2 part2) in F:\\workspace\\_work\\1\\s\\src\\fsharp\\FSharp.Core\\async.fs:line 398\r\n   at Microsoft.FSharp.Control.Trampoline.Execute(FSharpFunc`2 firstAction) in F:\\workspace\\_work\\1\\s\\src\\fsharp\\FSharp.Core\\async.fs:line 109

Known workarounds

You can change the Type definition of the Guid field to be just a String, but semantically that's not correct (I'm still getting my head around GraphQL, but as far as I know that's the whole purpose of using ID type - it is not intended to be human‐readable):

let ProjectType: ObjectDef<Person> = Define.Object(
    name = "Person",
    fields = [
        Define.AutoField("id", String)
        Define.AutoField("name", String)
    ])
mjarosie commented 3 years ago

As it turns out it's just the AutoField which doesn't work for ID:

Define.Field("id", ID, (fun _ person -> person.Id )) // Works
Define.AutoField("id", ID) // Doesn't work as shown above
njlr commented 2 years ago

I think FSharp.Data.GraphQL.Types.Guid should be used here

xperiandri commented 9 months ago

@mjarosie try with the latest package from GitHub feed. If not fixed I'll take care