@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.
- package/dist/argumentCompositionStrategies.d.ts +8 -14
- package/dist/argumentCompositionStrategies.d.ts.map +1 -1
- package/dist/argumentCompositionStrategies.js +48 -48
- package/dist/argumentCompositionStrategies.js.map +1 -1
- package/dist/definitions.d.ts +1 -0
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js +12 -10
- package/dist/definitions.js.map +1 -1
- package/dist/error.d.ts +5 -0
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +10 -0
- package/dist/error.js.map +1 -1
- package/dist/extractSubgraphsFromSupergraph.js +68 -63
- package/dist/extractSubgraphsFromSupergraph.js.map +1 -1
- package/dist/federation.d.ts.map +1 -1
- package/dist/federation.js +72 -1
- package/dist/federation.js.map +1 -1
- package/dist/operations.d.ts +6 -4
- package/dist/operations.d.ts.map +1 -1
- package/dist/operations.js +28 -18
- package/dist/operations.js.map +1 -1
- package/dist/specs/coreSpec.d.ts +2 -2
- package/dist/specs/coreSpec.d.ts.map +1 -1
- package/dist/specs/coreSpec.js +15 -20
- package/dist/specs/coreSpec.js.map +1 -1
- package/dist/specs/costSpec.d.ts +3 -0
- package/dist/specs/costSpec.d.ts.map +1 -1
- package/dist/specs/costSpec.js +6 -0
- package/dist/specs/costSpec.js.map +1 -1
- package/dist/supergraphs.d.ts +8 -1
- package/dist/supergraphs.d.ts.map +1 -1
- package/dist/supergraphs.js +20 -2
- package/dist/supergraphs.js.map +1 -1
- package/dist/values.d.ts.map +1 -1
- package/dist/values.js +14 -5
- package/dist/values.js.map +1 -1
- package/package.json +1 -1
- package/src/argumentCompositionStrategies.ts +68 -50
- package/src/definitions.ts +25 -9
- package/src/error.ts +36 -0
- package/src/extractSubgraphsFromSupergraph.ts +73 -92
- package/src/federation.ts +140 -1
- package/src/operations.ts +36 -19
- package/src/specs/coreSpec.ts +27 -26
- package/src/specs/costSpec.ts +9 -1
- package/src/supergraphs.ts +31 -3
- 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
|
|
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
|
-
|
|
101
|
-
if (!this.
|
|
102
|
-
this.
|
|
100
|
+
addAttachment(key: string, value: string) {
|
|
101
|
+
if (!this.attachments) {
|
|
102
|
+
this.attachments = new Map();
|
|
103
103
|
}
|
|
104
|
-
this.
|
|
104
|
+
this.attachments.set(key, value);
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
|
|
108
|
-
return this.
|
|
107
|
+
getAttachment(key: string): string | undefined {
|
|
108
|
+
return this.attachments?.get(key);
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
protected
|
|
112
|
-
if (this.
|
|
113
|
-
for (const [k, v] of this.
|
|
114
|
-
elt.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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;
|
package/src/specs/coreSpec.ts
CHANGED
|
@@ -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(
|
|
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
|
|
548
|
-
if (
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
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
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
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
|
}
|
package/src/specs/costSpec.ts
CHANGED
|
@@ -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)
|
package/src/supergraphs.ts
CHANGED
|
@@ -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): [
|
|
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
|
-
|
|
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
|
-
|
|
139
|
-
|
|
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
|
-
|
|
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);
|