Skip to content

Commit

Permalink
enhance(federation): orphan types (#5956)
Browse files Browse the repository at this point in the history
* enhance(federation): orphan types

* Fix TS build
  • Loading branch information
ardatan authored Mar 11, 2024
1 parent 8199416 commit d4395dd
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 44 deletions.
5 changes: 5 additions & 0 deletions .changeset/wicked-rats-decide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@graphql-tools/federation": patch
---

Handle orphan types
76 changes: 51 additions & 25 deletions packages/federation/src/supergraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export function getSubschemasFromSupergraphSdl({
const typeNameFieldsKeyBySubgraphMap = new Map<string, Map<string, Map<string, string>>>();
const typeNameCanonicalMap = new Map<string, string>();
const subgraphTypeNameExtraFieldsMap = new Map<string, Map<string, FieldDefinitionNode[]>>();
const orphanTypeMap = new Map<string, TypeDefinitionNode>();
// TODO: Temporary fix to add missing join__type directives to Query
const subgraphNames: string[] = [];
visit(ast, {
Expand All @@ -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 || []),
Expand All @@ -95,6 +95,7 @@ export function getSubschemasFromSupergraphSdl({
})),
];
}
let isOrphan = true;
// END TODO
const fieldDefinitionNodesByGraphName = new Map<string, FieldDefinitionNode[]>();
typeNode.directives?.forEach(directiveNode => {
Expand All @@ -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',
);
Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand All @@ -334,6 +334,9 @@ export function getSubschemasFromSupergraphSdl({
});
}
});
if (isOrphan) {
orphanTypeMap.set(node.name.value, node);
}
},
InterfaceTypeDefinition: TypeWithFieldsVisitor,
UnionTypeDefinition(node) {
Expand Down Expand Up @@ -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 => {
Expand All @@ -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;
Expand Down Expand Up @@ -449,6 +455,9 @@ export function getSubschemasFromSupergraphSdl({
});
}
});
if (isOrphan) {
orphanTypeMap.set(node.name.value, node);
}
},
ObjectTypeDefinition: TypeWithFieldsVisitor,
});
Expand Down Expand Up @@ -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<TypeDefinitionNode>();
// 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,
Expand Down
32 changes: 13 additions & 19 deletions packages/federation/test/__snapshots__/supergraphs.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -525,12 +525,6 @@ type Query {
getAuthor: ReactionAuthor
}
scalar Url
interface Node {
id: ID!
}
type Mutation {
setNumber: Int
}
Expand All @@ -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`] = `
Expand All @@ -551,12 +551,6 @@ type Query {
_entities(representations: [_Any!]!): _Entity
}
scalar Url
interface Node {
id: ID!
}
union _Entity
scalar _Any"
Expand All @@ -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"
Expand Down
12 changes: 12 additions & 0 deletions packages/federation/test/fixtures/supergraphs/d.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -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") {
Expand Down

0 comments on commit d4395dd

Please sign in to comment.