Skip to content

Commit 2b34f17

Browse files
authored
fix: leaked federation subgraph types on supergraph (#230)
Prevent subgraph-specific federation types and scalars being re-declared within the subgraph leaking into the supergraph. https://linear.app/the-guild/issue/CONSOLE-1643
1 parent 0eed438 commit 2b34f17

File tree

3 files changed

+78
-1
lines changed

3 files changed

+78
-1
lines changed

.changeset/tangy-walls-decide.md

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+
Prevent subgraph-specific federation types and scalars being re-declared within the subgraph leaking into the supergraph.

__tests__/composition.spec.ts

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,7 @@ testImplementations((api) => {
529529
typeDefs: parse(/* GraphQL */ `
530530
type User @key(fields: "id") {
531531
id: ID!
532-
"it should not be in supergraph"
532+
533533
name: String! @external
534534
}
535535
@@ -8053,4 +8053,66 @@ testImplementations((api) => {
80538053
}
80548054
`);
80558055
});
8056+
8057+
test("FieldSet does not leak as external type onto supergraph if defined in subgraph", () => {
8058+
const result = api.composeServices([
8059+
{
8060+
typeDefs: parse(/* GraphQL */ `
8061+
schema
8062+
@link(
8063+
url: "https://specs.apollo.dev/federation/v2.5"
8064+
import: ["@key", "FieldSet"]
8065+
) {
8066+
query: Query
8067+
mutation: Mutation
8068+
}
8069+
8070+
interface Node {
8071+
id: ID!
8072+
}
8073+
8074+
type Mutation {
8075+
_: String
8076+
}
8077+
8078+
type Query {
8079+
node(id: ID!): Node
8080+
_service: _Service!
8081+
_entities(representations: [_Any!]!): [_Entity]!
8082+
}
8083+
8084+
type User implements Node
8085+
@key(fields: "id")
8086+
@key(fields: "username") {
8087+
id: ID!
8088+
username: String
8089+
}
8090+
8091+
type _Service {
8092+
sdl: String!
8093+
}
8094+
8095+
union _Entity = User
8096+
8097+
directive @key(
8098+
fields: FieldSet!
8099+
resolvable: Boolean = true
8100+
) repeatable on OBJECT | INTERFACE
8101+
8102+
directive @link(url: String!, import: [String!]) repeatable on SCHEMA
8103+
8104+
scalar FieldSet
8105+
8106+
scalar Long
8107+
8108+
scalar _Any
8109+
`),
8110+
name: "a",
8111+
url: "a",
8112+
},
8113+
]);
8114+
8115+
assertCompositionSuccess(result);
8116+
expect(result.supergraphSdl).not.includes("scalar FieldSet");
8117+
});
80568118
});

src/supergraph/state.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,17 @@ export function createSupergraphStateBuilder() {
211211
}
212212
}
213213

214+
const allFederationImports = new Set<string>(
215+
subgraphState.federation.imports.map((i) => i.alias ?? i.name),
216+
);
217+
214218
for (const [typeName, type] of subgraphState.types) {
219+
// Federation Types that are imported, but also declared within th subgraph (most-likely for IDE support)
220+
// should not be added to the supergraph.
221+
if (allFederationImports.has(typeName)) {
222+
continue;
223+
}
224+
215225
switch (type.kind) {
216226
case "OBJECT": {
217227
objectType.visitSubgraphState(

0 commit comments

Comments
 (0)