Skip to content

Commit 25a7c15

Browse files
committed
Composition - use extensions instead of custom federation bits
Composition in its current form modifies the graphql-js types in order to accommodate some additional metadata required during composition, validation, query planning, and execution. This is no longer required due to the addition of 'extensions', which this is intended to be used exactly for. This commit transitions our usages of these custom metadata objects into the extensions object where they belong.
1 parent 753e60c commit 25a7c15

17 files changed

+200
-187
lines changed

packages/apollo-federation/src/composition/__tests__/compose.test.ts

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
selectionSetSerializer,
1212
} from '../../snapshotSerializers';
1313
import { normalizeTypeDefs } from '../normalize';
14+
import { getFederationMetadata } from '../utils';
1415

1516
expect.addSnapshotSerializer(astSerializer);
1617
expect.addSnapshotSerializer(typeSerializer);
@@ -59,8 +60,8 @@ describe('composeServices', () => {
5960
const product = schema.getType('Product') as GraphQLObjectType;
6061
const user = schema.getType('User') as GraphQLObjectType;
6162

62-
expect(product.federation.serviceName).toEqual('serviceA');
63-
expect(user.federation.serviceName).toEqual('serviceB');
63+
expect(getFederationMetadata(product).serviceName).toEqual('serviceA');
64+
expect(getFederationMetadata(user).serviceName).toEqual('serviceB');
6465
});
6566

6667
it("doesn't leave federation directives in the final schema", () => {
@@ -115,8 +116,8 @@ describe('composeServices', () => {
115116

116117
const product = schema.getType('Product') as GraphQLObjectType;
117118

118-
expect(product.federation.serviceName).toEqual('serviceA');
119-
expect(product.getFields()['price'].federation.serviceName).toEqual(
119+
expect(getFederationMetadata(product).serviceName).toEqual('serviceA');
120+
expect(getFederationMetadata(product.getFields()['price']).serviceName).toEqual(
120121
'serviceB',
121122
);
122123
});
@@ -154,8 +155,8 @@ describe('composeServices', () => {
154155

155156
const product = schema.getType('Product') as GraphQLObjectType;
156157

157-
expect(product.federation.serviceName).toEqual('serviceB');
158-
expect(product.getFields()['price'].federation.serviceName).toEqual(
158+
expect(getFederationMetadata(product).serviceName).toEqual('serviceB');
159+
expect(getFederationMetadata(product.getFields()['price']).serviceName).toEqual(
159160
'serviceA',
160161
);
161162
});
@@ -208,11 +209,11 @@ describe('composeServices', () => {
208209

209210
const product = schema.getType('Product') as GraphQLObjectType;
210211

211-
expect(product.federation.serviceName).toEqual('serviceB');
212-
expect(product.getFields()['price'].federation.serviceName).toEqual(
212+
expect(getFederationMetadata(product).serviceName).toEqual('serviceB');
213+
expect(getFederationMetadata(product.getFields()['price']).serviceName).toEqual(
213214
'serviceA',
214215
);
215-
expect(product.getFields()['color'].federation.serviceName).toEqual(
216+
expect(getFederationMetadata(product.getFields()['color']).serviceName).toEqual(
216217
'serviceC',
217218
);
218219
});
@@ -269,8 +270,8 @@ describe('composeServices', () => {
269270
}
270271
`);
271272

272-
expect(product.federation.serviceName).toEqual('serviceB');
273-
expect(product.getFields()['price'].federation.serviceName).toEqual(
273+
expect(getFederationMetadata(product).serviceName).toEqual('serviceB');
274+
expect(getFederationMetadata(product.getFields()['price']).serviceName).toEqual(
274275
'serviceC',
275276
);
276277
});
@@ -353,10 +354,10 @@ describe('composeServices', () => {
353354
name: String!
354355
}
355356
`);
356-
expect(product.getFields()['sku'].federation.serviceName).toEqual(
357+
expect(getFederationMetadata(product.getFields()['sku']).serviceName).toEqual(
357358
'serviceB',
358359
);
359-
expect(product.getFields()['name'].federation.serviceName).toEqual(
360+
expect(getFederationMetadata(product.getFields()['name']).serviceName).toEqual(
360361
'serviceB',
361362
);
362363
});
@@ -398,7 +399,7 @@ describe('composeServices', () => {
398399
name: String!
399400
}
400401
`);
401-
expect(product.getFields()['name'].federation.serviceName).toEqual(
402+
expect(getFederationMetadata(product.getFields()['name']).serviceName).toEqual(
402403
'serviceB',
403404
);
404405
});
@@ -445,7 +446,7 @@ describe('composeServices', () => {
445446
name: String!
446447
}
447448
`);
448-
expect(product.getFields()['name'].federation.serviceName).toEqual(
449+
expect(getFederationMetadata(product.getFields()['name']).serviceName).toEqual(
449450
'serviceB',
450451
);
451452
});
@@ -595,8 +596,8 @@ describe('composeServices', () => {
595596

596597
const product = schema.getType('Product') as GraphQLObjectType;
597598

598-
expect(product.federation.serviceName).toEqual('serviceA');
599-
expect(product.getFields()['id'].federation.serviceName).toEqual(
599+
expect(getFederationMetadata(product).serviceName).toEqual('serviceA');
600+
expect(getFederationMetadata(product.getFields()['id']).serviceName).toEqual(
600601
'serviceB',
601602
);
602603
});
@@ -635,7 +636,7 @@ describe('composeServices', () => {
635636

636637
const query = schema.getQueryType();
637638

638-
expect(query.federation.serviceName).toBeUndefined();
639+
expect(getFederationMetadata(query).serviceName).toBeUndefined();
639640
});
640641

641642
it('treats root Query type definition as an extension, not base definitions', () => {
@@ -676,7 +677,7 @@ describe('composeServices', () => {
676677

677678
const query = schema.getType('Query') as GraphQLObjectType;
678679

679-
expect(query.federation.serviceName).toBeUndefined();
680+
expect(getFederationMetadata(query).serviceName).toBeUndefined();
680681
});
681682

682683
it('allows extension of the Mutation type with no base type definition', () => {
@@ -806,7 +807,7 @@ describe('composeServices', () => {
806807

807808
const product = schema.getType('Product');
808809

809-
expect(product.federation.externals).toMatchInlineSnapshot(`
810+
expect(getFederationMetadata(product).externals).toMatchInlineSnapshot(`
810811
Object {
811812
"serviceB--MISSING": Array [
812813
Object {
@@ -864,10 +865,10 @@ describe('composeServices', () => {
864865
price: Int!
865866
}
866867
`);
867-
expect(product.getFields()['price'].federation.serviceName).toEqual(
868+
expect(getFederationMetadata(product.getFields()['price']).serviceName).toEqual(
868869
'serviceB',
869870
);
870-
expect(product.federation.serviceName).toEqual('serviceA');
871+
expect(getFederationMetadata(product).serviceName).toEqual('serviceA');
871872
});
872873
});
873874

@@ -897,7 +898,7 @@ describe('composeServices', () => {
897898

898899
const product = schema.getType('Product') as GraphQLObjectType;
899900
expect(
900-
product.getFields()['price'].federation.requires,
901+
getFederationMetadata(product.getFields()['price']).requires,
901902
).toMatchInlineSnapshot(`sku`);
902903
});
903904

@@ -930,7 +931,7 @@ describe('composeServices', () => {
930931
expect(errors).toHaveLength(0);
931932

932933
const product = schema.getType('Product') as GraphQLObjectType;
933-
expect(product.getFields()['price'].federation.requires)
934+
expect(getFederationMetadata(product.getFields()['price']).requires)
934935
.toMatchInlineSnapshot(`
935936
sku {
936937
id
@@ -970,7 +971,7 @@ describe('composeServices', () => {
970971
expect(errors).toHaveLength(0);
971972

972973
const review = schema.getType('Review') as GraphQLObjectType;
973-
expect(review.getFields()['product'].federation).toMatchInlineSnapshot(`
974+
expect(getFederationMetadata(review.getFields()['product'])).toMatchInlineSnapshot(`
974975
Object {
975976
"belongsToValueType": false,
976977
"provides": sku,
@@ -1013,7 +1014,7 @@ describe('composeServices', () => {
10131014
expect(errors).toHaveLength(0);
10141015

10151016
const review = schema.getType('Review') as GraphQLObjectType;
1016-
expect(review.getFields()['product'].federation.provides)
1017+
expect(getFederationMetadata(review.getFields()['product']).provides)
10171018
.toMatchInlineSnapshot(`
10181019
sku {
10191020
id
@@ -1050,7 +1051,7 @@ describe('composeServices', () => {
10501051
expect(errors).toHaveLength(0);
10511052

10521053
const review = schema.getType('Review') as GraphQLObjectType;
1053-
expect(review.getFields()['products'].federation)
1054+
expect(getFederationMetadata(review.getFields()['products']))
10541055
.toMatchInlineSnapshot(`
10551056
Object {
10561057
"belongsToValueType": false,
@@ -1099,9 +1100,9 @@ describe('composeServices', () => {
10991100
expect(errors).toHaveLength(0);
11001101

11011102
const valueType = schema.getType('ValueType') as GraphQLObjectType;
1102-
const userField = valueType.getFields()['user'].federation;
1103-
expect(userField.belongsToValueType).toBe(true);
1104-
expect(userField.serviceName).toBe(null);
1103+
const userFieldFederationMetadata = getFederationMetadata(valueType.getFields()['user']);
1104+
expect(userFieldFederationMetadata.belongsToValueType).toBe(true);
1105+
expect(userFieldFederationMetadata.serviceName).toBe(null);
11051106
});
11061107
});
11071108

@@ -1131,7 +1132,7 @@ describe('composeServices', () => {
11311132
expect(errors).toHaveLength(0);
11321133

11331134
const product = schema.getType('Product') as GraphQLObjectType;
1134-
expect(product.federation.keys).toMatchInlineSnapshot(`
1135+
expect(getFederationMetadata(product).keys).toMatchInlineSnapshot(`
11351136
Object {
11361137
"serviceA": Array [
11371138
sku,
@@ -1172,7 +1173,7 @@ describe('composeServices', () => {
11721173
expect(errors).toHaveLength(0);
11731174

11741175
const product = schema.getType('Product') as GraphQLObjectType;
1175-
expect(product.federation.keys).toMatchInlineSnapshot(`
1176+
expect(getFederationMetadata(product).keys).toMatchInlineSnapshot(`
11761177
Object {
11771178
"serviceA": Array [
11781179
color {
@@ -1215,7 +1216,7 @@ describe('composeServices', () => {
12151216
expect(errors).toHaveLength(0);
12161217

12171218
const product = schema.getType('Product') as GraphQLObjectType;
1218-
expect(product.federation.keys).toMatchInlineSnapshot(`
1219+
expect(getFederationMetadata(product).keys).toMatchInlineSnapshot(`
12191220
Object {
12201221
"serviceA": Array [
12211222
color {

packages/apollo-federation/src/composition/compose.ts

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,15 @@ import {
3131
executableDirectiveLocations,
3232
stripTypeSystemDirectivesFromTypeDefs,
3333
defaultRootOperationNameLookup,
34+
getFederationMetadata,
3435
} from './utils';
3536
import {
3637
ServiceDefinition,
3738
ExternalFieldDefinition,
3839
ServiceNameToKeyDirectivesMap,
40+
FederationType,
41+
FederationField,
42+
FederationDirective,
3943
} from './types';
4044
import { validateSDL } from 'graphql/validation/validate';
4145
import { compositionRules } from './rules';
@@ -410,13 +414,18 @@ export function addFederationMetadataToSchemaNodes({
410414
const isValueType = valueTypes.has(typeName);
411415
const serviceName = isValueType ? null : owningService;
412416

413-
namedType.federation = {
414-
...namedType.federation,
417+
const federationMetadata: FederationType = {
418+
...getFederationMetadata(namedType),
415419
serviceName,
416420
isValueType,
417421
...(keyDirectivesMap[typeName] && {
418422
keys: keyDirectivesMap[typeName],
419423
}),
424+
}
425+
426+
namedType.extensions = {
427+
...namedType.extensions,
428+
federation: federationMetadata,
420429
};
421430

422431
// For object types, add metadata for all the @provides directives from its fields
@@ -432,13 +441,18 @@ export function addFederationMetadataToSchemaNodes({
432441
providesDirective.arguments &&
433442
isStringValueNode(providesDirective.arguments[0].value)
434443
) {
435-
field.federation = {
436-
...field.federation,
444+
const fieldFederationMetadata: FederationField = {
445+
...getFederationMetadata(field),
437446
serviceName,
438447
provides: parseSelections(
439448
providesDirective.arguments[0].value.value,
440449
),
441450
belongsToValueType: isValueType,
451+
}
452+
453+
field.extensions = {
454+
...field.extensions,
455+
federation: fieldFederationMetadata
442456
};
443457
}
444458
}
@@ -455,9 +469,15 @@ export function addFederationMetadataToSchemaNodes({
455469
// TODO: Why don't we need to check for non-object types here
456470
if (isObjectType(namedType)) {
457471
const field = namedType.getFields()[fieldName];
458-
field.federation = {
459-
...field.federation,
472+
473+
const fieldFederationMetadata: FederationField = {
474+
...getFederationMetadata(field),
460475
serviceName: extendingServiceName,
476+
}
477+
478+
field.extensions = {
479+
...field.extensions,
480+
federation: fieldFederationMetadata,
461481
};
462482

463483
const [requiresDirective] = findDirectivesOnTypeOrField(
@@ -470,11 +490,16 @@ export function addFederationMetadataToSchemaNodes({
470490
requiresDirective.arguments &&
471491
isStringValueNode(requiresDirective.arguments[0].value)
472492
) {
473-
field.federation = {
474-
...field.federation,
493+
const fieldFederationMetadata: FederationField = {
494+
...getFederationMetadata(field),
475495
requires: parseSelections(
476496
requiresDirective.arguments[0].value.value,
477497
),
498+
}
499+
500+
field.extensions = {
501+
...field.extensions,
502+
federation: fieldFederationMetadata,
478503
};
479504
}
480505
}
@@ -485,31 +510,38 @@ export function addFederationMetadataToSchemaNodes({
485510
const namedType = schema.getType(field.parentTypeName);
486511
if (!namedType) continue;
487512

488-
namedType.federation = {
489-
...namedType.federation,
513+
const existingMetadata = getFederationMetadata(namedType);
514+
const typeFederationMetadata: FederationType = {
515+
...existingMetadata,
490516
externals: {
491-
...(namedType.federation && namedType.federation.externals),
517+
...existingMetadata?.externals,
492518
[field.serviceName]: [
493-
...(namedType.federation &&
494-
namedType.federation.externals &&
495-
namedType.federation.externals[field.serviceName]
496-
? namedType.federation.externals[field.serviceName]
497-
: []),
519+
...(existingMetadata?.externals?.[field.serviceName] || []),
498520
field,
499521
],
500522
},
501523
};
524+
525+
namedType.extensions = {
526+
...namedType.extensions,
527+
federation: typeFederationMetadata,
528+
};
502529
}
503530

504531
// add all definitions of a specific directive for validation later
505532
for (const directiveName of Object.keys(directiveDefinitionsMap)) {
506533
const directive = schema.getDirective(directiveName);
507534
if (!directive) continue;
508535

509-
directive.federation = {
510-
...directive.federation,
536+
const directiveFederationMetadata: FederationDirective = {
537+
...getFederationMetadata(directive),
511538
directiveDefinitions: directiveDefinitionsMap[directiveName],
512-
};
539+
}
540+
541+
directive.extensions = {
542+
...directive.extensions,
543+
federation: directiveFederationMetadata,
544+
}
513545
}
514546
}
515547

0 commit comments

Comments
 (0)