Skip to content

Commit

Permalink
Fix assertion errors when querying types with no common supertypes at…
Browse files Browse the repository at this point in the history
… the same path (#2467)

Fetches input selections used to be based on the queried subgraph
schema. However, those selections truly applies to the in-memory data
maintained by the gateway/router, so the supergraph. This used to
not matter (all input selection did correctly applied to the subgraph
schema), but `@interfaceObject` made it so that some input selections
could be invalid for the subgraph. Typically, a fetch that queries a
subgraph with an `@interfaceObject` may look like:
```
Fetch(service: 'A') {
  {
    ... on Book {
      id
    }
  } => {
    ... on Product {
      <some fields>
    }
  }
}
```
where `Book` is a concrete implementation of `Product` but unknown to
subgraph `A`.

However, basing the input selections on the supergraph schema means
having the parent type, in the supergraph, for those selections, and
because some fetches can query multiple different entities, this is
not trivial.

The initial code for `@interfaceObject` make the (incorrect) assumption
that, when a fetch does query multiple types, then we could always find
a common supertype for all those types, and so was using that type as
parent type.

Unfortunately, this isn't always true (see test in this PR, which uses
a union type with types having a field with the same name but different
(and unrelated) types).

So in pratice, fetch inputs cannot always have a common parent type.
This patch thus changes the fetch inputs to be a map of type to
selection sets instead of a single selection set.

Note that it does mean that the `requires` field of a `FetchGroup` is
not a true valid "single" selection for the supergraph (or any subgraph
for that matter), but it is more of a "fun fact" since the execution
never relied on this anyway.

Fixes #2466
  • Loading branch information
Sylvain Lebresne committed Mar 15, 2023
1 parent bceee47 commit 09382e7
Show file tree
Hide file tree
Showing 3 changed files with 225 additions and 85 deletions.
6 changes: 6 additions & 0 deletions .changeset/thin-weeks-refuse.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@apollo/query-planner": patch
---

Fix query planner assertion error when types with no common supertypes are requested at the same path

115 changes: 115 additions & 0 deletions query-planner-js/src/__tests__/buildPlan.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5452,3 +5452,118 @@ test('handles case of key chains in parallel requires', () => {
}
`);
});

test('handles types with no common supertype at the same "mergeAt"', () => {
const subgraph1 = {
name: 'Subgraph1',
typeDefs: gql`
type Query {
t: T
}
union T = T1 | T2
type T1 @key(fields: "id") {
id: ID!
sub: Foo
}
type Foo @key(fields: "id") {
id: ID!
x: Int
}
type T2 @key(fields: "id") {
id: ID!
sub: Bar
}
type Bar @key(fields: "id") {
id: ID!
x: Int
}
`
}

const subgraph2 = {
name: 'Subgraph2',
typeDefs: gql`
type Foo @key(fields: "id") {
id: ID!
y: Int
}
type Bar @key(fields: "id") {
id: ID!
y: Int
}
`
}

const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
const operation = operationFromDocument(api, gql`
{
t {
... on T1 {
sub {
y
}
}
... on T2 {
sub {
y
}
}
}
}
`);

const plan = queryPlanner.buildQueryPlan(operation);
expect(plan).toMatchInlineSnapshot(`
QueryPlan {
Sequence {
Fetch(service: "Subgraph1") {
{
t {
__typename
... on T1 {
sub {
__typename
id
}
}
... on T2 {
sub {
__typename
id
}
}
}
}
},
Flatten(path: "t.sub") {
Fetch(service: "Subgraph2") {
{
... on Foo {
__typename
id
}
... on Bar {
__typename
id
}
} =>
{
... on Foo {
y
}
... on Bar {
y
}
}
},
},
},
}
`);
});

0 comments on commit 09382e7

Please sign in to comment.