tailcallhq / tailcall

High Performance GraphQL Runtime
https://tailcall.run
Apache License 2.0
1.29k stars 253 forks source link

bug: failed to resolve field from correct union type when JIT is enabled #3009

Open karatakis opened 2 weeks ago

karatakis commented 2 weeks ago

Prerequisites

Describe the bug

I have a field that returns a Union type. I resolve a sub-field with the @http directive in one of those union types. Tailcall tries to fetch that field on a type that this field does not exist.

Steps to reproduce

config.graphql

schema @server(port: 8080) @upstream(baseURL: "https://example.com/", batch: {delay: 10}) {
  query: Query
}

type Query {
  data: [Foo!]!
    @discriminate(field: "type")
    @expr(
      body: [
        {id: 1, type: "Fizz"}
        {uuid: "hazz-1", type: "Hazz"}
        {uuid: "buzz-1", type: "Buzz", spam: {identifier: 1}}
        {uuid: "buzz-2", type: "Buzz", spam: {identifier: 2}}
        {uuid: "buzz-3", type: "Buzz", spam: {identifier: 3}}
      ]
    )
}

union Foo = Fizz | Buzz | Hazz

type Fizz {
  id: Int!
}

type Hazz {
  uuid: String!
}

type Buzz {
  uuid: String!
  spam: Spam
}

type Spam {
  identifier: Int!
  spam: String! @http(path: "/spam", query: [{key: "identifier", value: "{{.value.identifier}}"}], batchKey: ["identifier"])
}

Expected behavior

When I query:

{
  data {
    ... on Buzz {
      spam {
        spam
      }
    }
  }
}

I expect the spam field to be be queried only from the Buzz subtype and collect the data from the following URL:

https://example.com/spam?identifier=1&identifier=2&identifier=3

Actual behavior

Tailcall tries to get spam field from other union types resulting in the following URL:

https://example.com/spam?identifier&identifier=1&identifier=2&identifier=3

Logs

 INFO File read: demo.graphql ... ok
 INFO N + 1 detected: 0
 INFO 🚀 Tailcall launched at [127.0.0.1:8030] over HTTP/1.1
 INFO 🌍 Playground: https://tailcall.run/playground/?u=http://127.0.0.1:8080/graphql&utm_source=tailcall-release&utm_medium=server
 INFO GET https://example.com/spam?identifier&identifier=1&identifier=2&identifier=3  HTTP/1.1
karatakis commented 2 weeks ago

The bug is JIT related. I can replicate it without batching too.

schema @server(port: 8030) @upstream(baseURL: "https://example.com/") {
  query: Query
}

type Query {
  data: [Foo!]!
    @discriminate(field: "type")
    @expr(
      body: [
        {id: 1, type: "Fizz"}
        {uuid: "hazz-1", type: "Hazz"}
        {uuid: "buzz-1", type: "Buzz", spam: {identifier: 1}}
        {uuid: "buzz-2", type: "Buzz", spam: {identifier: 2}}
        {uuid: "buzz-3", type: "Buzz", spam: {identifier: 3}}
      ]
    )
}

union Foo = Fizz | Buzz | Hazz

type Fizz {
  id: Int!
}

type Hazz {
  uuid: String!
}

type Buzz {
  uuid: String!
  spam: Spam
}

type Spam {
  identifier: Int!
  spam: String! @http(path: "/spam", query: [{key: "identifier", value: "{{.value.identifier}}"}])
}

Logs

 INFO 🚀 Tailcall launched at [127.0.0.1:8030] over HTTP/1.1
 INFO 🌍 Playground: https://tailcall.run/playground/?u=http://127.0.0.1:8030/graphql&utm_source=tailcall-debug&utm_medium=server
K: "identifier"
 INFO File read: main.graphql ... ok
 INFO N + 1 detected: 0
 INFO 🚀 Tailcall launched at [127.0.0.1:8030] over HTTP/1.1
 INFO 🌍 Playground: https://tailcall.run/playground/?u=http://127.0.0.1:8030/graphql&utm_source=tailcall-debug&utm_medium=server
 INFO GET https://example.com/spam?identifier HTTP/1.1
 INFO GET https://example.com/spam?identifier HTTP/1.1
 INFO GET https://example.com/spam?identifier=1 HTTP/1.1
 INFO GET https://example.com/spam?identifier=2 HTTP/1.1
 INFO GET https://example.com/spam?identifier=3 HTTP/1.1
meskill commented 2 weeks ago

fixed in https://github.com/tailcallhq/tailcall/pull/3000/commits/1fde1b6f84f23be1bdad1a37bf6d5c365e70c9e2