Skip to content

Commit 2a1475f

Browse files
authored
fix: failing satisfiability check for interface(Objects) (#186)
test case is a reproduction of #177
1 parent 5e3cb1a commit 2a1475f

File tree

3 files changed

+86
-0
lines changed

3 files changed

+86
-0
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@theguild/federation-composition": patch
3+
---
4+
5+
Fix failing satisfiability check for interface types.

__tests__/interface-object-composition.spec.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,5 +685,72 @@ testImplementations((api) => {
685685
test.skip(`several interfaces and several interface objects. Should succeed`, () => {});
686686
});
687687
});
688+
689+
test("satisfiability with @interfaceObject", () => {
690+
const result = api.composeServices([
691+
{
692+
name: "a",
693+
typeDefs: parse(/* GraphQL */ `
694+
type Query {
695+
book: Book!
696+
}
697+
698+
interface Media @key(fields: "id") {
699+
id: ID!
700+
}
701+
702+
type Book implements Media @key(fields: "id") {
703+
id: ID!
704+
isbn: String!
705+
}
706+
707+
extend schema
708+
@link(
709+
url: "https://specs.apollo.dev/federation/v2.3"
710+
import: ["@key"]
711+
)
712+
`),
713+
},
714+
{
715+
name: "b",
716+
typeDefs: parse(/* GraphQL */ `
717+
type Media @key(fields: "id") @interfaceObject {
718+
id: ID!
719+
}
720+
721+
type Query {
722+
topRatedMedia: [Media!]!
723+
}
724+
725+
extend schema
726+
@link(
727+
url: "https://specs.apollo.dev/federation/v2.3"
728+
import: ["@key", "@interfaceObject"]
729+
)
730+
`),
731+
},
732+
{
733+
name: "c",
734+
typeDefs: parse(/* GraphQL */ `
735+
type Query {
736+
cBook: Book
737+
}
738+
739+
type Book @key(fields: "id") {
740+
id: ID!
741+
}
742+
743+
extend schema
744+
@link(
745+
url: "https://specs.apollo.dev/federation/v2.3"
746+
import: ["@key"]
747+
)
748+
`),
749+
},
750+
]);
751+
752+
// console.log(api.library, result.errors);
753+
expect(result.errors).toBe(undefined);
754+
});
688755
});
689756
});

src/supergraph/validation/rules/satisfiablity/finder.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,20 @@ export class PathFinder {
260260
return;
261261
}
262262

263+
// We can skip the edge in case the the graph interface implementation does not provide the field.
264+
if (edge.tail.typeState?.kind === "interface") {
265+
const fieldInEdge = edge.tail.typeState.fields
266+
.get(fieldName)
267+
?.byGraph.get(edge.tail.graphId);
268+
269+
if (fieldInEdge === undefined) {
270+
this.logger.groupEnd(
271+
() => "Ignore edge: tail type does not have this field",
272+
);
273+
return;
274+
}
275+
}
276+
263277
// A huge win for performance, is when you do less work :D
264278
// We can ignore an edge that has already been visited with the same key fields / requirements.
265279
// The way entity-move edges are created, where every graph points to every other graph:

0 commit comments

Comments
 (0)