Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Papercut: duplicate type names do not cause an error when building a Schema object #454

Open
njlr opened this issue Dec 5, 2023 · 10 comments

Comments

@njlr
Copy link
Contributor

njlr commented Dec 5, 2023

This script demonstrates the issue:

#r "nuget: FSharp.Data.GraphQL.Server, 1.0.7"

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

type Book =
  {
    Title : string
    Year : int
  }

let bookType1 =
  Define.Object(
    name = "Book",
    description = "Book type 1",
    fields =
      [
        Define.Field(
          "title",
          String,
          fun ctx (x : Book) -> x.Title
        )
      ]
  )

let bookType2 =
  Define.Object(
    name = "Book",
    description = "Book type 2",
    fields =
      [
        Define.Field(
          "title",
          String,
          fun ctx (x : Book) -> x.Title
        )
        Define.Field(
          "year",
          Int,
          fun ctx (x : Book) -> x.Year
        )
      ]
  )

let schemaType : ObjectDef<unit> =
  Define.Object(
    name = "Query",
    fields =
      [
        Define.Field(
          "books1",
          ListOf bookType1,
          fun _ () -> []
        )
        Define.Field(
          "books2",
          ListOf bookType2,
          fun _ () -> []
        )
      ]
  )

let schema = Schema(schemaType)
let executor = Executor(schema)

let query = """
{
  books2 {
    title
    year
  }
}
"""

let result =
  executor.AsyncExecute (query)
  |> Async.RunSynchronously

let output, errors =
  match result.Content with
  | GQLResponseContent.Direct (output, errors) -> output, errors
  | x -> failwith $"Unsupported: %A{x}"

printfn $"Output:\n%A{output}\n"

for error in errors do
  printfn $"Error:\n%A{error}\n"
Output:
map []

Error:
("Field 'year' is not defined in schema type 'Book'.", ["books2"; "year"])

The two book types share a name, and the library silently picks bookType1.

I think that duplicate names should cause schema building to fail, so that this error is caught earlier.

let schema = Schema(schemaType) // Should raise an exception on an invalid schema

Any thoughts?

@xperiandri
Copy link
Collaborator

Is it on NuGet release package or in GitHub CI package?

@njlr
Copy link
Contributor Author

njlr commented Dec 12, 2023

Is it on NuGet release package or in GitHub CI package?

This is NuGet

@xperiandri
Copy link
Collaborator

xperiandri commented Dec 12, 2023

Try package from GitHub, it may be fixed there

@njlr
Copy link
Contributor Author

njlr commented Dec 12, 2023

Try package from GitHub, it may be fixed there

Is there any reason the latest is not pushed to NuGet?

@xperiandri
Copy link
Collaborator

There are 2 important PRs left to merge

@xperiandri
Copy link
Collaborator

Please review them and give your opinion

@njlr
Copy link
Contributor Author

njlr commented Oct 14, 2024

Still an issue:

#r "nuget: FSharp.Data.GraphQL.Server, 2.2.1"

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

type Book =
  {
    Title : string
    Year : int
  }

let bookType1 =
  Define.Object(
    name = "Book",
    description = "Book type 1",
    fields =
      [
        Define.Field(
          "title",
          StringType,
          fun ctx (x : Book) -> x.Title
        )
      ]
  )

let bookType2 =
  Define.Object(
    name = "Book",
    description = "Book type 2",
    fields =
      [
        Define.Field(
          "title",
          StringType,
          fun ctx (x : Book) -> x.Title
        )
        Define.Field(
          "year",
          IntType,
          fun ctx (x : Book) -> x.Year
        )
      ]
  )

let schemaType : ObjectDef<unit> =
  Define.Object(
    name = "Query",
    fields =
      [
        Define.Field(
          "books1",
          ListOf bookType1,
          fun _ () -> []
        )
        Define.Field(
          "books2",
          ListOf bookType2,
          fun _ () -> []
        )
      ]
  )

let schema = Schema(schemaType)
let executor = Executor(schema)

let query = """
{
  books2 {
    title
    year
  }
}
"""

let result =
  executor.AsyncExecute (query)
  |> Async.RunSynchronously

let output, errors =
  match result.Content with
  | GQLResponseContent.Direct (output, errors) -> output, errors
  | GQLResponseContent.RequestError errors -> dict [], errors
  | x -> failwith $"Unsupported: %A{x}"

printfn $"Output:\n%A{output}\n"

for error in errors do
  printfn $"Error:\n%A{error}\n"
Output:
seq []

Error:
{ Message = "Field 'year' is not defined in schema type 'Book'."
  Exception = ValueNone
  Path = Include ["books2"; "year"]
  Locations = Skip
  Extensions = Include (seq [[kind, Validation]]) }

@xperiandri
Copy link
Collaborator

2.2.1 does not contain the fix
3.0.0 should

try with such nuget.config

<configuration>
  <packageSources>
    <add key="NuGet official package source" value="https://api.nuget.org/v3/index.json" />
    <add key="FSProjects" value="https://nuget.pkg.github.com/fsprojects/index.json" protocolVersion="3" />
  </packageSources>
  <packageSourceCredentials>
    <FSProjects>
      <add key="Username" value="<your user name>" />
      <add key="ClearTextPassword" value="<your token>" />
    </FSProjects>
  </packageSourceCredentials>
  <packageSourceMapping>
    <packageSource key="FSProjects">
      <package pattern="FSharp.Data.GraphQL*" />
    </packageSource>
    <packageSource key="NuGet official package source">
      <package pattern="*" />
    </packageSource>
  </packageSourceMapping>
</configuration>

@xperiandri
Copy link
Collaborator

We do testing of the latest 3.0.0 changes on our project and almost came to release

@xperiandri
Copy link
Collaborator

@njlr currently the logic is such

member _.AddType (def : NamedDef, ?overwrite : bool) =
let overwrite = defaultArg overwrite false
let add name def overwrite =
if not (map.ContainsKey (name)) then
map.Add (name, def)
elif overwrite then
map.[name] <- def

let compileMiddleware (ctx : SchemaCompileContext) (next : SchemaCompileContext -> unit) =
let modifyFields (object : ObjectDef<'ObjectType>) (fields : FieldDef<'ObjectType> seq) =
let args = [ Define.Input("filter", Nullable ObjectListFilter) ]
let fields = fields |> Seq.map (fun x -> x.WithArgs(args)) |> List.ofSeq
object.WithFields(fields)
let typesWithListFields =
ctx.TypeMap.GetTypesWithListFields<'ObjectType, 'ListType>()
if Seq.isEmpty typesWithListFields
then failwith <| sprintf "No lists with specified type '%A' where found on object of type '%A'." typeof<'ObjectType> typeof<'ListType>
let modifiedTypes =
typesWithListFields
|> Seq.map (fun (object, fields) -> modifyFields object fields)
|> Seq.cast<NamedDef>
ctx.TypeMap.AddTypes(modifiedTypes, overwrite = true)
next ctx

ctx.TypeMap.AddTypes(modifiedTypes, overwrite = true)

How would you suggest to change that?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants