FSharp.Data.GraphQL
FSharp.Data.GraphQL copied to clipboard
Invalid introspection schema when fields arguments are given default values
Description
The library does not generate a valid introspection schema for arguments with default values. They do not appear to be escaped properly.
Repro steps
Here is an F# script that defines a simple schema and outputs the introspection JSON:
#r "nuget: FSharp.Data.GraphQL.Server, 1.0.7"
#r "nuget: Thoth.Json.Net, 5.0.0"
open System
open System.IO
open FSharp.Data.GraphQL
open FSharp.Data.GraphQL.Types
open FSharp.Data.GraphQL.Execution
open Thoth.Json.Net
// JSON stuff
module Encode =
open System.Collections.Generic
let rec private item (x : obj) =
match x with
| x when isNull x -> Encode.nil
| :? Error as e -> graphQlError e
| :? Guid as x -> Encode.guid x
| :? int as i -> Encode.int i
| :? int16 as i -> Encode.int16 i
| :? int64 as i -> Encode.int64 i
| :? string as s -> Encode.string s
| :? float as x -> Encode.float x
| :? float32 as x -> Encode.float32 x
| :? decimal as x -> Encode.decimal x
| :? DateTime as x -> Encode.datetime x
| :? DateTimeOffset as x -> Encode.datetimeOffset x
| :? bool as x -> Encode.bool x
| :? (obj list) as xs -> Encode.list (xs |> Seq.map item |> Seq.toList)
| :? (obj array) as xs -> Encode.array (xs |> Array.map item)
| :? Map<string, obj> as xs ->
xs
|> Map.toSeq
|> Seq.map (fun (k, v) -> k, item v)
|> Seq.toList
|> Encode.object
| :? IDictionary<string, obj> as d ->
Encode.object
(
d
|> Seq.map (fun (KeyValue (k, v)) -> k, item v)
|> Seq.toList
)
| :? (obj seq) as xs -> Encode.list (xs |> Seq.map item |> Seq.toList)
| _ -> failwithf "Unsupported data item of type %A: %A" (x.GetType ()) x
and graphQlError : Encoder<Error> =
(fun (message, path) ->
Encode.object
[
"message", Encode.string message
"path", path |> List.map item |> Encode.list
])
let graphQlOutput : Encoder<Output> = item
// Schema definition
let queryType =
Define.Object<unit>(
name = "Query",
fields =
[
Define.Field(
"foo",
String,
"Foo query",
[
Define.Input("bar", Nullable String, Some "this is the default value")
],
(fun ctx x ->
let bar = ctx.TryArg "bar"
bar |> Option.defaultValue "no bar was given")
)
]
)
let schema = Schema (queryType)
// Introspection
let executor = Executor(schema)
let response =
executor.AsyncExecute Introspection.IntrospectionQuery
|> Async.RunSynchronously
let (Direct (output, _)) = response
let json =
output
|> Encode.graphQlOutput
|> Encode.toString 2
// printfn "%A" json
File.WriteAllText("schema.json", json)
Here is a Node script that validates the schema:
// https://github.com/graphql/graphql-spec/issues/786#issuecomment-706195108
const fs = require("fs");
const { buildClientSchema, printSchema } = require("graphql");
const json = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
if (json.errors) {
throw new Error(
`Errors in introspection: ${JSON.stringify(json.errors, null, 2)}`
);
}
const schema = buildClientSchema(json.data);
console.log(printSchema(schema));
Expected behavior
# Generate schema.json
$ dotnet fsi ./Introspection.fsx
# Validate the schema
$ node ./validate-introspection-schema.js ./schema.json
The F# should generate a valid schema!
Actual behavior
The Node script fails:
GraphQLError [Object]: Syntax Error: Expected <EOF>, found Name "is".
The problem is that the default value in the schema JSON is not properly escaped.
We can manually escape it like this:
- "defaultValue": "this is the default value"
+ "defaultValue": "\"this is the default value\""
And then the Node script works!
$ node ./validate-introspection-schema.js ./schema.json
"""Defers the resolution of this field or fragment"""
directive @defer on FIELD | FRAGMENT_DEFINITION | FRAGMENT_SPREAD | INLINE_FRAGMENT
"""Streams the resolution of this field or fragment"""
directive @stream on FIELD | FRAGMENT_DEFINITION | FRAGMENT_SPREAD | INLINE_FRAGMENT
"""Subscribes for live updates of this field or fragment"""
directive @live on FIELD | FRAGMENT_DEFINITION | FRAGMENT_SPREAD | INLINE_FRAGMENT
"""
The `Date` scalar type represents a Date value with Time component. The Date type appears in a JSON response as a String representation compatible with ISO-8601 format.
"""
scalar Date
"""
The `URI` scalar type represents a string resource identifier compatible with URI standard. The URI type appears in a JSON response as a String.
"""
scalar URI
type Query {
"""Foo query"""
foo(bar: String = "this is the default value"): String!
}
Related information
- Operating system:
Ubuntu 20.04 - Version:
1.0.7 - .NET Runtime, CoreCLR or Mono Version:
6.0.101
This issue was also raised here: https://github.com/graphql/graphql-spec/issues/786
This issue may prevent the GraphIQL Docs panel from working, with error message:
Error fetching schema
Simply a matter of escaping here?
https://github.com/fsprojects/FSharp.Data.GraphQL/blob/3cd0940df3150be671c284d205f9071a5d5950ed/src/FSharp.Data.GraphQL.Shared/Introspection.fs#L238
Ah, I remember, I got this issue too
Any chance this can be merged?
As far as I remember it is fixed either in dev or in IGQLError branches
I'm going to merge all that stuff this week
I had a look. This needs to be migrated to the new changes in the IGQLError branch
Fixed in #391