@apollo/federation-internals 2.0.0-alpha.4 → 2.0.0-preview.0

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 (107) hide show
  1. package/CHANGELOG.md +11 -1
  2. package/dist/buildSchema.d.ts +7 -3
  3. package/dist/buildSchema.d.ts.map +1 -1
  4. package/dist/buildSchema.js +41 -22
  5. package/dist/buildSchema.js.map +1 -1
  6. package/dist/coreSpec.d.ts +26 -4
  7. package/dist/coreSpec.d.ts.map +1 -1
  8. package/dist/coreSpec.js +86 -25
  9. package/dist/coreSpec.js.map +1 -1
  10. package/dist/definitions.d.ts +50 -43
  11. package/dist/definitions.d.ts.map +1 -1
  12. package/dist/definitions.js +201 -217
  13. package/dist/definitions.js.map +1 -1
  14. package/dist/directiveAndTypeSpecification.d.ts +38 -0
  15. package/dist/directiveAndTypeSpecification.d.ts.map +1 -0
  16. package/dist/directiveAndTypeSpecification.js +196 -0
  17. package/dist/directiveAndTypeSpecification.js.map +1 -0
  18. package/dist/error.d.ts +10 -1
  19. package/dist/error.d.ts.map +1 -1
  20. package/dist/error.js +22 -2
  21. package/dist/error.js.map +1 -1
  22. package/dist/extractSubgraphsFromSupergraph.d.ts.map +1 -1
  23. package/dist/extractSubgraphsFromSupergraph.js +29 -94
  24. package/dist/extractSubgraphsFromSupergraph.js.map +1 -1
  25. package/dist/federation.d.ts +88 -46
  26. package/dist/federation.d.ts.map +1 -1
  27. package/dist/federation.js +745 -233
  28. package/dist/federation.js.map +1 -1
  29. package/dist/federationSpec.d.ts +19 -0
  30. package/dist/federationSpec.d.ts.map +1 -0
  31. package/dist/federationSpec.js +91 -0
  32. package/dist/federationSpec.js.map +1 -0
  33. package/dist/index.d.ts +2 -0
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +7 -1
  36. package/dist/index.js.map +1 -1
  37. package/dist/joinSpec.d.ts +1 -0
  38. package/dist/joinSpec.d.ts.map +1 -1
  39. package/dist/joinSpec.js +1 -0
  40. package/dist/joinSpec.js.map +1 -1
  41. package/dist/operations.d.ts +8 -1
  42. package/dist/operations.d.ts.map +1 -1
  43. package/dist/operations.js +11 -4
  44. package/dist/operations.js.map +1 -1
  45. package/dist/print.d.ts +11 -9
  46. package/dist/print.d.ts.map +1 -1
  47. package/dist/print.js +21 -11
  48. package/dist/print.js.map +1 -1
  49. package/dist/schemaUpgrader.d.ts +108 -0
  50. package/dist/schemaUpgrader.d.ts.map +1 -0
  51. package/dist/schemaUpgrader.js +498 -0
  52. package/dist/schemaUpgrader.js.map +1 -0
  53. package/dist/sharing.d.ts +3 -0
  54. package/dist/sharing.d.ts.map +1 -0
  55. package/dist/sharing.js +51 -0
  56. package/dist/sharing.js.map +1 -0
  57. package/dist/supergraphs.d.ts.map +1 -1
  58. package/dist/supergraphs.js +2 -3
  59. package/dist/supergraphs.js.map +1 -1
  60. package/dist/tagSpec.d.ts.map +1 -1
  61. package/dist/tagSpec.js +1 -3
  62. package/dist/tagSpec.js.map +1 -1
  63. package/dist/utils.d.ts +8 -0
  64. package/dist/utils.d.ts.map +1 -1
  65. package/dist/utils.js +49 -1
  66. package/dist/utils.js.map +1 -1
  67. package/dist/validate.d.ts.map +1 -1
  68. package/dist/validate.js +9 -4
  69. package/dist/validate.js.map +1 -1
  70. package/dist/validation/KnownTypeNamesInFederationRule.d.ts.map +1 -1
  71. package/dist/validation/KnownTypeNamesInFederationRule.js +1 -2
  72. package/dist/validation/KnownTypeNamesInFederationRule.js.map +1 -1
  73. package/dist/values.d.ts +1 -0
  74. package/dist/values.d.ts.map +1 -1
  75. package/dist/values.js +3 -2
  76. package/dist/values.js.map +1 -1
  77. package/jest.config.js +5 -1
  78. package/package.json +4 -7
  79. package/src/__tests__/definitions.test.ts +19 -17
  80. package/src/__tests__/extractSubgraphsFromSupergraph.test.ts +103 -0
  81. package/src/__tests__/federation.test.ts +31 -0
  82. package/src/__tests__/operations.test.ts +2 -3
  83. package/src/__tests__/schemaUpgrader.test.ts +168 -0
  84. package/src/__tests__/subgraphValidation.test.ts +33 -19
  85. package/src/__tests__/values.test.ts +2 -4
  86. package/src/buildSchema.ts +55 -36
  87. package/src/coreSpec.ts +112 -31
  88. package/src/definitions.ts +247 -260
  89. package/src/directiveAndTypeSpecification.ts +276 -0
  90. package/src/error.ts +61 -5
  91. package/src/extractSubgraphsFromSupergraph.ts +35 -119
  92. package/src/federation.ts +960 -293
  93. package/src/federationSpec.ts +113 -0
  94. package/src/index.ts +2 -0
  95. package/src/joinSpec.ts +2 -1
  96. package/src/operations.ts +22 -7
  97. package/src/print.ts +51 -38
  98. package/src/schemaUpgrader.ts +657 -0
  99. package/src/sharing.ts +68 -0
  100. package/src/supergraphs.ts +3 -3
  101. package/src/tagSpec.ts +1 -3
  102. package/src/utils.ts +85 -0
  103. package/src/validate.ts +13 -7
  104. package/src/validation/KnownTypeNamesInFederationRule.ts +1 -7
  105. package/src/values.ts +7 -3
  106. package/tsconfig.test.tsbuildinfo +1 -1
  107. package/tsconfig.tsbuildinfo +1 -1
package/src/coreSpec.ts CHANGED
@@ -1,11 +1,15 @@
1
1
  import { ASTNode, DirectiveLocation, GraphQLError, StringValueNode } from "graphql";
2
2
  import { URL } from "url";
3
- import { CoreFeature, Directive, DirectiveDefinition, EnumType, NamedType, NonNullType, ScalarType, Schema, SchemaDefinition } from "./definitions";
3
+ import { CoreFeature, Directive, DirectiveDefinition, EnumType, ListType, NamedType, NonNullType, ScalarType, Schema, SchemaDefinition } from "./definitions";
4
4
  import { sameType } from "./types";
5
5
  import { err } from '@apollo/core-schema';
6
6
  import { assert } from './utils';
7
+ import { ERRORS } from "./error";
7
8
 
8
9
  export const coreIdentity = 'https://specs.apollo.dev/core';
10
+ export const linkIdentity = 'https://specs.apollo.dev/link';
11
+
12
+ export const linkDirectiveDefaultName = 'link';
9
13
 
10
14
  export const ErrCoreCheckFailed = (causes: Error[]) =>
11
15
  err('CheckFailed', {
@@ -18,7 +22,6 @@ function buildError(message: string): Error {
18
22
  return new Error(message);
19
23
  }
20
24
 
21
-
22
25
  export const corePurposes = [
23
26
  'SECURITY' as const,
24
27
  'EXECUTION' as const,
@@ -65,11 +68,14 @@ export abstract class FeatureDefinition {
65
68
  return feature?.nameInSchema;
66
69
  }
67
70
 
68
- protected elementNameInSchema(schema: Schema, elementName: string): string | undefined {
69
- const nameInSchema = this.nameInSchema(schema);
70
- return nameInSchema
71
- ? (elementName === nameInSchema ? nameInSchema : `${nameInSchema}__${elementName}`)
72
- : undefined;
71
+ protected directiveNameInSchema(schema: Schema, directiveName: string): string | undefined {
72
+ const feature = this.featureInSchema(schema);
73
+ return feature ? feature.directiveNameInSchema(directiveName) : undefined;
74
+ }
75
+
76
+ protected typeNameInSchema(schema: Schema, directiveName: string): string | undefined {
77
+ const feature = this.featureInSchema(schema);
78
+ return feature ? feature.typeNameInSchema(directiveName) : undefined;
73
79
  }
74
80
 
75
81
  protected rootDirective<TApplicationArgs extends {[key: string]: any}>(schema: Schema): DirectiveDefinition<TApplicationArgs> | undefined {
@@ -78,12 +84,12 @@ export abstract class FeatureDefinition {
78
84
  }
79
85
 
80
86
  protected directive<TApplicationArgs extends {[key: string]: any}>(schema: Schema, elementName: string): DirectiveDefinition<TApplicationArgs> | undefined {
81
- const name = this.elementNameInSchema(schema, elementName);
87
+ const name = this.directiveNameInSchema(schema, elementName);
82
88
  return name ? schema.directive(name) as DirectiveDefinition<TApplicationArgs> | undefined : undefined;
83
89
  }
84
90
 
85
91
  protected type<T extends NamedType>(schema: Schema, elementName: string): T | undefined {
86
- const name = this.elementNameInSchema(schema, elementName);
92
+ const name = this.typeNameInSchema(schema, elementName);
87
93
  return name ? schema.type(name) as T : undefined;
88
94
  }
89
95
 
@@ -92,15 +98,15 @@ export abstract class FeatureDefinition {
92
98
  }
93
99
 
94
100
  protected addDirective(schema: Schema, name: string): DirectiveDefinition {
95
- return schema.addDirectiveDefinition(this.elementNameInSchema(schema, name)!);
101
+ return schema.addDirectiveDefinition(this.directiveNameInSchema(schema, name)!);
96
102
  }
97
103
 
98
104
  protected addScalarType(schema: Schema, name: string): ScalarType {
99
- return schema.addType(new ScalarType(this.elementNameInSchema(schema, name)!));
105
+ return schema.addType(new ScalarType(this.typeNameInSchema(schema, name)!));
100
106
  }
101
107
 
102
108
  protected addEnumType(schema: Schema, name: string): EnumType {
103
- return schema.addType(new EnumType(this.elementNameInSchema(schema, name)!));
109
+ return schema.addType(new EnumType(this.typeNameInSchema(schema, name)!));
104
110
  }
105
111
 
106
112
  protected featureInSchema(schema: Schema): CoreFeature | undefined {
@@ -117,20 +123,59 @@ export abstract class FeatureDefinition {
117
123
  }
118
124
 
119
125
  export type CoreDirectiveArgs = {
126
+ url: undefined,
120
127
  feature: string,
121
128
  as?: string,
122
129
  for?: string
123
130
  }
124
131
 
125
- export function isCoreSpecDirectiveApplication(directive: Directive<SchemaDefinition, any>): directive is Directive<SchemaDefinition, CoreDirectiveArgs> {
132
+ export type LinkDirectiveArgs = {
133
+ url: string,
134
+ feature: undefined,
135
+ as?: string,
136
+ for?: string,
137
+ import?: (string | CoreImport)[]
138
+ }
139
+
140
+ export type CoreOrLinkDirectiveArgs = CoreDirectiveArgs | LinkDirectiveArgs;
141
+
142
+ export type CoreImport = {
143
+ name: string,
144
+ as?: string,
145
+ };
146
+
147
+ export function extractCoreFeatureImports(directive: Directive<SchemaDefinition, CoreOrLinkDirectiveArgs>): CoreImport[] {
148
+ const args = directive.arguments();
149
+ if (!('import' in args)) {
150
+ return [];
151
+ }
152
+ const importArg = args.import;
153
+ const imports: CoreImport[] = importArg ? importArg.map((a) => typeof a === 'string' ? { name: a } : a) : [];
154
+ for (const i of imports) {
155
+ if (!i.as) {
156
+ continue;
157
+ }
158
+ if (i.name.charAt(0) === '@' && i.as.charAt(0) !== '@') {
159
+ throw ERRORS.INVALID_LINK_DIRECTIVE_USAGE.err({
160
+ message: `Invalid @link import renaming: directive ${i.name} imported name should starts with a '@' character, but got "${i.as}"`,
161
+ nodes: directive.sourceAST
162
+ });
163
+ }
164
+ if (i.name.charAt(0) !== '@' && i.as.charAt(0) === '@') {
165
+ throw ERRORS.INVALID_LINK_DIRECTIVE_USAGE.err({
166
+ message: `Invalid @link import renaming: type ${i.name} imported name should not starts with a '@' character, but got "${i.as}" (or, if @${i.name} is a directive, then it should be refered to with a '@')`,
167
+ nodes: directive.sourceAST
168
+ });
169
+ }
170
+ }
171
+ return imports;
172
+ }
173
+
174
+ export function isCoreSpecDirectiveApplication(directive: Directive<SchemaDefinition, any>): directive is Directive<SchemaDefinition, CoreOrLinkDirectiveArgs> {
126
175
  const definition = directive.definition;
127
176
  if (!definition) {
128
177
  return false;
129
178
  }
130
- const featureArg = definition.argument('feature');
131
- if (!featureArg || !sameType(featureArg.type!, new NonNullType(directive.schema().stringType()))) {
132
- return false;
133
- }
134
179
  const asArg = definition.argument('as');
135
180
  if (asArg && !sameType(asArg.type!, directive.schema().stringType())) {
136
181
  return false;
@@ -138,19 +183,27 @@ export function isCoreSpecDirectiveApplication(directive: Directive<SchemaDefini
138
183
  if (!definition.repeatable || definition.locations.length !== 1 || definition.locations[0] !== DirectiveLocation.SCHEMA) {
139
184
  return false;
140
185
  }
186
+ const urlArg = definition.argument('url') ?? definition.argument('feature');
187
+ if (!urlArg || !sameType(urlArg.type!, new NonNullType(directive.schema().stringType()))) {
188
+ return false;
189
+ }
141
190
 
142
- const args = (directive as Directive<SchemaDefinition, CoreDirectiveArgs>).arguments();
191
+ const args = directive.arguments();
143
192
  try {
144
- const url = FeatureUrl.parse(args.feature);
145
- return url.identity === coreIdentity && directive.name === (args.as ?? 'core');
193
+ const url = FeatureUrl.parse(args[urlArg.name] as string);
194
+ if (url.identity === coreIdentity) {
195
+ return directive.name === (args.as ?? 'core');
196
+ } else {
197
+ return url.identity === linkIdentity && directive.name === (args.as ?? linkDirectiveDefaultName);
198
+ }
146
199
  } catch (err) {
147
200
  return false;
148
201
  }
149
202
  }
150
203
 
151
204
  export class CoreSpecDefinition extends FeatureDefinition {
152
- constructor(version: FeatureVersion) {
153
- super(new FeatureUrl(coreIdentity, 'core', version));
205
+ constructor(version: FeatureVersion, identity: string = linkIdentity, name: string = linkDirectiveDefaultName) {
206
+ super(new FeatureUrl(identity, name, version));
154
207
  }
155
208
 
156
209
  addElementsToSchema(_: Schema): void {
@@ -171,7 +224,7 @@ export class CoreSpecDefinition extends FeatureDefinition {
171
224
  const nameInSchema = as ?? this.url.name;
172
225
  const core = schema.addDirectiveDefinition(nameInSchema).addLocations(DirectiveLocation.SCHEMA);
173
226
  core.repeatable = true;
174
- core.addArgument('feature', new NonNullType(schema.stringType()));
227
+ core.addArgument(this.urlArgName(), new NonNullType(schema.stringType()));
175
228
  core.addArgument('as', schema.stringType());
176
229
  if (this.supportPurposes()) {
177
230
  const purposeEnum = schema.addType(new EnumType(`${nameInSchema}__Purpose`));
@@ -180,10 +233,17 @@ export class CoreSpecDefinition extends FeatureDefinition {
180
233
  }
181
234
  core.addArgument('for', purposeEnum);
182
235
  }
236
+ if (this.supportImport()) {
237
+ if (schema.type(`${nameInSchema}__Import`)) {
238
+ console.trace();
239
+ }
240
+ const importType = schema.addType(new ScalarType(`${nameInSchema}__Import`));
241
+ core.addArgument('import', new ListType(importType));
242
+ }
183
243
 
184
244
  // Note: we don't use `applyFeatureToSchema` because it would complain the schema is not a core schema, which it isn't
185
245
  // until the next line.
186
- const args: CoreDirectiveArgs = { feature: this.toString() }
246
+ const args = { [this.urlArgName()]: this.toString() } as unknown as CoreOrLinkDirectiveArgs;
187
247
  if (as) {
188
248
  args.as = as;
189
249
  }
@@ -194,6 +254,10 @@ export class CoreSpecDefinition extends FeatureDefinition {
194
254
  return this.version.strictlyGreaterThan(new FeatureVersion(0, 1));
195
255
  }
196
256
 
257
+ private supportImport() {
258
+ return this.url.name === linkDirectiveDefaultName;
259
+ }
260
+
197
261
  private extractFeature(schema: Schema): CoreFeature {
198
262
  const features = schema.coreFeatures;
199
263
  if (!features) {
@@ -205,10 +269,10 @@ export class CoreSpecDefinition extends FeatureDefinition {
205
269
  return features.coreItself;
206
270
  }
207
271
 
208
- coreDirective(schema: Schema): DirectiveDefinition<CoreDirectiveArgs> {
272
+ coreDirective(schema: Schema): DirectiveDefinition<CoreOrLinkDirectiveArgs> {
209
273
  const feature = this.extractFeature(schema);
210
274
  const directive = schema.directive(feature.nameInSchema);
211
- return directive as DirectiveDefinition<CoreDirectiveArgs>;
275
+ return directive as DirectiveDefinition<CoreOrLinkDirectiveArgs>;
212
276
  }
213
277
 
214
278
  coreVersion(schema: Schema): FeatureVersion {
@@ -218,16 +282,24 @@ export class CoreSpecDefinition extends FeatureDefinition {
218
282
 
219
283
  applyFeatureToSchema(schema: Schema, feature: FeatureDefinition, as?: string, purpose?: CorePurpose) {
220
284
  const coreDirective = this.coreDirective(schema);
221
- const args: CoreDirectiveArgs = {
222
- feature: feature.toString(),
285
+ const args = {
286
+ [this.urlArgName()]: feature.toString(),
223
287
  as,
224
- };
288
+ } as CoreDirectiveArgs;
225
289
  if (this.supportPurposes() && purpose) {
226
290
  args.for = purpose;
227
291
  }
228
292
  schema.schemaDefinition.applyDirective(coreDirective, args);
229
293
  feature.addElementsToSchema(schema);
230
294
  }
295
+
296
+ extractFeatureUrl(args: CoreOrLinkDirectiveArgs): FeatureUrl {
297
+ return FeatureUrl.parse(args[this.urlArgName()]!);
298
+ }
299
+
300
+ urlArgName(): 'feature' | 'url' {
301
+ return this.url.name === 'core' ? 'feature' : 'url';
302
+ }
231
303
  }
232
304
 
233
305
  export class FeatureDefinitions<T extends FeatureDefinition = FeatureDefinition> {
@@ -463,9 +535,18 @@ export class FeatureUrl {
463
535
  }
464
536
  }
465
537
 
538
+ export function findCoreSpecVersion(featureUrl: FeatureUrl): CoreSpecDefinition | undefined {
539
+ return featureUrl.name === 'core'
540
+ ? CORE_VERSIONS.find(featureUrl.version)
541
+ : (featureUrl.name === linkDirectiveDefaultName ? LINK_VERSIONS.find(featureUrl.version) : undefined)
542
+ }
543
+
466
544
  export const CORE_VERSIONS = new FeatureDefinitions<CoreSpecDefinition>(coreIdentity)
467
- .add(new CoreSpecDefinition(new FeatureVersion(0, 1)))
468
- .add(new CoreSpecDefinition(new FeatureVersion(0, 2)));
545
+ .add(new CoreSpecDefinition(new FeatureVersion(0, 1), coreIdentity, 'core'))
546
+ .add(new CoreSpecDefinition(new FeatureVersion(0, 2), coreIdentity, 'core'));
547
+
548
+ export const LINK_VERSIONS = new FeatureDefinitions<CoreSpecDefinition>(linkIdentity)
549
+ .add(new CoreSpecDefinition(new FeatureVersion(1, 0)));
469
550
 
470
551
  export function removeFeatureElements(schema: Schema, feature: CoreFeature) {
471
552
  // Removing directives first, so that when we remove types, the checks that there is no references don't fail due a directive of a the feature