Skip to content

Commit 7368829

Browse files
authored
Federation: Update Tests & Interface Objects 2nd take (#6194)
* Interface Objects 2nd take * Remove extra as definitions * Make v15 happy * Update tests
1 parent afceb8b commit 7368829

File tree

42 files changed

+577
-228
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+577
-228
lines changed

.changeset/dry-experts-camp.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
"@graphql-tools/batch-delegate": patch
3+
"@graphql-tools/federation": patch
4+
"@graphql-tools/delegate": patch
5+
"@graphql-tools/schema": patch
6+
"@graphql-tools/stitch": patch
7+
"@graphql-tools/utils": patch
8+
---
9+
10+
Handle interface objects in a different way

packages/batch-delegate/src/getLoader.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import DataLoader from 'dataloader';
2-
import { getNamedType, GraphQLList, GraphQLOutputType, GraphQLSchema, print } from 'graphql';
2+
import { getNamedType, GraphQLList, GraphQLSchema, print } from 'graphql';
33
import { ValueOrPromise } from 'value-or-promise';
44
import { delegateToSchema, getActualFieldNodes, SubschemaConfig } from '@graphql-tools/delegate';
55
import { memoize1, memoize2, relocatedError } from '@graphql-tools/utils';
@@ -13,7 +13,7 @@ function createBatchFn<K = any>(options: BatchDelegateOptions) {
1313
return function batchFn(keys: ReadonlyArray<K>) {
1414
return new ValueOrPromise(() =>
1515
delegateToSchema({
16-
returnType: new GraphQLList(getNamedType(options.info.returnType) as GraphQLOutputType),
16+
returnType: new GraphQLList(getNamedType(options.returnType || options.info.returnType)),
1717
onLocatedError: originalError => {
1818
if (originalError.path == null) {
1919
return originalError;
@@ -82,13 +82,14 @@ export function getLoader<K = any, V = any, C = K>(
8282
dataLoaderOptions,
8383
fieldNodes = getActualFieldNodes(info.fieldNodes[0]),
8484
selectionSet = fieldNodes[0].selectionSet,
85+
returnType = info.returnType,
8586
} = options;
8687
const loaders = getLoadersMap<K, V, C>(context ?? GLOBAL_CONTEXT, schema);
8788

8889
let cacheKey = fieldName;
8990

90-
if (info.returnType) {
91-
const namedType = getNamedType(info.returnType);
91+
if (returnType) {
92+
const namedType = getNamedType(returnType);
9293
cacheKey += '@' + namedType.name;
9394
}
9495

packages/delegate/src/finalizeGatewayRequest.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,12 @@ function finalizeSelectionSet(
443443
if (node.typeCondition != null) {
444444
const parentType = typeInfo.getParentType();
445445
const innerType = schema.getType(node.typeCondition.name.value);
446+
if (
447+
isUnionType(parentType) &&
448+
parentType.getTypes().some(t => t.name === innerType?.name)
449+
) {
450+
return node;
451+
}
446452
if (!implementsAbstractType(schema, parentType, innerType)) {
447453
return null;
448454
}

packages/delegate/src/mergeFields.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
GraphQLObjectType,
55
GraphQLResolveInfo,
66
GraphQLSchema,
7+
isAbstractType,
78
locatedError,
89
responsePathAsArray,
910
SelectionSetNode,
@@ -99,11 +100,15 @@ export function mergeFields<TContext>(
99100
return executeFn();
100101
}, undefined);
101102

103+
function handleDelegationPlanResult() {
104+
return object;
105+
}
106+
102107
if (isPromise(res$)) {
103-
return res$.then(() => object);
108+
return res$.then(handleDelegationPlanResult);
104109
}
105110

106-
return object;
111+
return handleDelegationPlanResult();
107112
}
108113

109114
export function handleResolverResult(
@@ -149,6 +154,13 @@ export function handleResolverResult(
149154
}
150155
const existingPropValue = object[responseKey];
151156
const sourcePropValue = resolverResult[responseKey];
157+
if (
158+
responseKey === '__typename' &&
159+
existingPropValue !== sourcePropValue &&
160+
isAbstractType(subschema.transformedSchema.getType(sourcePropValue))
161+
) {
162+
continue;
163+
}
152164
if (sourcePropValue != null || existingPropValue == null) {
153165
if (
154166
existingPropValue != null &&

packages/delegate/src/prepareGatewayDocument.ts

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
FieldNode,
44
FragmentDefinitionNode,
55
getNamedType,
6+
GraphQLNamedOutputType,
67
GraphQLNamedType,
78
GraphQLOutputType,
89
GraphQLSchema,
@@ -72,6 +73,24 @@ export function prepareGatewayDocument(
7273
});
7374
}
7475
}
76+
const typeInSubschema = transformedSchema.getType(typeName);
77+
if (!typeInSubschema) {
78+
for (const selection of selectionNode.selectionSet.selections) {
79+
newSelections.push(selection);
80+
}
81+
}
82+
if (typeInSubschema && 'getFields' in typeInSubschema) {
83+
const fieldMap = typeInSubschema.getFields();
84+
for (const selection of selectionNode.selectionSet.selections) {
85+
if (selection.kind === Kind.FIELD) {
86+
const fieldName = selection.name.value;
87+
const field = fieldMap[fieldName];
88+
if (!field) {
89+
newSelections.push(selection);
90+
}
91+
}
92+
}
93+
}
7594
}
7695
newSelections.push(selectionNode);
7796
}
@@ -180,6 +199,12 @@ function visitSelectionSet(
180199
const possibleTypes = possibleTypesMap[selection.typeCondition.name.value];
181200

182201
if (possibleTypes == null) {
202+
const fieldNodesForTypeName = fieldNodesByField[parentTypeName]?.['__typename'];
203+
if (fieldNodesForTypeName) {
204+
for (const fieldNode of fieldNodesForTypeName) {
205+
newSelections.add(fieldNode);
206+
}
207+
}
183208
newSelections.add(selection);
184209
continue;
185210
}
@@ -193,6 +218,10 @@ function visitSelectionSet(
193218
newSelections.add(generateInlineFragment(possibleTypeName, selection.selectionSet));
194219
}
195220
}
221+
222+
if (possibleTypes.length === 0) {
223+
newSelections.add(selection);
224+
}
196225
}
197226
} else if (selection.kind === Kind.FRAGMENT_SPREAD) {
198227
const fragmentName = selection.name.value;
@@ -222,17 +251,28 @@ function visitSelectionSet(
222251
} else {
223252
const fieldName = selection.name.value;
224253
let skipAddingDependencyNodes = false;
254+
225255
// TODO: Optimization to prevent extra fields to the subgraph
226-
if (isObjectType(parentType) || isInterfaceType(parentType)) {
256+
if (isAbstractType(parentType)) {
257+
skipAddingDependencyNodes = false;
258+
const fieldNodesForTypeName = fieldNodesByField[parentTypeName]?.['__typename'];
259+
if (fieldNodesForTypeName) {
260+
for (const fieldNode of fieldNodesForTypeName) {
261+
newSelections.add(fieldNode);
262+
}
263+
}
264+
} else if (isObjectType(parentType) || isInterfaceType(parentType)) {
227265
const fieldMap = parentType.getFields();
228266
const field = fieldMap[fieldName];
229267
if (field) {
230268
const unavailableFields = extractUnavailableFields(schema, field, selection, shouldAdd);
231269
skipAddingDependencyNodes = unavailableFields.length === 0;
232270
}
233271
}
272+
234273
if (!skipAddingDependencyNodes) {
235274
const fieldNodes = fieldNodesByField[parentTypeName]?.[fieldName];
275+
236276
if (fieldNodes != null) {
237277
for (const fieldNode of fieldNodes) {
238278
newSelections.add(fieldNode);
@@ -470,10 +510,15 @@ function wrapConcreteTypes(
470510
if (isLeafType(namedType)) {
471511
return document;
472512
}
473-
const possibleTypes = isAbstractType(namedType)
513+
514+
let possibleTypes: readonly GraphQLNamedOutputType[] = isAbstractType(namedType)
474515
? targetSchema.getPossibleTypes(namedType)
475516
: [namedType];
476517

518+
if (possibleTypes.length === 0) {
519+
possibleTypes = [namedType];
520+
}
521+
477522
const rootTypeNames = getRootTypeNames(targetSchema);
478523

479524
const typeInfo = new TypeInfo(targetSchema);

0 commit comments

Comments
 (0)