@apollo/federation-internals 2.0.0-preview.5 → 2.0.0-preview.9

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 (100) hide show
  1. package/CHANGELOG.md +15 -3
  2. package/dist/buildSchema.d.ts.map +1 -1
  3. package/dist/buildSchema.js +51 -41
  4. package/dist/buildSchema.js.map +1 -1
  5. package/dist/coreSpec.d.ts +15 -7
  6. package/dist/coreSpec.d.ts.map +1 -1
  7. package/dist/coreSpec.js +171 -42
  8. package/dist/coreSpec.js.map +1 -1
  9. package/dist/definitions.d.ts +10 -5
  10. package/dist/definitions.d.ts.map +1 -1
  11. package/dist/definitions.js +98 -19
  12. package/dist/definitions.js.map +1 -1
  13. package/dist/directiveAndTypeSpecification.d.ts +11 -1
  14. package/dist/directiveAndTypeSpecification.d.ts.map +1 -1
  15. package/dist/directiveAndTypeSpecification.js +67 -19
  16. package/dist/directiveAndTypeSpecification.js.map +1 -1
  17. package/dist/error.d.ts +7 -0
  18. package/dist/error.d.ts.map +1 -1
  19. package/dist/error.js +33 -1
  20. package/dist/error.js.map +1 -1
  21. package/dist/extractSubgraphsFromSupergraph.d.ts.map +1 -1
  22. package/dist/extractSubgraphsFromSupergraph.js +6 -0
  23. package/dist/extractSubgraphsFromSupergraph.js.map +1 -1
  24. package/dist/federation.d.ts +18 -4
  25. package/dist/federation.d.ts.map +1 -1
  26. package/dist/federation.js +113 -63
  27. package/dist/federation.js.map +1 -1
  28. package/dist/federationSpec.d.ts +6 -2
  29. package/dist/federationSpec.d.ts.map +1 -1
  30. package/dist/federationSpec.js +47 -22
  31. package/dist/federationSpec.js.map +1 -1
  32. package/dist/inaccessibleSpec.d.ts +5 -1
  33. package/dist/inaccessibleSpec.d.ts.map +1 -1
  34. package/dist/inaccessibleSpec.js +31 -3
  35. package/dist/inaccessibleSpec.js.map +1 -1
  36. package/dist/index.d.ts +2 -0
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +2 -0
  39. package/dist/index.js.map +1 -1
  40. package/dist/introspection.d.ts.map +1 -1
  41. package/dist/introspection.js +8 -3
  42. package/dist/introspection.js.map +1 -1
  43. package/dist/joinSpec.d.ts +5 -1
  44. package/dist/joinSpec.d.ts.map +1 -1
  45. package/dist/joinSpec.js +21 -0
  46. package/dist/joinSpec.js.map +1 -1
  47. package/dist/knownCoreFeatures.d.ts +4 -0
  48. package/dist/knownCoreFeatures.d.ts.map +1 -0
  49. package/dist/knownCoreFeatures.js +16 -0
  50. package/dist/knownCoreFeatures.js.map +1 -0
  51. package/dist/operations.d.ts +1 -0
  52. package/dist/operations.d.ts.map +1 -1
  53. package/dist/operations.js +16 -1
  54. package/dist/operations.js.map +1 -1
  55. package/dist/{sharing.d.ts → precompute.d.ts} +1 -1
  56. package/dist/precompute.d.ts.map +1 -0
  57. package/dist/{sharing.js → precompute.js} +1 -1
  58. package/dist/precompute.js.map +1 -0
  59. package/dist/schemaUpgrader.d.ts.map +1 -1
  60. package/dist/schemaUpgrader.js +1 -2
  61. package/dist/schemaUpgrader.js.map +1 -1
  62. package/dist/suggestions.d.ts +1 -1
  63. package/dist/suggestions.d.ts.map +1 -1
  64. package/dist/suggestions.js.map +1 -1
  65. package/dist/supergraphs.d.ts.map +1 -1
  66. package/dist/supergraphs.js +1 -0
  67. package/dist/supergraphs.js.map +1 -1
  68. package/dist/tagSpec.d.ts +7 -2
  69. package/dist/tagSpec.d.ts.map +1 -1
  70. package/dist/tagSpec.js +35 -14
  71. package/dist/tagSpec.js.map +1 -1
  72. package/package.json +3 -3
  73. package/src/__tests__/coreSpec.test.ts +100 -0
  74. package/src/__tests__/definitions.test.ts +75 -0
  75. package/src/__tests__/removeInaccessibleElements.test.ts +59 -6
  76. package/src/__tests__/schemaUpgrader.test.ts +3 -2
  77. package/src/__tests__/subgraphValidation.test.ts +402 -4
  78. package/src/buildSchema.ts +98 -51
  79. package/src/coreSpec.ts +208 -50
  80. package/src/definitions.ts +128 -21
  81. package/src/directiveAndTypeSpecification.ts +80 -20
  82. package/src/error.ts +58 -0
  83. package/src/extractSubgraphsFromSupergraph.ts +6 -0
  84. package/src/federation.ts +150 -78
  85. package/src/federationSpec.ts +56 -24
  86. package/src/inaccessibleSpec.ts +39 -11
  87. package/src/index.ts +2 -0
  88. package/src/introspection.ts +8 -3
  89. package/src/joinSpec.ts +33 -3
  90. package/src/knownCoreFeatures.ts +13 -0
  91. package/src/operations.ts +15 -0
  92. package/src/{sharing.ts → precompute.ts} +1 -2
  93. package/src/schemaUpgrader.ts +4 -7
  94. package/src/suggestions.ts +1 -1
  95. package/src/supergraphs.ts +1 -0
  96. package/src/tagSpec.ts +49 -16
  97. package/tsconfig.test.tsbuildinfo +1 -1
  98. package/tsconfig.tsbuildinfo +1 -1
  99. package/dist/sharing.d.ts.map +0 -1
  100. package/dist/sharing.js.map +0 -1
@@ -46,7 +46,9 @@ import {
46
46
  UnionType,
47
47
  InputObjectType,
48
48
  EnumType,
49
- Extension
49
+ Extension,
50
+ ErrGraphQLValidationFailed,
51
+ errorCauses,
50
52
  } from "./definitions";
51
53
 
52
54
  function buildValue(value?: ValueNode): any {
@@ -66,6 +68,7 @@ export function buildSchemaFromAST(
66
68
  documentNode: DocumentNode,
67
69
  options?: BuildSchemaOptions,
68
70
  ): Schema {
71
+ const errors: GraphQLError[] = [];
69
72
  const schema = new Schema(options?.blueprint);
70
73
  // We do a first pass to add all empty types and directives definition. This ensure any reference on one of
71
74
  // those can be resolved in the 2nd pass, regardless of the order of the definitions in the AST.
@@ -77,29 +80,30 @@ export function buildSchemaFromAST(
77
80
  // populated (and at this point, we don't really know the name of the `@core` directive since it can be renamed, so
78
81
  // we just handle all directives).
79
82
  for (const directiveDefinitionNode of directiveDefinitions) {
80
- buildDirectiveDefinitionInner(directiveDefinitionNode, schema.directive(directiveDefinitionNode.name.value)!);
83
+ buildDirectiveDefinitionInner(directiveDefinitionNode, schema.directive(directiveDefinitionNode.name.value)!, errors);
81
84
  }
82
85
  for (const schemaDefinition of schemaDefinitions) {
83
- buildSchemaDefinitionInner(schemaDefinition, schema.schemaDefinition);
86
+ buildSchemaDefinitionInner(schemaDefinition, schema.schemaDefinition, errors);
84
87
  }
85
88
  for (const schemaExtension of schemaExtensions) {
86
- buildSchemaDefinitionInner(schemaExtension, schema.schemaDefinition, schema.schemaDefinition.newExtension());
89
+ buildSchemaDefinitionInner(schemaExtension, schema.schemaDefinition, errors, schema.schemaDefinition.newExtension());
87
90
  }
88
91
 
89
- schema.blueprint.onDirectiveDefinitionAndSchemaParsed(schema);
92
+ errors.push(...schema.blueprint.onDirectiveDefinitionAndSchemaParsed(schema));
90
93
 
91
94
  for (const definitionNode of documentNode.definitions) {
92
95
  switch (definitionNode.kind) {
93
96
  case 'OperationDefinition':
94
97
  case 'FragmentDefinition':
95
- throw new GraphQLError("Invalid executable definition found while building schema", definitionNode);
98
+ errors.push(new GraphQLError("Invalid executable definition found while building schema", definitionNode));
99
+ continue;
96
100
  case 'ScalarTypeDefinition':
97
101
  case 'ObjectTypeDefinition':
98
102
  case 'InterfaceTypeDefinition':
99
103
  case 'UnionTypeDefinition':
100
104
  case 'EnumTypeDefinition':
101
105
  case 'InputObjectTypeDefinition':
102
- buildNamedTypeInner(definitionNode, schema.type(definitionNode.name.value)!, schema.blueprint);
106
+ buildNamedTypeInner(definitionNode, schema.type(definitionNode.name.value)!, schema.blueprint, errors);
103
107
  break;
104
108
  case 'ScalarTypeExtension':
105
109
  case 'ObjectTypeExtension':
@@ -110,11 +114,20 @@ export function buildSchemaFromAST(
110
114
  const toExtend = schema.type(definitionNode.name.value)!;
111
115
  const extension = toExtend.newExtension();
112
116
  extension.sourceAST = definitionNode;
113
- buildNamedTypeInner(definitionNode, toExtend, schema.blueprint, extension);
117
+ buildNamedTypeInner(definitionNode, toExtend, schema.blueprint, errors, extension);
114
118
  break;
115
119
  }
116
120
  }
117
121
 
122
+ // Note: we could try calling `schema.validate()` regardless of errors building the schema and merge the resulting
123
+ // errors, and there is some subset of cases where this be a tad more convenient (as the user would get all the errors
124
+ // at once), but in most cases a bunch of the errors thrown by `schema.validate()` would actually be consequences of
125
+ // the schema not be properly built in the first place and those errors would be confusing to the user. And avoiding
126
+ // confusing users probably trumps a rare minor convenience.
127
+ if (errors.length > 0) {
128
+ throw ErrGraphQLValidationFailed(errors);
129
+ }
130
+
118
131
  if (options?.validate ?? true) {
119
132
  schema.validate();
120
133
  }
@@ -187,21 +200,24 @@ function getReferencedType(node: NamedTypeNode, schema: Schema): NamedType {
187
200
  return type;
188
201
  }
189
202
 
190
- function withNodeAttachedToError(operation: () => void, node: ASTNode) {
203
+ function withNodeAttachedToError(operation: () => void, node: ASTNode, errors: GraphQLError[]) {
191
204
  try {
192
205
  operation();
193
206
  } catch (e) {
194
- if (e instanceof GraphQLError) {
195
- const allNodes: ASTNode | ASTNode[] = e.nodes ? [node, ...e.nodes] : node;
196
- throw new GraphQLError(
197
- e.message,
198
- allNodes,
199
- e.source,
200
- e.positions,
201
- e.path,
202
- e,
203
- e.extensions
204
- );
207
+ const causes = errorCauses(e);
208
+ if (causes) {
209
+ for (const cause of causes) {
210
+ const allNodes: ASTNode | ASTNode[] = cause.nodes ? [node, ...cause.nodes] : node;
211
+ errors.push(new GraphQLError(
212
+ cause.message,
213
+ allNodes,
214
+ cause.source,
215
+ cause.positions,
216
+ cause.path,
217
+ cause,
218
+ cause.extensions
219
+ ));
220
+ }
205
221
  } else {
206
222
  throw e;
207
223
  }
@@ -211,31 +227,39 @@ function withNodeAttachedToError(operation: () => void, node: ASTNode) {
211
227
  function buildSchemaDefinitionInner(
212
228
  schemaNode: SchemaDefinitionNode | SchemaExtensionNode,
213
229
  schemaDefinition: SchemaDefinition,
230
+ errors: GraphQLError[],
214
231
  extension?: Extension<SchemaDefinition>
215
232
  ) {
216
233
  for (const opTypeNode of schemaNode.operationTypes ?? []) {
217
234
  withNodeAttachedToError(
218
235
  () => schemaDefinition.setRoot(opTypeNode.operation, opTypeNode.type.name.value).setOfExtension(extension),
219
- opTypeNode);
236
+ opTypeNode,
237
+ errors,
238
+ );
220
239
  }
221
240
  schemaDefinition.sourceAST = schemaNode;
222
241
  if ('description' in schemaNode) {
223
242
  schemaDefinition.description = schemaNode.description?.value;
224
243
  }
225
- buildAppliedDirectives(schemaNode, schemaDefinition, extension);
244
+ buildAppliedDirectives(schemaNode, schemaDefinition, errors, extension);
226
245
  }
227
246
 
228
247
  function buildAppliedDirectives(
229
248
  elementNode: NodeWithDirectives,
230
249
  element: SchemaElement<any, any>,
250
+ errors: GraphQLError[],
231
251
  extension?: Extension<any>
232
252
  ) {
233
253
  for (const directive of elementNode.directives ?? []) {
234
- withNodeAttachedToError(() => {
235
- const d = element.applyDirective(directive.name.value, buildArgs(directive));
236
- d.setOfExtension(extension);
237
- d.sourceAST = directive;
238
- }, directive);
254
+ withNodeAttachedToError(
255
+ () => {
256
+ const d = element.applyDirective(directive.name.value, buildArgs(directive));
257
+ d.setOfExtension(extension);
258
+ d.sourceAST = directive;
259
+ },
260
+ directive,
261
+ errors,
262
+ );
239
263
  }
240
264
  }
241
265
 
@@ -251,6 +275,7 @@ function buildNamedTypeInner(
251
275
  definitionNode: DefinitionNode & NodeWithDirectives & NodeWithDescription,
252
276
  type: NamedType,
253
277
  blueprint: SchemaBlueprint,
278
+ errors: GraphQLError[],
254
279
  extension?: Extension<any>,
255
280
  ) {
256
281
  switch (definitionNode.kind) {
@@ -265,18 +290,20 @@ function buildNamedTypeInner(
265
290
  }
266
291
  const field = fieldBasedType.addField(fieldNode.name.value);
267
292
  field.setOfExtension(extension);
268
- buildFieldDefinitionInner(fieldNode, field);
293
+ buildFieldDefinitionInner(fieldNode, field, errors);
269
294
  }
270
295
  for (const itfNode of definitionNode.interfaces ?? []) {
271
296
  withNodeAttachedToError(
272
297
  () => {
273
298
  const itfName = itfNode.name.value;
274
299
  if (fieldBasedType.implementsInterface(itfName)) {
275
- throw new GraphQLError(`Type ${type} can only implement ${itfName} once.`);
300
+ throw new GraphQLError(`Type "${type}" can only implement "${itfName}" once.`);
276
301
  }
277
302
  fieldBasedType.addImplementedInterface(itfName).setOfExtension(extension);
278
303
  },
279
- itfNode);
304
+ itfNode,
305
+ errors,
306
+ );
280
307
  }
281
308
  break;
282
309
  case 'UnionTypeDefinition':
@@ -287,11 +314,13 @@ function buildNamedTypeInner(
287
314
  () => {
288
315
  const name = namedType.name.value;
289
316
  if (unionType.hasTypeMember(name)) {
290
- throw new GraphQLError(`Union type ${unionType} can only include type ${name} once.`);
317
+ throw new GraphQLError(`Union type "${unionType}" can only include type "${name}" once.`);
291
318
  }
292
319
  unionType.addType(name).setOfExtension(extension);
293
320
  },
294
- namedType);
321
+ namedType,
322
+ errors,
323
+ );
295
324
  }
296
325
  break;
297
326
  case 'EnumTypeDefinition':
@@ -303,7 +332,7 @@ function buildNamedTypeInner(
303
332
  v.description = enumVal.description.value;
304
333
  }
305
334
  v.setOfExtension(extension);
306
- buildAppliedDirectives(enumVal, v);
335
+ buildAppliedDirectives(enumVal, v, errors);
307
336
  }
308
337
  break;
309
338
  case 'InputObjectTypeDefinition':
@@ -312,41 +341,47 @@ function buildNamedTypeInner(
312
341
  for (const fieldNode of definitionNode.fields ?? []) {
313
342
  const field = inputObjectType.addField(fieldNode.name.value);
314
343
  field.setOfExtension(extension);
315
- buildInputFieldDefinitionInner(fieldNode, field);
344
+ buildInputFieldDefinitionInner(fieldNode, field, errors);
316
345
  }
317
346
  break;
318
347
  }
319
- buildAppliedDirectives(definitionNode, type, extension);
348
+ buildAppliedDirectives(definitionNode, type, errors, extension);
320
349
  if (definitionNode.description) {
321
350
  type.description = definitionNode.description.value;
322
351
  }
323
352
  type.sourceAST = definitionNode;
324
353
  }
325
354
 
326
- function buildFieldDefinitionInner(fieldNode: FieldDefinitionNode, field: FieldDefinition<any>) {
355
+ function buildFieldDefinitionInner(
356
+ fieldNode: FieldDefinitionNode,
357
+ field: FieldDefinition<any>,
358
+ errors: GraphQLError[],
359
+ ) {
327
360
  const type = buildTypeReferenceFromAST(fieldNode.type, field.schema());
328
- field.type = ensureOutputType(type, field.coordinate, fieldNode);
361
+ field.type = validateOutputType(type, field.coordinate, fieldNode, errors);
329
362
  for (const inputValueDef of fieldNode.arguments ?? []) {
330
- buildArgumentDefinitionInner(inputValueDef, field.addArgument(inputValueDef.name.value));
363
+ buildArgumentDefinitionInner(inputValueDef, field.addArgument(inputValueDef.name.value), errors);
331
364
  }
332
- buildAppliedDirectives(fieldNode, field);
365
+ buildAppliedDirectives(fieldNode, field, errors);
333
366
  field.description = fieldNode.description?.value;
334
367
  field.sourceAST = fieldNode;
335
368
  }
336
369
 
337
- function ensureOutputType(type: Type, what: string, node: ASTNode): OutputType {
370
+ function validateOutputType(type: Type, what: string, node: ASTNode, errors: GraphQLError[]): OutputType | undefined {
338
371
  if (isOutputType(type)) {
339
372
  return type;
340
373
  } else {
341
- throw new GraphQLError(`The type of ${what} must be Output Type but got: ${type}, a ${type.kind}.`, node);
374
+ errors.push(new GraphQLError(`The type of "${what}" must be Output Type but got "${type}", a ${type.kind}.`, node));
375
+ return undefined;
342
376
  }
343
377
  }
344
378
 
345
- function ensureInputType(type: Type, what: string, node: ASTNode): InputType {
379
+ function validateInputType(type: Type, what: string, node: ASTNode, errors: GraphQLError[]): InputType | undefined {
346
380
  if (isInputType(type)) {
347
381
  return type;
348
382
  } else {
349
- throw new GraphQLError(`The type of ${what} must be Input Type but got: ${type}, a ${type.kind}.`, node);
383
+ errors.push(new GraphQLError(`The type of "${what}" must be Input Type but got "${type}", a ${type.kind}.`, node));
384
+ return undefined;
350
385
  }
351
386
  }
352
387
 
@@ -369,27 +404,39 @@ function buildTypeReferenceFromAST(typeNode: TypeNode, schema: Schema): Type {
369
404
  }
370
405
  }
371
406
 
372
- function buildArgumentDefinitionInner(inputNode: InputValueDefinitionNode, arg: ArgumentDefinition<any>) {
407
+ function buildArgumentDefinitionInner(
408
+ inputNode: InputValueDefinitionNode,
409
+ arg: ArgumentDefinition<any>,
410
+ errors: GraphQLError[],
411
+ ) {
373
412
  const type = buildTypeReferenceFromAST(inputNode.type, arg.schema());
374
- arg.type = ensureInputType(type, arg.coordinate, inputNode);
413
+ arg.type = validateInputType(type, arg.coordinate, inputNode, errors);
375
414
  arg.defaultValue = buildValue(inputNode.defaultValue);
376
- buildAppliedDirectives(inputNode, arg);
415
+ buildAppliedDirectives(inputNode, arg, errors);
377
416
  arg.description = inputNode.description?.value;
378
417
  arg.sourceAST = inputNode;
379
418
  }
380
419
 
381
- function buildInputFieldDefinitionInner(fieldNode: InputValueDefinitionNode, field: InputFieldDefinition) {
420
+ function buildInputFieldDefinitionInner(
421
+ fieldNode: InputValueDefinitionNode,
422
+ field: InputFieldDefinition,
423
+ errors: GraphQLError[],
424
+ ) {
382
425
  const type = buildTypeReferenceFromAST(fieldNode.type, field.schema());
383
- field.type = ensureInputType(type, field.coordinate, fieldNode);
426
+ field.type = validateInputType(type, field.coordinate, fieldNode, errors);
384
427
  field.defaultValue = buildValue(fieldNode.defaultValue);
385
- buildAppliedDirectives(fieldNode, field);
428
+ buildAppliedDirectives(fieldNode, field, errors);
386
429
  field.description = fieldNode.description?.value;
387
430
  field.sourceAST = fieldNode;
388
431
  }
389
432
 
390
- function buildDirectiveDefinitionInner(directiveNode: DirectiveDefinitionNode, directive: DirectiveDefinition) {
433
+ function buildDirectiveDefinitionInner(
434
+ directiveNode: DirectiveDefinitionNode,
435
+ directive: DirectiveDefinition,
436
+ errors: GraphQLError[],
437
+ ) {
391
438
  for (const inputValueDef of directiveNode.arguments ?? []) {
392
- buildArgumentDefinitionInner(inputValueDef, directive.addArgument(inputValueDef.name.value));
439
+ buildArgumentDefinitionInner(inputValueDef, directive.addArgument(inputValueDef.name.value), errors);
393
440
  }
394
441
  directive.repeatable = directiveNode.repeatable;
395
442
  const locations = directiveNode.locations.map(({ value }) => value as DirectiveLocation);