@apollo/federation-internals 2.9.0 → 2.9.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 (47) hide show
  1. package/dist/argumentCompositionStrategies.d.ts +8 -14
  2. package/dist/argumentCompositionStrategies.d.ts.map +1 -1
  3. package/dist/argumentCompositionStrategies.js +48 -48
  4. package/dist/argumentCompositionStrategies.js.map +1 -1
  5. package/dist/definitions.d.ts +1 -0
  6. package/dist/definitions.d.ts.map +1 -1
  7. package/dist/definitions.js +12 -10
  8. package/dist/definitions.js.map +1 -1
  9. package/dist/error.d.ts +5 -0
  10. package/dist/error.d.ts.map +1 -1
  11. package/dist/error.js +10 -0
  12. package/dist/error.js.map +1 -1
  13. package/dist/extractSubgraphsFromSupergraph.js +68 -63
  14. package/dist/extractSubgraphsFromSupergraph.js.map +1 -1
  15. package/dist/federation.d.ts.map +1 -1
  16. package/dist/federation.js +72 -1
  17. package/dist/federation.js.map +1 -1
  18. package/dist/operations.d.ts +6 -4
  19. package/dist/operations.d.ts.map +1 -1
  20. package/dist/operations.js +28 -18
  21. package/dist/operations.js.map +1 -1
  22. package/dist/specs/coreSpec.d.ts +2 -2
  23. package/dist/specs/coreSpec.d.ts.map +1 -1
  24. package/dist/specs/coreSpec.js +15 -20
  25. package/dist/specs/coreSpec.js.map +1 -1
  26. package/dist/specs/costSpec.d.ts +3 -0
  27. package/dist/specs/costSpec.d.ts.map +1 -1
  28. package/dist/specs/costSpec.js +6 -0
  29. package/dist/specs/costSpec.js.map +1 -1
  30. package/dist/supergraphs.d.ts +8 -1
  31. package/dist/supergraphs.d.ts.map +1 -1
  32. package/dist/supergraphs.js +20 -2
  33. package/dist/supergraphs.js.map +1 -1
  34. package/dist/values.d.ts.map +1 -1
  35. package/dist/values.js +14 -5
  36. package/dist/values.js.map +1 -1
  37. package/package.json +1 -1
  38. package/src/argumentCompositionStrategies.ts +68 -50
  39. package/src/definitions.ts +25 -9
  40. package/src/error.ts +36 -0
  41. package/src/extractSubgraphsFromSupergraph.ts +73 -92
  42. package/src/federation.ts +140 -1
  43. package/src/operations.ts +36 -19
  44. package/src/specs/coreSpec.ts +27 -26
  45. package/src/specs/costSpec.ts +9 -1
  46. package/src/supergraphs.ts +31 -3
  47. package/src/values.ts +13 -5
package/src/operations.ts CHANGED
@@ -69,7 +69,7 @@ function haveSameDirectives<TElement extends OperationElement>(op1: TElement, op
69
69
  }
70
70
 
71
71
  abstract class AbstractOperationElement<T extends AbstractOperationElement<T>> extends DirectiveTargetElement<T> {
72
- private attachements?: Map<string, string>;
72
+ private attachments?: Map<string, string>;
73
73
 
74
74
  constructor(
75
75
  schema: Schema,
@@ -97,21 +97,21 @@ abstract class AbstractOperationElement<T extends AbstractOperationElement<T>> e
97
97
 
98
98
  protected abstract collectVariablesInElement(collector: VariableCollector): void;
99
99
 
100
- addAttachement(key: string, value: string) {
101
- if (!this.attachements) {
102
- this.attachements = new Map();
100
+ addAttachment(key: string, value: string) {
101
+ if (!this.attachments) {
102
+ this.attachments = new Map();
103
103
  }
104
- this.attachements.set(key, value);
104
+ this.attachments.set(key, value);
105
105
  }
106
106
 
107
- getAttachement(key: string): string | undefined {
108
- return this.attachements?.get(key);
107
+ getAttachment(key: string): string | undefined {
108
+ return this.attachments?.get(key);
109
109
  }
110
110
 
111
- protected copyAttachementsTo(elt: AbstractOperationElement<any>) {
112
- if (this.attachements) {
113
- for (const [k, v] of this.attachements.entries()) {
114
- elt.addAttachement(k, v);
111
+ protected copyAttachmentsTo(elt: AbstractOperationElement<any>) {
112
+ if (this.attachments) {
113
+ for (const [k, v] of this.attachments.entries()) {
114
+ elt.addAttachment(k, v);
115
115
  }
116
116
  }
117
117
  }
@@ -170,6 +170,17 @@ export class Field<TArgs extends {[key: string]: any} = {[key: string]: any}> ex
170
170
  baseType(): NamedType {
171
171
  return baseType(this.definition.type!);
172
172
  }
173
+
174
+ copy(): Field<TArgs> {
175
+ const newField = new Field<TArgs>(
176
+ this.definition,
177
+ this.args,
178
+ this.appliedDirectives,
179
+ this.alias,
180
+ );
181
+ this.copyAttachmentsTo(newField);
182
+ return newField;
183
+ }
173
184
 
174
185
  withUpdatedArguments(newArgs: TArgs): Field<TArgs> {
175
186
  const newField = new Field<TArgs>(
@@ -178,7 +189,7 @@ export class Field<TArgs extends {[key: string]: any} = {[key: string]: any}> ex
178
189
  this.appliedDirectives,
179
190
  this.alias,
180
191
  );
181
- this.copyAttachementsTo(newField);
192
+ this.copyAttachmentsTo(newField);
182
193
  return newField;
183
194
  }
184
195
 
@@ -189,7 +200,7 @@ export class Field<TArgs extends {[key: string]: any} = {[key: string]: any}> ex
189
200
  this.appliedDirectives,
190
201
  this.alias,
191
202
  );
192
- this.copyAttachementsTo(newField);
203
+ this.copyAttachmentsTo(newField);
193
204
  return newField;
194
205
  }
195
206
 
@@ -200,7 +211,7 @@ export class Field<TArgs extends {[key: string]: any} = {[key: string]: any}> ex
200
211
  this.appliedDirectives,
201
212
  newAlias,
202
213
  );
203
- this.copyAttachementsTo(newField);
214
+ this.copyAttachmentsTo(newField);
204
215
  return newField;
205
216
  }
206
217
 
@@ -211,7 +222,7 @@ export class Field<TArgs extends {[key: string]: any} = {[key: string]: any}> ex
211
222
  newDirectives,
212
223
  this.alias,
213
224
  );
214
- this.copyAttachementsTo(newField);
225
+ this.copyAttachmentsTo(newField);
215
226
  return newField;
216
227
  }
217
228
 
@@ -505,13 +516,13 @@ export class FragmentElement extends AbstractOperationElement<FragmentElement> {
505
516
  // schema (typically, the supergraph) than `this.sourceType` (typically, a subgraph), then the new condition uses the
506
517
  // definition of the proper schema (the supergraph in such cases, instead of the subgraph).
507
518
  const newFragment = new FragmentElement(newSourceType, newCondition?.name, this.appliedDirectives);
508
- this.copyAttachementsTo(newFragment);
519
+ this.copyAttachmentsTo(newFragment);
509
520
  return newFragment;
510
521
  }
511
522
 
512
523
  withUpdatedDirectives(newDirectives: Directive<OperationElement>[]): FragmentElement {
513
524
  const newFragment = new FragmentElement(this.sourceType, this.typeCondition, newDirectives);
514
- this.copyAttachementsTo(newFragment);
525
+ this.copyAttachmentsTo(newFragment);
515
526
  return newFragment;
516
527
  }
517
528
 
@@ -590,7 +601,7 @@ export class FragmentElement extends AbstractOperationElement<FragmentElement> {
590
601
  }
591
602
 
592
603
  const updated = new FragmentElement(this.sourceType, this.typeCondition, updatedDirectives);
593
- this.copyAttachementsTo(updated);
604
+ this.copyAttachmentsTo(updated);
594
605
  return updated;
595
606
  }
596
607
 
@@ -655,7 +666,7 @@ export class FragmentElement extends AbstractOperationElement<FragmentElement> {
655
666
  .concat(new Directive<FragmentElement>(deferDirective.name, newDeferArgs));
656
667
 
657
668
  const updated = new FragmentElement(this.sourceType, this.typeCondition, updatedDirectives);
658
- this.copyAttachementsTo(updated);
669
+ this.copyAttachmentsTo(updated);
659
670
  return updated;
660
671
  }
661
672
 
@@ -3042,6 +3053,12 @@ export class FieldSelection extends AbstractSelection<Field<any>, undefined, Fie
3042
3053
  return this.element.definition.name === typenameFieldName;
3043
3054
  }
3044
3055
 
3056
+ withAttachment(key: string, value: string): FieldSelection {
3057
+ const updatedField = this.element.copy();
3058
+ updatedField.addAttachment(key, value);
3059
+ return this.withUpdatedElement(updatedField);
3060
+ }
3061
+
3045
3062
  withUpdatedComponents(field: Field<any>, selectionSet: SelectionSet | undefined): FieldSelection {
3046
3063
  if (this.element === field && this.selectionSet === selectionSet) {
3047
3064
  return this;
@@ -195,7 +195,8 @@ export type CoreDirectiveArgs = {
195
195
  url: undefined,
196
196
  feature: string,
197
197
  as?: string,
198
- for?: string
198
+ for?: string,
199
+ import: undefined,
199
200
  }
200
201
 
201
202
  export type LinkDirectiveArgs = {
@@ -203,7 +204,7 @@ export type LinkDirectiveArgs = {
203
204
  feature: undefined,
204
205
  as?: string,
205
206
  for?: string,
206
- import?: (string | CoreImport)[]
207
+ import?: (string | CoreImport)[],
207
208
  }
208
209
 
209
210
  export type CoreOrLinkDirectiveArgs = CoreDirectiveArgs | LinkDirectiveArgs;
@@ -539,36 +540,36 @@ export class CoreSpecDefinition extends FeatureDefinition {
539
540
  return feature.url.version;
540
541
  }
541
542
 
542
- applyFeatureToSchema(schema: Schema, feature: FeatureDefinition, as?: string, purpose?: CorePurpose): GraphQLError[] {
543
+ applyFeatureToSchema(
544
+ schema: Schema,
545
+ feature: FeatureDefinition,
546
+ as?: string,
547
+ purpose?: CorePurpose,
548
+ imports?: CoreImport[],
549
+ ): GraphQLError[] {
543
550
  const coreDirective = this.coreDirective(schema);
544
551
  const args = {
545
552
  [this.urlArgName()]: feature.toString(),
546
553
  as,
547
- } as CoreDirectiveArgs;
548
- if (this.supportPurposes() && purpose) {
549
- args.for = purpose;
550
- }
551
- schema.schemaDefinition.applyDirective(coreDirective, args);
552
- return feature.addElementsToSchema(schema);
553
- }
554
-
555
- applyFeatureAsLink(schema: Schema, feature: FeatureDefinition, purpose?: CorePurpose, imports?: CoreImport[]): GraphQLError[] {
556
- const existing = schema.schemaDefinition.appliedDirectivesOf(linkDirectiveDefaultName).find((link) => link.arguments().url === feature.toString());
557
- if (existing) {
558
- existing.remove();
554
+ } as CoreOrLinkDirectiveArgs;
555
+ if (purpose) {
556
+ if (this.supportPurposes()) {
557
+ args.for = purpose;
558
+ } else {
559
+ return [new GraphQLError(
560
+ `Cannot apply feature ${feature} with purpose since the schema's @core/@link version does not support it.`
561
+ )];
562
+ }
559
563
  }
560
-
561
- const coreDirective = this.coreDirective(schema);
562
- const args: LinkDirectiveArgs = {
563
- url: feature.toString(),
564
- import: (existing?.arguments().import ?? []).concat(imports?.map((i) => i.as ? { name: `@${i.name}`, as: `@${i.as}` } : `@${i.name}`)),
565
- feature: undefined,
566
- };
567
-
568
- if (this.supportPurposes() && purpose) {
569
- args.for = purpose;
564
+ if (imports && imports.length > 0) {
565
+ if (this.supportImport()) {
566
+ args.import = imports.map(i => i.as ? i : i.name);
567
+ } else {
568
+ return [new GraphQLError(
569
+ `Cannot apply feature ${feature} with imports since the schema's @core/@link version does not support it.`
570
+ )];
571
+ }
570
572
  }
571
-
572
573
  schema.schemaDefinition.applyDirective(coreDirective, args);
573
574
  return feature.addElementsToSchema(schema);
574
575
  }
@@ -1,7 +1,7 @@
1
1
  import { DirectiveLocation } from 'graphql';
2
2
  import { createDirectiveSpecification } from '../directiveAndTypeSpecification';
3
3
  import { FeatureDefinition, FeatureDefinitions, FeatureUrl, FeatureVersion } from './coreSpec';
4
- import { ListType, NonNullType } from '../definitions';
4
+ import { DirectiveDefinition, ListType, NonNullType, Schema } from '../definitions';
5
5
  import { registerKnownFeature } from '../knownCoreFeatures';
6
6
  import { ARGUMENT_COMPOSITION_STRATEGIES } from '../argumentCompositionStrategies';
7
7
 
@@ -41,6 +41,14 @@ export class CostSpecDefinition extends FeatureDefinition {
41
41
  supergraphSpecification: (fedVersion) => COST_VERSIONS.getMinimumRequiredVersion(fedVersion)
42
42
  }));
43
43
  }
44
+
45
+ costDirective(schema: Schema): DirectiveDefinition<CostDirectiveArguments> | undefined {
46
+ return this.directive(schema, 'cost');
47
+ }
48
+
49
+ listSizeDirective(schema: Schema): DirectiveDefinition<ListSizeDirectiveArguments> | undefined {
50
+ return this.directive(schema, 'listSize');
51
+ }
44
52
  }
45
53
 
46
54
  export const COST_VERSIONS = new FeatureDefinitions<CostSpecDefinition>(costIdentity)
@@ -1,7 +1,9 @@
1
1
  import { DocumentNode, GraphQLError } from "graphql";
2
- import { ErrCoreCheckFailed, FeatureUrl, FeatureVersion } from "./specs/coreSpec";
3
2
  import { CoreFeatures, Schema, sourceASTs } from "./definitions";
3
+ import { ErrCoreCheckFailed, FeatureUrl, FeatureVersion } from "./specs/coreSpec";
4
4
  import { joinIdentity, JoinSpecDefinition, JOIN_VERSIONS } from "./specs/joinSpec";
5
+ import { CONTEXT_VERSIONS, ContextSpecDefinition } from "./specs/contextSpec";
6
+ import { COST_VERSIONS, costIdentity, CostSpecDefinition } from "./specs/costSpec";
5
7
  import { buildSchema, buildSchemaFromAST } from "./buildSchema";
6
8
  import { extractSubgraphsNamesAndUrlsFromSupergraph, extractSubgraphsFromSupergraph } from "./extractSubgraphsFromSupergraph";
7
9
  import { ERRORS } from "./error";
@@ -81,11 +83,17 @@ function checkFeatureSupport(coreFeatures: CoreFeatures, supportedFeatures: Set<
81
83
  }
82
84
  }
83
85
 
84
- export function validateSupergraph(supergraph: Schema): [CoreFeatures, JoinSpecDefinition] {
86
+ export function validateSupergraph(supergraph: Schema): [
87
+ CoreFeatures,
88
+ JoinSpecDefinition,
89
+ ContextSpecDefinition | undefined,
90
+ CostSpecDefinition | undefined,
91
+ ] {
85
92
  const coreFeatures = supergraph.coreFeatures;
86
93
  if (!coreFeatures) {
87
94
  throw ERRORS.INVALID_FEDERATION_SUPERGRAPH.err("Invalid supergraph: must be a core schema");
88
95
  }
96
+
89
97
  const joinFeature = coreFeatures.getByIdentity(joinIdentity);
90
98
  if (!joinFeature) {
91
99
  throw ERRORS.INVALID_FEDERATION_SUPERGRAPH.err("Invalid supergraph: must use the join spec");
@@ -95,7 +103,27 @@ export function validateSupergraph(supergraph: Schema): [CoreFeatures, JoinSpecD
95
103
  throw ERRORS.INVALID_FEDERATION_SUPERGRAPH.err(
96
104
  `Invalid supergraph: uses unsupported join spec version ${joinFeature.url.version} (supported versions: ${JOIN_VERSIONS.versions().join(', ')})`);
97
105
  }
98
- return [coreFeatures, joinSpec];
106
+
107
+ const contextFeature = coreFeatures.getByIdentity(ContextSpecDefinition.identity);
108
+ let contextSpec = undefined;
109
+ if (contextFeature) {
110
+ contextSpec = CONTEXT_VERSIONS.find(contextFeature.url.version);
111
+ if (!contextSpec) {
112
+ throw ERRORS.INVALID_FEDERATION_SUPERGRAPH.err(
113
+ `Invalid supergraph: uses unsupported context spec version ${contextFeature.url.version} (supported versions: ${CONTEXT_VERSIONS.versions().join(', ')})`);
114
+ }
115
+ }
116
+
117
+ const costFeature = coreFeatures.getByIdentity(costIdentity);
118
+ let costSpec = undefined;
119
+ if (costFeature) {
120
+ costSpec = COST_VERSIONS.find(costFeature.url.version);
121
+ if (!costSpec) {
122
+ throw ERRORS.INVALID_FEDERATION_SUPERGRAPH.err(
123
+ `Invalid supergraph: uses unsupported cost spec version ${costFeature.url.version} (supported versions: ${COST_VERSIONS.versions().join(', ')})`);
124
+ }
125
+ }
126
+ return [coreFeatures, joinSpec, contextSpec, costSpec];
99
127
  }
100
128
 
101
129
  export function isFed1Supergraph(supergraph: Schema): boolean {
package/src/values.ts CHANGED
@@ -135,8 +135,10 @@ export function valueEquals(a: any, b: any): boolean {
135
135
  if (Array.isArray(a)) {
136
136
  return Array.isArray(b) && arrayValueEquals(a, b) ;
137
137
  }
138
- if (typeof a === 'object') {
139
- return typeof b === 'object' && objectEquals(a, b);
138
+ // Note that typeof null === 'object', so we have to manually rule that out
139
+ // here.
140
+ if (a !== null && typeof a === 'object') {
141
+ return b !== null && typeof b === 'object' && objectEquals(a, b);
140
142
  }
141
143
  return a === b;
142
144
  }
@@ -224,8 +226,10 @@ function applyDefaultValues(value: any, type: InputType): any {
224
226
  if (fieldValue === undefined) {
225
227
  if (field.defaultValue !== undefined) {
226
228
  updated[field.name] = applyDefaultValues(field.defaultValue, field.type);
227
- } else if (isNonNullType(field.type)) {
228
- throw ERRORS.INVALID_GRAPHQL.err(`Field "${field.name}" of required type ${type} was not provided.`);
229
+ } else if (!isNonNullType(field.type)) {
230
+ updated[field.name] = null;
231
+ } else {
232
+ throw ERRORS.INVALID_GRAPHQL.err(`Required field "${field.name}" of type ${type} was not provided.`);
229
233
  }
230
234
  } else {
231
235
  updated[field.name] = applyDefaultValues(fieldValue, field.type);
@@ -249,8 +253,12 @@ export function withDefaultValues(value: any, argument: ArgumentDefinition<any>)
249
253
  throw buildError(`Cannot compute default value for argument ${argument} as the type is undefined`);
250
254
  }
251
255
  if (value === undefined) {
252
- if (argument.defaultValue) {
256
+ if (argument.defaultValue !== undefined) {
253
257
  return applyDefaultValues(argument.defaultValue, argument.type);
258
+ } else if (!isNonNullType(argument.type)) {
259
+ return null;
260
+ } else {
261
+ throw ERRORS.INVALID_GRAPHQL.err(`Required argument "${argument.coordinate}" was not provided.`);
254
262
  }
255
263
  }
256
264
  return applyDefaultValues(value, argument.type);