diff --git a/.changeset/lovely-games-reflect.md b/.changeset/lovely-games-reflect.md new file mode 100644 index 00000000000..ad180273e24 --- /dev/null +++ b/.changeset/lovely-games-reflect.md @@ -0,0 +1,6 @@ +--- +"@graphql-tools/federation": patch +--- + +Support overrides on interfaces +See [packages/federation/test/fixtures/federation-compatibility/override-type-interface/supergraph.graphql](https://github.com/ardatan/graphql-tools/blob/739264d5f7f2f4254d4d41f965d664ae04c37e45/packages/federation/test/fixtures/federation-compatibility/override-type-interface/supergraph.graphql) for more details diff --git a/packages/federation/src/supergraph.ts b/packages/federation/src/supergraph.ts index d2ce251fcf2..757db402430 100644 --- a/packages/federation/src/supergraph.ts +++ b/packages/federation/src/supergraph.ts @@ -213,7 +213,13 @@ export function getSubschemasFromSupergraphSdl({ argumentNode.value?.kind === Kind.BOOLEAN && argumentNode.value.value === true, ); - if (!isExternal) { + const isOverridden = joinFieldDirectiveNode.arguments?.some( + argumentNode => + argumentNode.name.value === 'usedOverridden' && + argumentNode.value?.kind === Kind.BOOLEAN && + argumentNode.value.value === true, + ); + if (!isExternal && !isOverridden) { const typeArg = joinFieldDirectiveNode.arguments?.find( argumentNode => argumentNode.name.value === 'type', ); diff --git a/packages/federation/test/fixtures/federation-compatibility/override-type-interface/supergraph.graphql b/packages/federation/test/fixtures/federation-compatibility/override-type-interface/supergraph.graphql new file mode 100644 index 00000000000..99e486044fe --- /dev/null +++ b/packages/federation/test/fixtures/federation-compatibility/override-type-interface/supergraph.graphql @@ -0,0 +1,83 @@ +schema + @link(url: "https://specs.apollo.dev/link/v1.0") + @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION) +{ + query: Query +} + +directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE + +directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: join__FieldSet, type: String, external: Boolean, override: String, usedOverridden: Boolean) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION + +directive @join__graph(name: String!, url: String!) on ENUM_VALUE + +directive @join__implements(graph: join__Graph!, interface: String!) repeatable on OBJECT | INTERFACE + +directive @join__type(graph: join__Graph!, key: join__FieldSet, extension: Boolean! = false, resolvable: Boolean! = true, isInterfaceObject: Boolean! = false) repeatable on OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT | SCALAR + +directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on UNION + +directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA + +interface AnotherPost + @join__type(graph: B) +{ + id: ID! + createdAt: String! +} + +type ImagePost implements Post & AnotherPost + @join__implements(graph: A, interface: "Post") + @join__implements(graph: B, interface: "AnotherPost") + @join__type(graph: A, key: "id") + @join__type(graph: B, key: "id") +{ + id: ID! + createdAt: String! @join__field(graph: A, usedOverridden: true) @join__field(graph: B, override: "a") +} + +scalar join__FieldSet + +enum join__Graph { + A @join__graph(name: "a", url: "https://federation-compatibility.the-guild.dev/override-type-interface/a") + B @join__graph(name: "b", url: "https://federation-compatibility.the-guild.dev/override-type-interface/b") +} + +scalar link__Import + +enum link__Purpose { + """ + `SECURITY` features provide metadata necessary to securely resolve fields. + """ + SECURITY + + """ + `EXECUTION` features provide metadata necessary for operation execution. + """ + EXECUTION +} + +interface Post + @join__type(graph: A) + @join__type(graph: B) +{ + id: ID! + createdAt: String! +} + +type Query + @join__type(graph: A) + @join__type(graph: B) +{ + feed: [Post] @join__field(graph: A) + anotherFeed: [AnotherPost] @join__field(graph: B) +} + +type TextPost implements Post + @join__implements(graph: B, interface: "Post") + @join__type(graph: B, key: "id") +{ + id: ID! + createdAt: String! + body: String! +} \ No newline at end of file diff --git a/packages/federation/test/fixtures/federation-compatibility/override-type-interface/tests.json b/packages/federation/test/fixtures/federation-compatibility/override-type-interface/tests.json new file mode 100644 index 00000000000..3e3c967c8a3 --- /dev/null +++ b/packages/federation/test/fixtures/federation-compatibility/override-type-interface/tests.json @@ -0,0 +1,62 @@ +[ + { + "query": "\n query {\n feed {\n id\n createdAt\n }\n }\n ", + "expected": { + "data": { + "feed": [ + { + "id": "i1", + "createdAt": "i1-createdAt" + }, + { + "id": "i2", + "createdAt": "i2-createdAt" + } + ] + } + } + }, + { + "query": "\n query {\n feed {\n ... on TextPost {\n id\n body\n }\n }\n }\n ", + "expected": { + "data": { + "feed": [ + {}, + {} + ] + } + } + }, + { + "query": "\n query {\n anotherFeed {\n createdAt\n }\n }\n ", + "expected": { + "data": { + "anotherFeed": [ + { + "createdAt": "i1-createdAt" + }, + { + "createdAt": "i2-createdAt" + } + ] + } + } + }, + { + "query": "\n {\n anotherFeed {\n createdAt\n id\n ... on ImagePost {\n createdAt\n id\n }\n }\n }\n ", + "expected": { + "data": { + "anotherFeed": [ + { + "createdAt": "i1-createdAt", + "id": "i1" + }, + { + "createdAt": "i2-createdAt", + "id": "i2" + } + ] + } + } + } +] \ No newline at end of file