@apollo/gateway 0.300.0-alpha.2 → 2.0.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/LICENSE +95 -0
  2. package/README.md +1 -1
  3. package/dist/__generated__/graphqlTypes.d.ts +130 -0
  4. package/dist/__generated__/graphqlTypes.d.ts.map +1 -0
  5. package/dist/__generated__/graphqlTypes.js +25 -0
  6. package/dist/__generated__/graphqlTypes.js.map +1 -0
  7. package/dist/config.d.ts +104 -0
  8. package/dist/config.d.ts.map +1 -0
  9. package/dist/config.js +47 -0
  10. package/dist/config.js.map +1 -0
  11. package/dist/datasources/LocalGraphQLDataSource.d.ts +3 -3
  12. package/dist/datasources/LocalGraphQLDataSource.d.ts.map +1 -1
  13. package/dist/datasources/LocalGraphQLDataSource.js +5 -5
  14. package/dist/datasources/LocalGraphQLDataSource.js.map +1 -1
  15. package/dist/datasources/RemoteGraphQLDataSource.d.ts +6 -4
  16. package/dist/datasources/RemoteGraphQLDataSource.d.ts.map +1 -1
  17. package/dist/datasources/RemoteGraphQLDataSource.js +60 -17
  18. package/dist/datasources/RemoteGraphQLDataSource.js.map +1 -1
  19. package/dist/datasources/index.d.ts +1 -1
  20. package/dist/datasources/index.d.ts.map +1 -1
  21. package/dist/datasources/index.js +1 -0
  22. package/dist/datasources/index.js.map +1 -1
  23. package/dist/datasources/parseCacheControlHeader.d.ts +2 -0
  24. package/dist/datasources/parseCacheControlHeader.d.ts.map +1 -0
  25. package/dist/datasources/parseCacheControlHeader.js +16 -0
  26. package/dist/datasources/parseCacheControlHeader.js.map +1 -0
  27. package/dist/datasources/types.d.ts +16 -1
  28. package/dist/datasources/types.d.ts.map +1 -1
  29. package/dist/datasources/types.js +7 -0
  30. package/dist/datasources/types.js.map +1 -1
  31. package/dist/executeQueryPlan.d.ts +2 -1
  32. package/dist/executeQueryPlan.d.ts.map +1 -1
  33. package/dist/executeQueryPlan.js +199 -112
  34. package/dist/executeQueryPlan.js.map +1 -1
  35. package/dist/index.d.ts +62 -80
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +543 -234
  38. package/dist/index.js.map +1 -1
  39. package/dist/loadServicesFromRemoteEndpoint.d.ts +9 -9
  40. package/dist/loadServicesFromRemoteEndpoint.d.ts.map +1 -1
  41. package/dist/loadServicesFromRemoteEndpoint.js +13 -8
  42. package/dist/loadServicesFromRemoteEndpoint.js.map +1 -1
  43. package/dist/loadSupergraphSdlFromStorage.d.ts +13 -0
  44. package/dist/loadSupergraphSdlFromStorage.d.ts.map +1 -0
  45. package/dist/loadSupergraphSdlFromStorage.js +101 -0
  46. package/dist/loadSupergraphSdlFromStorage.js.map +1 -0
  47. package/dist/operationContext.d.ts +17 -0
  48. package/dist/operationContext.d.ts.map +1 -0
  49. package/dist/operationContext.js +42 -0
  50. package/dist/operationContext.js.map +1 -0
  51. package/dist/outOfBandReporter.d.ts +15 -0
  52. package/dist/outOfBandReporter.d.ts.map +1 -0
  53. package/dist/outOfBandReporter.js +88 -0
  54. package/dist/outOfBandReporter.js.map +1 -0
  55. package/dist/utilities/array.d.ts +1 -2
  56. package/dist/utilities/array.d.ts.map +1 -1
  57. package/dist/utilities/array.js +7 -14
  58. package/dist/utilities/array.js.map +1 -1
  59. package/dist/utilities/assert.d.ts +2 -0
  60. package/dist/utilities/assert.d.ts.map +1 -0
  61. package/dist/utilities/assert.js +10 -0
  62. package/dist/utilities/assert.js.map +1 -0
  63. package/dist/utilities/cleanErrorOfInaccessibleNames.d.ts +3 -0
  64. package/dist/utilities/cleanErrorOfInaccessibleNames.d.ts.map +1 -0
  65. package/dist/utilities/cleanErrorOfInaccessibleNames.js +27 -0
  66. package/dist/utilities/cleanErrorOfInaccessibleNames.js.map +1 -0
  67. package/dist/utilities/deepMerge.js +2 -2
  68. package/dist/utilities/deepMerge.js.map +1 -1
  69. package/dist/utilities/graphql.d.ts +1 -4
  70. package/dist/utilities/graphql.d.ts.map +1 -1
  71. package/dist/utilities/graphql.js +3 -36
  72. package/dist/utilities/graphql.js.map +1 -1
  73. package/dist/utilities/opentelemetry.d.ts +10 -0
  74. package/dist/utilities/opentelemetry.d.ts.map +1 -0
  75. package/dist/utilities/opentelemetry.js +19 -0
  76. package/dist/utilities/opentelemetry.js.map +1 -0
  77. package/package.json +30 -21
  78. package/src/__generated__/graphqlTypes.ts +140 -0
  79. package/src/__mocks__/apollo-server-env.ts +56 -0
  80. package/src/__mocks__/make-fetch-happen-fetcher.ts +55 -0
  81. package/src/__mocks__/tsconfig.json +7 -0
  82. package/src/__tests__/build-query-plan.feature +40 -311
  83. package/src/__tests__/buildQueryPlan.test.ts +246 -426
  84. package/src/__tests__/executeQueryPlan.test.ts +1691 -194
  85. package/src/__tests__/execution-utils.ts +33 -26
  86. package/src/__tests__/gateway/__snapshots__/opentelemetry.test.ts.snap +195 -0
  87. package/src/__tests__/gateway/buildService.test.ts +16 -19
  88. package/src/__tests__/gateway/composedSdl.test.ts +44 -0
  89. package/src/__tests__/gateway/endToEnd.test.ts +166 -0
  90. package/src/__tests__/gateway/executor.test.ts +49 -43
  91. package/src/__tests__/gateway/lifecycle-hooks.test.ts +58 -29
  92. package/src/__tests__/gateway/opentelemetry.test.ts +123 -0
  93. package/src/__tests__/gateway/queryPlanCache.test.ts +19 -20
  94. package/src/__tests__/gateway/reporting.test.ts +76 -55
  95. package/src/__tests__/integration/abstract-types.test.ts +1086 -22
  96. package/src/__tests__/integration/aliases.test.ts +5 -6
  97. package/src/__tests__/integration/boolean.test.ts +40 -38
  98. package/src/__tests__/integration/complex-key.test.ts +41 -56
  99. package/src/__tests__/integration/configuration.test.ts +321 -0
  100. package/src/__tests__/integration/custom-directives.test.ts +61 -46
  101. package/src/__tests__/integration/fragments.test.ts +8 -2
  102. package/src/__tests__/integration/list-key.test.ts +2 -2
  103. package/src/__tests__/integration/logger.test.ts +2 -2
  104. package/src/__tests__/integration/multiple-key.test.ts +11 -12
  105. package/src/__tests__/integration/mutations.test.ts +8 -5
  106. package/src/__tests__/integration/networkRequests.test.ts +447 -289
  107. package/src/__tests__/integration/nockMocks.ts +95 -66
  108. package/src/__tests__/integration/provides.test.ts +9 -6
  109. package/src/__tests__/integration/requires.test.ts +17 -15
  110. package/src/__tests__/integration/scope.test.ts +557 -0
  111. package/src/__tests__/integration/unions.test.ts +1 -1
  112. package/src/__tests__/integration/value-types.test.ts +35 -32
  113. package/src/__tests__/integration/variables.test.ts +8 -2
  114. package/src/__tests__/loadServicesFromRemoteEndpoint.test.ts +6 -2
  115. package/src/__tests__/loadSupergraphSdlFromStorage.test.ts +694 -0
  116. package/src/__tests__/queryPlanCucumber.test.ts +11 -61
  117. package/src/__tests__/testSetup.ts +1 -4
  118. package/src/__tests__/tsconfig.json +2 -1
  119. package/src/config.ts +225 -0
  120. package/src/core/__tests__/core.test.ts +412 -0
  121. package/src/datasources/LocalGraphQLDataSource.ts +9 -10
  122. package/src/datasources/RemoteGraphQLDataSource.ts +117 -43
  123. package/src/datasources/__tests__/LocalGraphQLDataSource.test.ts +11 -4
  124. package/src/datasources/__tests__/RemoteGraphQLDataSource.test.ts +148 -79
  125. package/src/datasources/__tests__/tsconfig.json +4 -2
  126. package/src/datasources/index.ts +1 -1
  127. package/src/datasources/parseCacheControlHeader.ts +43 -0
  128. package/src/datasources/types.ts +47 -2
  129. package/src/executeQueryPlan.ts +264 -153
  130. package/src/index.ts +925 -480
  131. package/src/loadServicesFromRemoteEndpoint.ts +24 -17
  132. package/src/loadSupergraphSdlFromStorage.ts +140 -0
  133. package/src/make-fetch-happen.d.ts +2 -2
  134. package/src/operationContext.ts +70 -0
  135. package/src/outOfBandReporter.ts +128 -0
  136. package/src/utilities/__tests__/cleanErrorOfInaccessibleElements.test.ts +104 -0
  137. package/src/utilities/__tests__/tsconfig.json +8 -0
  138. package/src/utilities/array.ts +6 -28
  139. package/src/utilities/assert.ts +14 -0
  140. package/src/utilities/cleanErrorOfInaccessibleNames.ts +29 -0
  141. package/src/utilities/graphql.ts +0 -64
  142. package/src/utilities/opentelemetry.ts +13 -0
  143. package/CHANGELOG.md +0 -226
  144. package/LICENSE.md +0 -20
  145. package/dist/FieldSet.d.ts +0 -18
  146. package/dist/FieldSet.d.ts.map +0 -1
  147. package/dist/FieldSet.js +0 -96
  148. package/dist/FieldSet.js.map +0 -1
  149. package/dist/QueryPlan.d.ts +0 -41
  150. package/dist/QueryPlan.d.ts.map +0 -1
  151. package/dist/QueryPlan.js +0 -15
  152. package/dist/QueryPlan.js.map +0 -1
  153. package/dist/buildQueryPlan.d.ts +0 -44
  154. package/dist/buildQueryPlan.d.ts.map +0 -1
  155. package/dist/buildQueryPlan.js +0 -670
  156. package/dist/buildQueryPlan.js.map +0 -1
  157. package/dist/loadServicesFromStorage.d.ts +0 -21
  158. package/dist/loadServicesFromStorage.d.ts.map +0 -1
  159. package/dist/loadServicesFromStorage.js +0 -64
  160. package/dist/loadServicesFromStorage.js.map +0 -1
  161. package/dist/snapshotSerializers/astSerializer.d.ts +0 -3
  162. package/dist/snapshotSerializers/astSerializer.d.ts.map +0 -1
  163. package/dist/snapshotSerializers/astSerializer.js +0 -14
  164. package/dist/snapshotSerializers/astSerializer.js.map +0 -1
  165. package/dist/snapshotSerializers/index.d.ts +0 -13
  166. package/dist/snapshotSerializers/index.d.ts.map +0 -1
  167. package/dist/snapshotSerializers/index.js +0 -15
  168. package/dist/snapshotSerializers/index.js.map +0 -1
  169. package/dist/snapshotSerializers/queryPlanSerializer.d.ts +0 -3
  170. package/dist/snapshotSerializers/queryPlanSerializer.d.ts.map +0 -1
  171. package/dist/snapshotSerializers/queryPlanSerializer.js +0 -78
  172. package/dist/snapshotSerializers/queryPlanSerializer.js.map +0 -1
  173. package/dist/snapshotSerializers/selectionSetSerializer.d.ts +0 -3
  174. package/dist/snapshotSerializers/selectionSetSerializer.d.ts.map +0 -1
  175. package/dist/snapshotSerializers/selectionSetSerializer.js +0 -12
  176. package/dist/snapshotSerializers/selectionSetSerializer.js.map +0 -1
  177. package/dist/snapshotSerializers/typeSerializer.d.ts +0 -3
  178. package/dist/snapshotSerializers/typeSerializer.d.ts.map +0 -1
  179. package/dist/snapshotSerializers/typeSerializer.js +0 -12
  180. package/dist/snapshotSerializers/typeSerializer.js.map +0 -1
  181. package/dist/utilities/MultiMap.d.ts +0 -4
  182. package/dist/utilities/MultiMap.d.ts.map +0 -1
  183. package/dist/utilities/MultiMap.js +0 -17
  184. package/dist/utilities/MultiMap.js.map +0 -1
  185. package/src/FieldSet.ts +0 -169
  186. package/src/QueryPlan.ts +0 -57
  187. package/src/__tests__/matchers/toCallService.ts +0 -105
  188. package/src/__tests__/matchers/toHaveBeenCalledBefore.ts +0 -40
  189. package/src/__tests__/matchers/toHaveFetched.ts +0 -81
  190. package/src/__tests__/matchers/toMatchAST.ts +0 -64
  191. package/src/buildQueryPlan.ts +0 -1190
  192. package/src/loadServicesFromStorage.ts +0 -170
  193. package/src/snapshotSerializers/astSerializer.ts +0 -21
  194. package/src/snapshotSerializers/index.ts +0 -21
  195. package/src/snapshotSerializers/queryPlanSerializer.ts +0 -144
  196. package/src/snapshotSerializers/selectionSetSerializer.ts +0 -13
  197. package/src/snapshotSerializers/typeSerializer.ts +0 -11
  198. package/src/utilities/MultiMap.ts +0 -11
@@ -1,1190 +0,0 @@
1
- import { isNotNullOrUndefined } from 'apollo-env';
2
- import {
3
- DocumentNode,
4
- FieldNode,
5
- FragmentDefinitionNode,
6
- getNamedType,
7
- getOperationRootType,
8
- GraphQLAbstractType,
9
- GraphQLCompositeType,
10
- GraphQLError,
11
- GraphQLField,
12
- GraphQLObjectType,
13
- GraphQLSchema,
14
- GraphQLType,
15
- InlineFragmentNode,
16
- isAbstractType,
17
- isCompositeType,
18
- isIntrospectionType,
19
- isListType,
20
- isNamedType,
21
- isObjectType,
22
- Kind,
23
- OperationDefinitionNode,
24
- SelectionSetNode,
25
- typeFromAST,
26
- TypeNameMetaFieldDef,
27
- visit,
28
- VariableDefinitionNode,
29
- OperationTypeNode,
30
- print,
31
- stripIgnoredCharacters,
32
- } from 'graphql';
33
- import {
34
- Field,
35
- FieldSet,
36
- groupByParentType,
37
- groupByResponseName,
38
- matchesField,
39
- selectionSetFromFieldSet,
40
- Scope,
41
- } from './FieldSet';
42
- import {
43
- FetchNode,
44
- ParallelNode,
45
- PlanNode,
46
- SequenceNode,
47
- QueryPlan,
48
- ResponsePath,
49
- OperationContext,
50
- FragmentMap,
51
- } from './QueryPlan';
52
- import { getFieldDef, getResponseName } from './utilities/graphql';
53
- import { MultiMap } from './utilities/MultiMap';
54
- import { getFederationMetadata } from '@apollo/federation/dist/composition/utils';
55
-
56
- const typenameField = {
57
- kind: Kind.FIELD,
58
- name: {
59
- kind: Kind.NAME,
60
- value: TypeNameMetaFieldDef.name,
61
- },
62
- };
63
-
64
- export interface BuildQueryPlanOptions {
65
- autoFragmentization: boolean;
66
- }
67
-
68
- export function buildQueryPlan(
69
- operationContext: OperationContext,
70
- options: BuildQueryPlanOptions = { autoFragmentization: false },
71
- ): QueryPlan {
72
- const context = buildQueryPlanningContext(operationContext, options);
73
-
74
- if (context.operation.operation === 'subscription') {
75
- throw new GraphQLError(
76
- 'Query planning does not support subscriptions for now.',
77
- [context.operation],
78
- );
79
- }
80
-
81
- const rootType = getOperationRootType(context.schema, context.operation);
82
-
83
- const isMutation = context.operation.operation === 'mutation';
84
-
85
- const fields = collectFields(
86
- context,
87
- context.newScope(rootType),
88
- context.operation.selectionSet,
89
- );
90
-
91
- // Mutations are a bit more specific in how FetchGroups can be built, as some
92
- // calls to the same service may need to be executed serially.
93
- const groups = isMutation
94
- ? splitRootFieldsSerially(context, fields)
95
- : splitRootFields(context, fields);
96
-
97
- const nodes = groups.map(group =>
98
- executionNodeForGroup(context, group, rootType),
99
- );
100
-
101
- return {
102
- kind: 'QueryPlan',
103
- node: nodes.length
104
- ? flatWrap(isMutation ? 'Sequence' : 'Parallel', nodes)
105
- : undefined,
106
- };
107
- }
108
-
109
- function executionNodeForGroup(
110
- context: QueryPlanningContext,
111
- {
112
- serviceName,
113
- fields,
114
- requiredFields,
115
- internalFragments,
116
- mergeAt,
117
- dependentGroups,
118
- }: FetchGroup,
119
- parentType?: GraphQLCompositeType,
120
- ): PlanNode {
121
- const selectionSet = selectionSetFromFieldSet(fields, parentType);
122
- const requires =
123
- requiredFields.length > 0
124
- ? selectionSetFromFieldSet(requiredFields)
125
- : undefined;
126
- const variableUsages = context.getVariableUsages(
127
- selectionSet,
128
- internalFragments,
129
- );
130
-
131
- const operation = requires
132
- ? operationForEntitiesFetch({
133
- selectionSet,
134
- variableUsages,
135
- internalFragments,
136
- })
137
- : operationForRootFetch({
138
- selectionSet,
139
- variableUsages,
140
- internalFragments,
141
- operation: context.operation.operation,
142
- });
143
-
144
- const fetchNode: FetchNode = {
145
- kind: 'Fetch',
146
- serviceName,
147
- selectionSet,
148
- requires,
149
- variableUsages,
150
- internalFragments,
151
- source: stripIgnoredCharacters(print(operation)),
152
- };
153
-
154
- const node: PlanNode =
155
- mergeAt && mergeAt.length > 0
156
- ? {
157
- kind: 'Flatten',
158
- path: mergeAt,
159
- node: fetchNode,
160
- }
161
- : fetchNode;
162
-
163
- if (dependentGroups.length > 0) {
164
- const dependentNodes = dependentGroups.map(dependentGroup =>
165
- executionNodeForGroup(context, dependentGroup),
166
- );
167
-
168
- return flatWrap('Sequence', [node, flatWrap('Parallel', dependentNodes)]);
169
- } else {
170
- return node;
171
- }
172
- }
173
-
174
- interface VariableUsages {
175
- [name: string]: VariableDefinitionNode
176
- }
177
-
178
- function mapFetchNodeToVariableDefinitions(
179
- variableUsages: VariableUsages,
180
- ): VariableDefinitionNode[] {
181
- return variableUsages ? Object.values(variableUsages) : [];
182
- }
183
-
184
- function operationForRootFetch({
185
- selectionSet,
186
- variableUsages,
187
- internalFragments,
188
- operation = 'query',
189
- }: {
190
- selectionSet: SelectionSetNode;
191
- variableUsages: VariableUsages;
192
- internalFragments: Set<FragmentDefinitionNode>;
193
- operation?: OperationTypeNode;
194
- }): DocumentNode {
195
- return {
196
- kind: Kind.DOCUMENT,
197
- definitions: [
198
- {
199
- kind: Kind.OPERATION_DEFINITION,
200
- operation,
201
- selectionSet,
202
- variableDefinitions: mapFetchNodeToVariableDefinitions(variableUsages),
203
- },
204
- ...internalFragments,
205
- ],
206
- };
207
- }
208
-
209
- function operationForEntitiesFetch({
210
- selectionSet,
211
- variableUsages,
212
- internalFragments,
213
- }: {
214
- selectionSet: SelectionSetNode;
215
- variableUsages: VariableUsages;
216
- internalFragments: Set<FragmentDefinitionNode>;
217
- }): DocumentNode {
218
- const representationsVariable = {
219
- kind: Kind.VARIABLE,
220
- name: { kind: Kind.NAME, value: 'representations' },
221
- };
222
-
223
- return {
224
- kind: Kind.DOCUMENT,
225
- definitions: [
226
- {
227
- kind: Kind.OPERATION_DEFINITION,
228
- operation: 'query',
229
- variableDefinitions: ([
230
- {
231
- kind: Kind.VARIABLE_DEFINITION,
232
- variable: representationsVariable,
233
- type: {
234
- kind: Kind.NON_NULL_TYPE,
235
- type: {
236
- kind: Kind.LIST_TYPE,
237
- type: {
238
- kind: Kind.NON_NULL_TYPE,
239
- type: {
240
- kind: Kind.NAMED_TYPE,
241
- name: { kind: Kind.NAME, value: '_Any' },
242
- },
243
- },
244
- },
245
- },
246
- },
247
- ] as VariableDefinitionNode[]).concat(
248
- mapFetchNodeToVariableDefinitions(variableUsages),
249
- ),
250
- selectionSet: {
251
- kind: Kind.SELECTION_SET,
252
- selections: [
253
- {
254
- kind: Kind.FIELD,
255
- name: { kind: Kind.NAME, value: '_entities' },
256
- arguments: [
257
- {
258
- kind: Kind.ARGUMENT,
259
- name: {
260
- kind: Kind.NAME,
261
- value: representationsVariable.name.value,
262
- },
263
- value: representationsVariable,
264
- },
265
- ],
266
- selectionSet,
267
- },
268
- ],
269
- },
270
- },
271
- ...internalFragments,
272
- ],
273
- };
274
- }
275
-
276
- // Wraps the given nodes in a ParallelNode or SequenceNode, unless there's only
277
- // one node, in which case it is returned directly. Any nodes of the same kind
278
- // in the given list have their sub-nodes flattened into the list: ie,
279
- // flatWrap('Sequence', [a, flatWrap('Sequence', b, c), d]) returns a SequenceNode
280
- // with four children.
281
- function flatWrap(
282
- kind: ParallelNode['kind'] | SequenceNode['kind'],
283
- nodes: PlanNode[],
284
- ): PlanNode {
285
- if (nodes.length === 0) {
286
- throw Error('programming error: should always be called with nodes');
287
- }
288
- if (nodes.length === 1) {
289
- return nodes[0];
290
- }
291
- return {
292
- kind,
293
- nodes: nodes.flatMap(n => (n.kind === kind ? n.nodes : [n])),
294
- } as PlanNode;
295
- }
296
-
297
- function splitRootFields(
298
- context: QueryPlanningContext,
299
- fields: FieldSet,
300
- ): FetchGroup[] {
301
- const groupsByService: {
302
- [serviceName: string]: FetchGroup;
303
- } = Object.create(null);
304
-
305
- function groupForService(serviceName: string) {
306
- let group = groupsByService[serviceName];
307
-
308
- if (!group) {
309
- group = new FetchGroup(serviceName);
310
- groupsByService[serviceName] = group;
311
- }
312
-
313
- return group;
314
- }
315
-
316
- splitFields(context, [], fields, field => {
317
- const { scope, fieldNode, fieldDef } = field;
318
- const { parentType } = scope;
319
-
320
- const owningService = context.getOwningService(parentType, fieldDef);
321
-
322
- if (!owningService) {
323
- throw new GraphQLError(
324
- `Couldn't find owning service for field "${parentType.name}.${fieldDef.name}"`,
325
- fieldNode,
326
- );
327
- }
328
-
329
- return groupForService(owningService);
330
- });
331
-
332
- return Object.values(groupsByService);
333
- }
334
-
335
- // For mutations, we need to respect the order of the fields, in order to
336
- // determine which fields can be batched together in the same request. If
337
- // they're "split" by fields belonging to other services, then we need to manage
338
- // the proper sequencing at the gateway level. In this example, we need 3
339
- // FetchGroups (requests) in sequence:
340
- //
341
- // mutation abc {
342
- // createReview() # reviews service (1)
343
- // updateReview() # reviews service (1)
344
- // login() # account service (2)
345
- // deleteReview() # reviews service (3)
346
- // }
347
- function splitRootFieldsSerially(
348
- context: QueryPlanningContext,
349
- fields: FieldSet,
350
- ): FetchGroup[] {
351
- const fetchGroups: FetchGroup[] = [];
352
-
353
- function groupForField(serviceName: string) {
354
- let group: FetchGroup;
355
-
356
- // If the most recent FetchGroup in the array belongs to the same service,
357
- // the field in question can be batched within that group.
358
- const previousGroup = fetchGroups[fetchGroups.length - 1];
359
- if (previousGroup && previousGroup.serviceName === serviceName) {
360
- return previousGroup;
361
- }
362
-
363
- // If there's no previous group, or the previous group is from a different
364
- // service, then we need to add a new FetchGroup.
365
- group = new FetchGroup(serviceName);
366
- fetchGroups.push(group);
367
-
368
- return group;
369
- }
370
-
371
- splitFields(context, [], fields, field => {
372
- const { scope, fieldNode, fieldDef } = field;
373
- const { parentType } = scope;
374
-
375
- const owningService = context.getOwningService(parentType, fieldDef);
376
-
377
- if (!owningService) {
378
- throw new GraphQLError(
379
- `Couldn't find owning service for field "${parentType.name}.${fieldDef.name}"`,
380
- fieldNode,
381
- );
382
- }
383
-
384
- return groupForField(owningService);
385
- });
386
-
387
- return fetchGroups;
388
- }
389
-
390
- function splitSubfields(
391
- context: QueryPlanningContext,
392
- path: ResponsePath,
393
- fields: FieldSet,
394
- parentGroup: FetchGroup,
395
- ) {
396
- splitFields(context, path, fields, field => {
397
- const { scope, fieldNode, fieldDef } = field;
398
- const { parentType } = scope;
399
-
400
- let baseService, owningService;
401
-
402
- const parentTypeFederationMetadata = getFederationMetadata(parentType);
403
- if (parentTypeFederationMetadata?.isValueType) {
404
- baseService = parentGroup.serviceName;
405
- owningService = parentGroup.serviceName;
406
- } else {
407
- baseService = context.getBaseService(parentType);
408
- owningService = context.getOwningService(parentType, fieldDef);
409
- }
410
-
411
- if (!baseService) {
412
- throw new GraphQLError(
413
- `Couldn't find base service for type "${parentType.name}"`,
414
- fieldNode,
415
- );
416
- }
417
-
418
- if (!owningService) {
419
- throw new GraphQLError(
420
- `Couldn't find owning service for field "${parentType.name}.${fieldDef.name}"`,
421
- fieldNode,
422
- );
423
- }
424
- // Is the field defined on the base service?
425
- if (owningService === baseService) {
426
- // Can we fetch the field from the parent group?
427
- if (
428
- owningService === parentGroup.serviceName ||
429
- parentGroup.providedFields.some(matchesField(field))
430
- ) {
431
- return parentGroup;
432
- } else {
433
- // We need to fetch the key fields from the parent group first, and then
434
- // use a dependent fetch from the owning service.
435
- let keyFields = context.getKeyFields({
436
- parentType,
437
- serviceName: parentGroup.serviceName,
438
- });
439
- if (
440
- keyFields.length === 0 ||
441
- (keyFields.length === 1 &&
442
- keyFields[0].fieldDef.name === '__typename')
443
- ) {
444
- // Only __typename key found.
445
- // In some cases, the parent group does not have any @key directives.
446
- // Fall back to owning group's keys
447
- keyFields = context.getKeyFields({
448
- parentType,
449
- serviceName: owningService,
450
- });
451
- }
452
- return parentGroup.dependentGroupForService(owningService, keyFields);
453
- }
454
- } else {
455
- // It's an extension field, so we need to fetch the required fields first.
456
- const requiredFields = context.getRequiredFields(
457
- parentType,
458
- fieldDef,
459
- owningService,
460
- );
461
-
462
- // Can we fetch the required fields from the parent group?
463
- if (
464
- requiredFields.every(requiredField =>
465
- parentGroup.providedFields.some(matchesField(requiredField)),
466
- )
467
- ) {
468
- if (owningService === parentGroup.serviceName) {
469
- return parentGroup;
470
- } else {
471
- return parentGroup.dependentGroupForService(
472
- owningService,
473
- requiredFields,
474
- );
475
- }
476
- } else {
477
- // We need to go through the base group first.
478
-
479
- const keyFields = context.getKeyFields({
480
- parentType,
481
- serviceName: parentGroup.serviceName,
482
- });
483
-
484
- if (!keyFields) {
485
- throw new GraphQLError(
486
- `Couldn't find keys for type "${parentType.name}}" in service "${baseService}"`,
487
- fieldNode,
488
- );
489
- }
490
-
491
- if (baseService === parentGroup.serviceName) {
492
- return parentGroup.dependentGroupForService(
493
- owningService,
494
- requiredFields,
495
- );
496
- }
497
-
498
- const baseGroup = parentGroup.dependentGroupForService(
499
- baseService,
500
- keyFields,
501
- );
502
-
503
- return baseGroup.dependentGroupForService(
504
- owningService,
505
- requiredFields,
506
- );
507
- }
508
- }
509
- });
510
- }
511
-
512
- function splitFields(
513
- context: QueryPlanningContext,
514
- path: ResponsePath,
515
- fields: FieldSet,
516
- groupForField: (field: Field<GraphQLObjectType>) => FetchGroup,
517
- ) {
518
- for (const fieldsForResponseName of groupByResponseName(fields).values()) {
519
- for (const [parentType, fieldsForParentType] of groupByParentType(fieldsForResponseName)) {
520
- // Field nodes that share the same response name and parent type are guaranteed
521
- // to have the same field name and arguments. We only need the other nodes when
522
- // merging selection sets, to take node-specific subfields and directives
523
- // into account.
524
-
525
- const field = fieldsForParentType[0];
526
- const { scope, fieldDef } = field;
527
-
528
- // We skip `__typename` for root types.
529
- if (fieldDef.name === TypeNameMetaFieldDef.name) {
530
- const { schema } = context;
531
- const roots = [
532
- schema.getQueryType(),
533
- schema.getMutationType(),
534
- schema.getSubscriptionType(),
535
- ]
536
- .filter(isNotNullOrUndefined)
537
- .map(type => type.name);
538
- if (roots.indexOf(parentType.name) > -1) continue;
539
- }
540
-
541
- // We skip introspection fields like `__schema` and `__type`.
542
- if (isIntrospectionType(getNamedType(fieldDef.type))) {
543
- continue;
544
- }
545
-
546
- if (isObjectType(parentType) && scope.possibleTypes.includes(parentType)) {
547
- // If parent type is an object type, we can directly look for the right
548
- // group.
549
- const group = groupForField(field as Field<GraphQLObjectType>);
550
- group.fields.push(
551
- completeField(
552
- context,
553
- scope as Scope<typeof parentType>,
554
- group,
555
- path,
556
- fieldsForParentType,
557
- ),
558
- );
559
- } else {
560
- // For interfaces however, we need to look at all possible runtime types.
561
-
562
- /**
563
- * The following is an optimization to prevent an explosion of type
564
- * conditions to services when it isn't needed. If all possible runtime
565
- * types can be fufilled by only one service then we don't need to
566
- * expand the fields into unique type conditions.
567
- */
568
-
569
- // Collect all of the field defs on the possible runtime types
570
- const possibleFieldDefs = scope.possibleTypes.map(
571
- runtimeType => context.getFieldDef(runtimeType, field.fieldNode),
572
- );
573
-
574
- // If none of the field defs have a federation property, this interface's
575
- // implementors can all be resolved within the same service.
576
- const hasNoExtendingFieldDefs = !possibleFieldDefs.some(
577
- getFederationMetadata,
578
- );
579
-
580
- // With no extending field definitions, we can engage the optimization
581
- if (hasNoExtendingFieldDefs) {
582
- const group = groupForField(field as Field<GraphQLObjectType>);
583
- group.fields.push(
584
- completeField(context, scope, group, path, fieldsForResponseName)
585
- );
586
- continue;
587
- }
588
-
589
- // We keep track of which possible runtime parent types can be fetched
590
- // from which group,
591
- const groupsByRuntimeParentTypes = new MultiMap<
592
- FetchGroup,
593
- GraphQLObjectType
594
- >();
595
-
596
- for (const runtimeParentType of scope.possibleTypes) {
597
- const fieldDef = context.getFieldDef(
598
- runtimeParentType,
599
- field.fieldNode,
600
- );
601
- groupsByRuntimeParentTypes.add(
602
- groupForField({
603
- scope: context.newScope(runtimeParentType, scope),
604
- fieldNode: field.fieldNode,
605
- fieldDef,
606
- }),
607
- runtimeParentType,
608
- );
609
- }
610
-
611
- // We add the field separately for each runtime parent type.
612
- for (const [group, runtimeParentTypes] of groupsByRuntimeParentTypes) {
613
- for (const runtimeParentType of runtimeParentTypes) {
614
- // We need to adjust the fields to contain the right fieldDef for
615
- // their runtime parent type.
616
-
617
- const fieldDef = context.getFieldDef(
618
- runtimeParentType,
619
- field.fieldNode,
620
- );
621
-
622
- const fieldsWithRuntimeParentType = fieldsForParentType.map(field => ({
623
- ...field,
624
- fieldDef,
625
- }));
626
-
627
- group.fields.push(
628
- completeField(
629
- context,
630
- context.newScope(runtimeParentType, scope),
631
- group,
632
- path,
633
- fieldsWithRuntimeParentType,
634
- ),
635
- );
636
- }
637
- }
638
- }
639
- }
640
- }
641
- }
642
-
643
- function completeField(
644
- context: QueryPlanningContext,
645
- scope: Scope<GraphQLCompositeType>,
646
- parentGroup: FetchGroup,
647
- path: ResponsePath,
648
- fields: FieldSet,
649
- ): Field {
650
- const { fieldNode, fieldDef } = fields[0];
651
- const returnType = getNamedType(fieldDef.type);
652
-
653
- if (!isCompositeType(returnType)) {
654
- // FIXME: We should look at all field nodes to make sure we take directives
655
- // into account (or remove directives for the time being).
656
- return { scope, fieldNode, fieldDef };
657
- } else {
658
- // For composite types, we need to recurse.
659
-
660
- const fieldPath = addPath(path, getResponseName(fieldNode), fieldDef.type);
661
-
662
- const subGroup = new FetchGroup(parentGroup.serviceName);
663
- subGroup.mergeAt = fieldPath;
664
-
665
- subGroup.providedFields = context.getProvidedFields(
666
- fieldDef,
667
- parentGroup.serviceName,
668
- );
669
-
670
- // For abstract types, we always need to request `__typename`
671
- if (isAbstractType(returnType)) {
672
- subGroup.fields.push({
673
- scope: context.newScope(returnType, scope),
674
- fieldNode: typenameField,
675
- fieldDef: TypeNameMetaFieldDef,
676
- });
677
- }
678
-
679
- const subfields = collectSubfields(context, returnType, fields);
680
- splitSubfields(context, fieldPath, subfields, subGroup);
681
-
682
- parentGroup.otherDependentGroups.push(...subGroup.dependentGroups);
683
-
684
- let definition: FragmentDefinitionNode;
685
- let selectionSet = selectionSetFromFieldSet(subGroup.fields, returnType);
686
-
687
- if (context.autoFragmentization && subGroup.fields.length > 2) {
688
- ({ definition, selectionSet } = getInternalFragment(
689
- selectionSet,
690
- returnType,
691
- context,
692
- ));
693
- parentGroup.internalFragments.add(definition);
694
- }
695
-
696
- // "Hoist" internalFragments of the subGroup into the parentGroup so all
697
- // fragments can be included in the final request for the root FetchGroup
698
- subGroup.internalFragments.forEach(fragment => {
699
- parentGroup.internalFragments.add(fragment);
700
- });
701
-
702
- return {
703
- scope,
704
- fieldNode: {
705
- ...fieldNode,
706
- selectionSet,
707
- },
708
- fieldDef,
709
- };
710
- }
711
- }
712
-
713
- function getInternalFragment(
714
- selectionSet: SelectionSetNode,
715
- returnType: GraphQLCompositeType,
716
- context: QueryPlanningContext
717
- ) {
718
- const key = JSON.stringify(selectionSet);
719
- if (!context.internalFragments.has(key)) {
720
- const name = `__QueryPlanFragment_${context.internalFragmentCount++}__`;
721
-
722
- const definition: FragmentDefinitionNode = {
723
- kind: Kind.FRAGMENT_DEFINITION,
724
- name: {
725
- kind: Kind.NAME,
726
- value: name,
727
- },
728
- typeCondition: {
729
- kind: Kind.NAMED_TYPE,
730
- name: {
731
- kind: Kind.NAME,
732
- value: returnType.name,
733
- },
734
- },
735
- selectionSet,
736
- };
737
-
738
- const fragmentSelection: SelectionSetNode = {
739
- kind: Kind.SELECTION_SET,
740
- selections: [
741
- {
742
- kind: Kind.FRAGMENT_SPREAD,
743
- name: {
744
- kind: Kind.NAME,
745
- value: name,
746
- },
747
- },
748
- ],
749
- };
750
-
751
- context.internalFragments.set(key, {
752
- name,
753
- definition,
754
- selectionSet: fragmentSelection,
755
- });
756
- }
757
-
758
- return context.internalFragments.get(key)!;
759
- }
760
-
761
- function collectFields(
762
- context: QueryPlanningContext,
763
- scope: Scope<GraphQLCompositeType>,
764
- selectionSet: SelectionSetNode,
765
- fields: FieldSet = [],
766
- visitedFragmentNames: { [fragmentName: string]: boolean } = Object.create(
767
- null,
768
- ),
769
- ): FieldSet {
770
- for (const selection of selectionSet.selections) {
771
- switch (selection.kind) {
772
- case Kind.FIELD:
773
- const fieldDef = context.getFieldDef(scope.parentType, selection);
774
- fields.push({ scope, fieldNode: selection, fieldDef });
775
- break;
776
- case Kind.INLINE_FRAGMENT: {
777
- const newScope = context.newScope(getFragmentCondition(selection), scope);
778
- if (newScope.possibleTypes.length === 0) {
779
- break;
780
- }
781
-
782
- collectFields(
783
- context,
784
- context.newScope(getFragmentCondition(selection), scope),
785
- selection.selectionSet,
786
- fields,
787
- visitedFragmentNames,
788
- );
789
- break;
790
- }
791
- case Kind.FRAGMENT_SPREAD:
792
- const fragmentName = selection.name.value;
793
-
794
- const fragment = context.fragments[fragmentName];
795
- if (!fragment) {
796
- continue;
797
- }
798
-
799
- const newScope = context.newScope(getFragmentCondition(fragment), scope);
800
- if (newScope.possibleTypes.length === 0) {
801
- continue;
802
- }
803
-
804
- if (visitedFragmentNames[fragmentName]) {
805
- continue;
806
- }
807
- visitedFragmentNames[fragmentName] = true;
808
-
809
- collectFields(
810
- context,
811
- newScope,
812
- fragment.selectionSet,
813
- fields,
814
- visitedFragmentNames,
815
- );
816
- break;
817
- }
818
- }
819
-
820
- return fields;
821
-
822
- function getFragmentCondition(
823
- fragment: FragmentDefinitionNode | InlineFragmentNode,
824
- ): GraphQLCompositeType {
825
- const typeConditionNode = fragment.typeCondition;
826
- if (!typeConditionNode) return scope.parentType;
827
-
828
- return typeFromAST(
829
- context.schema,
830
- typeConditionNode,
831
- ) as GraphQLCompositeType;
832
- }
833
- }
834
-
835
- // Collecting subfields collapses parent types, because it merges
836
- // selection sets without taking the runtime parent type of the field
837
- // into account. If we want to keep track of multiple levels of possible
838
- // types, this is where that would need to happen.
839
- export function collectSubfields(
840
- context: QueryPlanningContext,
841
- returnType: GraphQLCompositeType,
842
- fields: FieldSet,
843
- ): FieldSet {
844
- let subfields: FieldSet = [];
845
- const visitedFragmentNames = Object.create(null);
846
-
847
- for (const field of fields) {
848
- const selectionSet = field.fieldNode.selectionSet;
849
-
850
- if (selectionSet) {
851
- subfields = collectFields(
852
- context,
853
- context.newScope(returnType),
854
- selectionSet,
855
- subfields,
856
- visitedFragmentNames,
857
- );
858
- }
859
- }
860
-
861
- return subfields;
862
- }
863
-
864
- class FetchGroup {
865
- constructor(
866
- public readonly serviceName: string,
867
- public readonly fields: FieldSet = [],
868
- public readonly internalFragments: Set<FragmentDefinitionNode> = new Set()
869
- ) {}
870
-
871
- requiredFields: FieldSet = [];
872
- providedFields: FieldSet = [];
873
-
874
- mergeAt?: ResponsePath;
875
-
876
- private dependentGroupsByService: {
877
- [serviceName: string]: FetchGroup;
878
- } = Object.create(null);
879
- public otherDependentGroups: FetchGroup[] = [];
880
-
881
- dependentGroupForService(serviceName: string, requiredFields: FieldSet) {
882
- let group = this.dependentGroupsByService[serviceName];
883
-
884
- if (!group) {
885
- group = new FetchGroup(serviceName);
886
- group.mergeAt = this.mergeAt;
887
- this.dependentGroupsByService[serviceName] = group;
888
- }
889
-
890
- if (requiredFields) {
891
- if (group.requiredFields) {
892
- group.requiredFields.push(...requiredFields);
893
- } else {
894
- group.requiredFields = requiredFields;
895
- }
896
- this.fields.push(...requiredFields);
897
- }
898
-
899
- return group;
900
- }
901
-
902
- get dependentGroups() {
903
- return [
904
- ...Object.values(this.dependentGroupsByService),
905
- ...this.otherDependentGroups,
906
- ];
907
- }
908
- }
909
-
910
- // Adapted from buildExecutionContext in graphql-js
911
- export function buildOperationContext(
912
- schema: GraphQLSchema,
913
- document: DocumentNode,
914
- operationName?: string,
915
- ): OperationContext {
916
- let operation: OperationDefinitionNode | undefined;
917
- const fragments: {
918
- [fragmentName: string]: FragmentDefinitionNode;
919
- } = Object.create(null);
920
- document.definitions.forEach(definition => {
921
- switch (definition.kind) {
922
- case Kind.OPERATION_DEFINITION:
923
- if (!operationName && operation) {
924
- throw new GraphQLError(
925
- 'Must provide operation name if query contains ' +
926
- 'multiple operations.',
927
- );
928
- }
929
- if (
930
- !operationName ||
931
- (definition.name && definition.name.value === operationName)
932
- ) {
933
- operation = definition;
934
- }
935
- break;
936
- case Kind.FRAGMENT_DEFINITION:
937
- fragments[definition.name.value] = definition;
938
- break;
939
- }
940
- });
941
- if (!operation) {
942
- if (operationName) {
943
- throw new GraphQLError(`Unknown operation named "${operationName}".`);
944
- } else {
945
- throw new GraphQLError('Must provide an operation.');
946
- }
947
- }
948
-
949
- return { schema, operation, fragments };
950
- }
951
-
952
- export function buildQueryPlanningContext(
953
- { operation, schema, fragments }: OperationContext,
954
- options: BuildQueryPlanOptions,
955
- ): QueryPlanningContext {
956
- return new QueryPlanningContext(
957
- schema,
958
- operation,
959
- fragments,
960
- options.autoFragmentization,
961
- );
962
- }
963
-
964
- export class QueryPlanningContext {
965
- public internalFragments: Map<
966
- string,
967
- {
968
- name: string;
969
- definition: FragmentDefinitionNode;
970
- selectionSet: SelectionSetNode;
971
- }
972
- > = new Map();
973
-
974
- public internalFragmentCount = 0;
975
-
976
- protected variableDefinitions: {
977
- [name: string]: VariableDefinitionNode;
978
- };
979
-
980
- constructor(
981
- public readonly schema: GraphQLSchema,
982
- public readonly operation: OperationDefinitionNode,
983
- public readonly fragments: FragmentMap,
984
- public readonly autoFragmentization: boolean,
985
- ) {
986
- this.variableDefinitions = Object.create(null);
987
- visit(operation, {
988
- VariableDefinition: definition => {
989
- this.variableDefinitions[definition.variable.name.value] = definition;
990
- },
991
- });
992
- }
993
-
994
- getFieldDef(parentType: GraphQLCompositeType, fieldNode: FieldNode) {
995
- const fieldName = fieldNode.name.value;
996
-
997
- const fieldDef = getFieldDef(this.schema, parentType, fieldName);
998
-
999
- if (!fieldDef) {
1000
- throw new GraphQLError(
1001
- `Cannot query field "${fieldNode.name.value}" on type "${String(
1002
- parentType,
1003
- )}"`,
1004
- fieldNode,
1005
- );
1006
- }
1007
-
1008
- return fieldDef;
1009
- }
1010
-
1011
- getPossibleTypes(
1012
- type: GraphQLAbstractType | GraphQLObjectType,
1013
- ): ReadonlyArray<GraphQLObjectType> {
1014
- return isAbstractType(type) ? this.schema.getPossibleTypes(type) : [type];
1015
- }
1016
-
1017
- getVariableUsages(
1018
- selectionSet: SelectionSetNode,
1019
- fragments: Set<FragmentDefinitionNode>,
1020
- ) {
1021
- const usages: {
1022
- [name: string]: VariableDefinitionNode;
1023
- } = Object.create(null);
1024
-
1025
- // Construct a document of the selection set and fragment definitions so we
1026
- // can visit them, adding all variable usages to the `usages` object.
1027
- const document: DocumentNode = {
1028
- kind: Kind.DOCUMENT,
1029
- definitions: [
1030
- { kind: Kind.OPERATION_DEFINITION, selectionSet, operation: 'query' },
1031
- ...Array.from(fragments),
1032
- ],
1033
- };
1034
-
1035
- visit(document, {
1036
- Variable: (node) => {
1037
- usages[node.name.value] = this.variableDefinitions[node.name.value];
1038
- },
1039
- });
1040
-
1041
- return usages;
1042
- }
1043
-
1044
- newScope<TParent extends GraphQLCompositeType>(
1045
- parentType: TParent,
1046
- enclosingScope?: Scope<GraphQLCompositeType>,
1047
- ): Scope<TParent> {
1048
- return {
1049
- parentType,
1050
- possibleTypes: enclosingScope
1051
- ? this.getPossibleTypes(parentType).filter(type =>
1052
- enclosingScope.possibleTypes.includes(type),
1053
- )
1054
- : this.getPossibleTypes(parentType),
1055
- enclosingScope,
1056
- };
1057
- }
1058
-
1059
- getBaseService(parentType: GraphQLObjectType): string | null {
1060
- return (getFederationMetadata(parentType)?.serviceName) || null;
1061
- }
1062
-
1063
- getOwningService(
1064
- parentType: GraphQLObjectType,
1065
- fieldDef: GraphQLField<any, any>,
1066
- ): string | null {
1067
- const fieldFederationMetadata = getFederationMetadata(fieldDef);
1068
- if (
1069
- fieldFederationMetadata?.serviceName &&
1070
- !fieldFederationMetadata?.belongsToValueType
1071
- ) {
1072
- return fieldFederationMetadata.serviceName;
1073
- } else {
1074
- return this.getBaseService(parentType);
1075
- }
1076
- }
1077
-
1078
- getKeyFields({
1079
- parentType,
1080
- serviceName,
1081
- fetchAll = false,
1082
- }: {
1083
- parentType: GraphQLCompositeType;
1084
- serviceName: string;
1085
- fetchAll?: boolean;
1086
- }): FieldSet {
1087
- const keyFields: FieldSet = [];
1088
-
1089
- keyFields.push({
1090
- scope: {
1091
- parentType,
1092
- possibleTypes: this.getPossibleTypes(parentType),
1093
- },
1094
- fieldNode: typenameField,
1095
- fieldDef: TypeNameMetaFieldDef,
1096
- });
1097
-
1098
- for (const possibleType of this.getPossibleTypes(parentType)) {
1099
- const keys = getFederationMetadata(possibleType)?.keys?.[serviceName];
1100
-
1101
- if (!(keys && keys.length > 0)) continue;
1102
-
1103
- if (fetchAll) {
1104
- keyFields.push(
1105
- ...keys.flatMap(key =>
1106
- collectFields(this, this.newScope(possibleType), {
1107
- kind: Kind.SELECTION_SET,
1108
- selections: key,
1109
- }),
1110
- ),
1111
- );
1112
- } else {
1113
- keyFields.push(
1114
- ...collectFields(this, this.newScope(possibleType), {
1115
- kind: Kind.SELECTION_SET,
1116
- selections: keys[0],
1117
- }),
1118
- );
1119
- }
1120
- }
1121
-
1122
- return keyFields;
1123
- }
1124
-
1125
- getRequiredFields(
1126
- parentType: GraphQLCompositeType,
1127
- fieldDef: GraphQLField<any, any>,
1128
- serviceName: string,
1129
- ): FieldSet {
1130
- const requiredFields: FieldSet = [];
1131
-
1132
- requiredFields.push(...this.getKeyFields({ parentType, serviceName }));
1133
-
1134
- const fieldFederationMetadata = getFederationMetadata(fieldDef);
1135
- if (fieldFederationMetadata?.requires) {
1136
- requiredFields.push(
1137
- ...collectFields(this, this.newScope(parentType), {
1138
- kind: Kind.SELECTION_SET,
1139
- selections: fieldFederationMetadata.requires,
1140
- }),
1141
- );
1142
- }
1143
-
1144
- return requiredFields;
1145
- }
1146
-
1147
- getProvidedFields(
1148
- fieldDef: GraphQLField<any, any>,
1149
- serviceName: string,
1150
- ): FieldSet {
1151
- const returnType = getNamedType(fieldDef.type);
1152
- if (!isCompositeType(returnType)) return [];
1153
-
1154
- const providedFields: FieldSet = [];
1155
-
1156
- providedFields.push(
1157
- ...this.getKeyFields({
1158
- parentType: returnType,
1159
- serviceName,
1160
- fetchAll: true,
1161
- }),
1162
- );
1163
-
1164
- const fieldFederationMetadata = getFederationMetadata(fieldDef);
1165
- if (fieldFederationMetadata?.provides) {
1166
- providedFields.push(
1167
- ...collectFields(this, this.newScope(returnType), {
1168
- kind: Kind.SELECTION_SET,
1169
- selections: fieldFederationMetadata.provides,
1170
- }),
1171
- );
1172
- }
1173
-
1174
- return providedFields;
1175
- }
1176
- }
1177
-
1178
- function addPath(path: ResponsePath, responseName: string, type: GraphQLType) {
1179
- path = [...path, responseName];
1180
-
1181
- while (!isNamedType(type)) {
1182
- if (isListType(type)) {
1183
- path.push('@');
1184
- }
1185
-
1186
- type = type.ofType;
1187
- }
1188
-
1189
- return path;
1190
- }