From d4395dd7d21db3becdf51cc0508e35d246dcbe1e Mon Sep 17 00:00:00 2001 From: Arda TANRIKULU Date: Mon, 11 Mar 2024 13:09:05 +0300 Subject: [PATCH] enhance(federation): orphan types (#5956) * enhance(federation): orphan types * Fix TS build --- .changeset/wicked-rats-decide.md | 5 ++ packages/federation/src/supergraph.ts | 76 +++++++++++++------ .../__snapshots__/supergraphs.test.ts.snap | 32 ++++---- .../test/fixtures/supergraphs/d.graphql | 12 +++ 4 files changed, 81 insertions(+), 44 deletions(-) create mode 100644 .changeset/wicked-rats-decide.md diff --git a/.changeset/wicked-rats-decide.md b/.changeset/wicked-rats-decide.md new file mode 100644 index 00000000000..19a49e383cd --- /dev/null +++ b/.changeset/wicked-rats-decide.md @@ -0,0 +1,5 @@ +--- +"@graphql-tools/federation": patch +--- + +Handle orphan types diff --git a/packages/federation/src/supergraph.ts b/packages/federation/src/supergraph.ts index 01724c85994..b371094296d 100644 --- a/packages/federation/src/supergraph.ts +++ b/packages/federation/src/supergraph.ts @@ -51,6 +51,7 @@ export function getSubschemasFromSupergraphSdl({ const typeNameFieldsKeyBySubgraphMap = new Map>>(); const typeNameCanonicalMap = new Map(); const subgraphTypeNameExtraFieldsMap = new Map>(); + const orphanTypeMap = new Map(); // TODO: Temporary fix to add missing join__type directives to Query const subgraphNames: string[] = []; visit(ast, { @@ -66,10 +67,9 @@ export function getSubschemasFromSupergraphSdl({ function TypeWithFieldsVisitor(typeNode: ObjectTypeDefinitionNode | InterfaceTypeDefinitionNode) { // TODO: Temporary fix to add missing join__type directives to Query if ( - (typeNode.name.value === 'Query' || - typeNode.name.value === 'Mutation' || - typeNode.kind === Kind.INTERFACE_TYPE_DEFINITION) && - !typeNode.directives?.some(directiveNode => directiveNode.name.value === 'join__type') + typeNode.name.value === 'Query' || + (typeNode.name.value === 'Mutation' && + !typeNode.directives?.some(directiveNode => directiveNode.name.value === 'join__type')) ) { (typeNode as any).directives = [ ...(typeNode.directives || []), @@ -95,6 +95,7 @@ export function getSubschemasFromSupergraphSdl({ })), ]; } + let isOrphan = true; // END TODO const fieldDefinitionNodesByGraphName = new Map(); typeNode.directives?.forEach(directiveNode => { @@ -108,6 +109,7 @@ export function getSubschemasFromSupergraphSdl({ } } if (directiveNode.name.value === 'join__type') { + isOrphan = false; const joinTypeGraphArgNode = directiveNode.arguments?.find( argumentNode => argumentNode.name.value === 'graph', ); @@ -277,12 +279,15 @@ export function getSubschemasFromSupergraphSdl({ } subgraphTypes.push(objectTypedDefNodeForSubgraph); }); + if (isOrphan) { + orphanTypeMap.set(typeNode.name.value, typeNode); + } } visit(ast, { ScalarTypeDefinition(node) { - let isShared = !node.name.value.startsWith('link__') && !node.name.value.startsWith('join__'); + let isOrphan = !node.name.value.startsWith('link__') && !node.name.value.startsWith('join__'); node.directives?.forEach(directiveNode => { - isShared = false; + isOrphan = false; if (directiveNode.name.value === 'join__type') { directiveNode.arguments?.forEach(argumentNode => { if (argumentNode.name.value === 'graph' && argumentNode?.value?.kind === Kind.ENUM) { @@ -302,22 +307,17 @@ export function getSubschemasFromSupergraphSdl({ }); } }); - if (isShared) { - subgraphNames.forEach(graphName => { - let subgraphTypes = subgraphTypesMap.get(graphName); - if (!subgraphTypes) { - subgraphTypes = []; - subgraphTypesMap.set(graphName, subgraphTypes); - } - subgraphTypes.push(node); - }); + if (isOrphan) { + orphanTypeMap.set(node.name.value, node); } }, InputObjectTypeDefinition(node) { + let isOrphan = true; node.directives?.forEach(directiveNode => { if (directiveNode.name.value === 'join__type') { directiveNode.arguments?.forEach(argumentNode => { if (argumentNode.name.value === 'graph' && argumentNode?.value?.kind === Kind.ENUM) { + isOrphan = false; const graphName = argumentNode.value.value; let subgraphTypes = subgraphTypesMap.get(graphName); if (!subgraphTypes) { @@ -334,6 +334,9 @@ export function getSubschemasFromSupergraphSdl({ }); } }); + if (isOrphan) { + orphanTypeMap.set(node.name.value, node); + } }, InterfaceTypeDefinition: TypeWithFieldsVisitor, UnionTypeDefinition(node) { @@ -388,8 +391,10 @@ export function getSubschemasFromSupergraphSdl({ }); }, EnumTypeDefinition(node) { + let isOrphan = true; if (node.name.value === 'join__Graph') { node.values?.forEach(valueNode => { + isOrphan = false; valueNode.directives?.forEach(directiveNode => { if (directiveNode.name.value === 'join__graph') { directiveNode.arguments?.forEach(argumentNode => { @@ -403,6 +408,7 @@ export function getSubschemasFromSupergraphSdl({ } node.directives?.forEach(directiveNode => { if (directiveNode.name.value === 'join__type') { + isOrphan = false; directiveNode.arguments?.forEach(argumentNode => { if (argumentNode.name.value === 'graph' && argumentNode.value?.kind === Kind.ENUM) { const graphName = argumentNode.value.value; @@ -449,6 +455,9 @@ export function getSubschemasFromSupergraphSdl({ }); } }); + if (isOrphan) { + orphanTypeMap.set(node.name.value, node); + } }, ObjectTypeDefinition: TypeWithFieldsVisitor, }); @@ -505,22 +514,39 @@ export function getSubschemasFromSupergraphSdl({ types: unionTypeNodes, }; - const subgraphTypes = subgraphTypesMap.get(subgraphName) || []; - const typeNameExtraFieldsMap = subgraphTypeNameExtraFieldsMap.get(subgraphName); - if (typeNameExtraFieldsMap) { - subgraphTypes.forEach(typeNode => { - if ('fields' in typeNode) { - const extraFields = typeNameExtraFieldsMap.get(typeNode.name.value); - if (extraFields) { - (typeNode.fields as FieldDefinitionNode[]).push(...extraFields); + const extraOrphanTypesForSubgraph = new Set(); + // eslint-disable-next-line no-inner-declarations + function visitTypeDefinitionsForOrphanTypes(node: TypeDefinitionNode) { + visit(node, { + [Kind.NAMED_TYPE](node) { + const orphanType = orphanTypeMap.get(node.name.value); + if (orphanType && !extraOrphanTypesForSubgraph.has(orphanType)) { + extraOrphanTypesForSubgraph.add(orphanType); + visitTypeDefinitionsForOrphanTypes(orphanType); } - } + }, }); } + const subgraphTypes = subgraphTypesMap.get(subgraphName) || []; + const typeNameExtraFieldsMap = subgraphTypeNameExtraFieldsMap.get(subgraphName); + subgraphTypes.forEach(typeNode => { + if (typeNameExtraFieldsMap && 'fields' in typeNode) { + const extraFields = typeNameExtraFieldsMap.get(typeNode.name.value); + if (extraFields) { + (typeNode.fields as FieldDefinitionNode[]).push(...extraFields); + } + } + visitTypeDefinitionsForOrphanTypes(typeNode); + }); const schema = buildASTSchema( { kind: Kind.DOCUMENT, - definitions: [...subgraphTypes, entitiesUnionTypeDefinitionNode, anyTypeDefinitionNode], + definitions: [ + ...subgraphTypes, + ...extraOrphanTypesForSubgraph, + entitiesUnionTypeDefinitionNode, + anyTypeDefinitionNode, + ], }, { assumeValidSDL: true, diff --git a/packages/federation/test/__snapshots__/supergraphs.test.ts.snap b/packages/federation/test/__snapshots__/supergraphs.test.ts.snap index 3586377445f..596f59e0067 100644 --- a/packages/federation/test/__snapshots__/supergraphs.test.ts.snap +++ b/packages/federation/test/__snapshots__/supergraphs.test.ts.snap @@ -525,12 +525,6 @@ type Query { getAuthor: ReactionAuthor } -scalar Url - -interface Node { - id: ID! -} - type Mutation { setNumber: Int } @@ -539,7 +533,13 @@ type ReactionAuthor implements Node { avatarUrl: Url! displayName: String! id: ID! -}" +} + +interface Node { + id: ID! +} + +scalar Url" `; exports[`Supergraphs d.graphql subgraphs: d.graphql - NODERESOLVER 1`] = ` @@ -551,12 +551,6 @@ type Query { _entities(representations: [_Any!]!): _Entity } -scalar Url - -interface Node { - id: ID! -} - union _Entity scalar _Any" @@ -577,18 +571,18 @@ type Query { _entities(representations: [_Any!]!): _Entity } -scalar Url - -interface Node { - id: ID! -} - type ReactionAuthor implements Node { avatarUrl: Url! displayName: String! id: ID! } +interface Node { + id: ID! +} + +scalar Url + union _Entity = ReactionAuthor scalar _Any" diff --git a/packages/federation/test/fixtures/supergraphs/d.graphql b/packages/federation/test/fixtures/supergraphs/d.graphql index 6a6bd73244c..63e1916c5ea 100644 --- a/packages/federation/test/fixtures/supergraphs/d.graphql +++ b/packages/federation/test/fixtures/supergraphs/d.graphql @@ -36,6 +36,18 @@ interface Node { id: ID! } +enum NotifiableEntityType { + ITEM +} + +interface AppNotification { + entityType: NotifiableEntityType +} + +type OwnerChangedNotification implements AppNotification { + entityType: NotifiableEntityType +} + type ReactionAuthor implements Node @join__owner(graph: PB_BACKEND) @join__type(graph: PB_BACKEND, key: "id") {