@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.
- package/CHANGELOG.md +11 -1
- package/dist/buildSchema.d.ts +7 -3
- package/dist/buildSchema.d.ts.map +1 -1
- package/dist/buildSchema.js +41 -22
- package/dist/buildSchema.js.map +1 -1
- package/dist/coreSpec.d.ts +26 -4
- package/dist/coreSpec.d.ts.map +1 -1
- package/dist/coreSpec.js +86 -25
- package/dist/coreSpec.js.map +1 -1
- package/dist/definitions.d.ts +50 -43
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js +201 -217
- package/dist/definitions.js.map +1 -1
- package/dist/directiveAndTypeSpecification.d.ts +38 -0
- package/dist/directiveAndTypeSpecification.d.ts.map +1 -0
- package/dist/directiveAndTypeSpecification.js +196 -0
- package/dist/directiveAndTypeSpecification.js.map +1 -0
- package/dist/error.d.ts +10 -1
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +22 -2
- package/dist/error.js.map +1 -1
- package/dist/extractSubgraphsFromSupergraph.d.ts.map +1 -1
- package/dist/extractSubgraphsFromSupergraph.js +29 -94
- package/dist/extractSubgraphsFromSupergraph.js.map +1 -1
- package/dist/federation.d.ts +88 -46
- package/dist/federation.d.ts.map +1 -1
- package/dist/federation.js +745 -233
- package/dist/federation.js.map +1 -1
- package/dist/federationSpec.d.ts +19 -0
- package/dist/federationSpec.d.ts.map +1 -0
- package/dist/federationSpec.js +91 -0
- package/dist/federationSpec.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/joinSpec.d.ts +1 -0
- package/dist/joinSpec.d.ts.map +1 -1
- package/dist/joinSpec.js +1 -0
- package/dist/joinSpec.js.map +1 -1
- package/dist/operations.d.ts +8 -1
- package/dist/operations.d.ts.map +1 -1
- package/dist/operations.js +11 -4
- package/dist/operations.js.map +1 -1
- package/dist/print.d.ts +11 -9
- package/dist/print.d.ts.map +1 -1
- package/dist/print.js +21 -11
- package/dist/print.js.map +1 -1
- package/dist/schemaUpgrader.d.ts +108 -0
- package/dist/schemaUpgrader.d.ts.map +1 -0
- package/dist/schemaUpgrader.js +498 -0
- package/dist/schemaUpgrader.js.map +1 -0
- package/dist/sharing.d.ts +3 -0
- package/dist/sharing.d.ts.map +1 -0
- package/dist/sharing.js +51 -0
- package/dist/sharing.js.map +1 -0
- package/dist/supergraphs.d.ts.map +1 -1
- package/dist/supergraphs.js +2 -3
- package/dist/supergraphs.js.map +1 -1
- package/dist/tagSpec.d.ts.map +1 -1
- package/dist/tagSpec.js +1 -3
- package/dist/tagSpec.js.map +1 -1
- package/dist/utils.d.ts +8 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +49 -1
- package/dist/utils.js.map +1 -1
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +9 -4
- package/dist/validate.js.map +1 -1
- package/dist/validation/KnownTypeNamesInFederationRule.d.ts.map +1 -1
- package/dist/validation/KnownTypeNamesInFederationRule.js +1 -2
- package/dist/validation/KnownTypeNamesInFederationRule.js.map +1 -1
- package/dist/values.d.ts +1 -0
- package/dist/values.d.ts.map +1 -1
- package/dist/values.js +3 -2
- package/dist/values.js.map +1 -1
- package/jest.config.js +5 -1
- package/package.json +4 -7
- package/src/__tests__/definitions.test.ts +19 -17
- package/src/__tests__/extractSubgraphsFromSupergraph.test.ts +103 -0
- package/src/__tests__/federation.test.ts +31 -0
- package/src/__tests__/operations.test.ts +2 -3
- package/src/__tests__/schemaUpgrader.test.ts +168 -0
- package/src/__tests__/subgraphValidation.test.ts +33 -19
- package/src/__tests__/values.test.ts +2 -4
- package/src/buildSchema.ts +55 -36
- package/src/coreSpec.ts +112 -31
- package/src/definitions.ts +247 -260
- package/src/directiveAndTypeSpecification.ts +276 -0
- package/src/error.ts +61 -5
- package/src/extractSubgraphsFromSupergraph.ts +35 -119
- package/src/federation.ts +960 -293
- package/src/federationSpec.ts +113 -0
- package/src/index.ts +2 -0
- package/src/joinSpec.ts +2 -1
- package/src/operations.ts +22 -7
- package/src/print.ts +51 -38
- package/src/schemaUpgrader.ts +657 -0
- package/src/sharing.ts +68 -0
- package/src/supergraphs.ts +3 -3
- package/src/tagSpec.ts +1 -3
- package/src/utils.ts +85 -0
- package/src/validate.ts +13 -7
- package/src/validation/KnownTypeNamesInFederationRule.ts +1 -7
- package/src/values.ts +7 -3
- package/tsconfig.test.tsbuildinfo +1 -1
- 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
|
|
69
|
-
const
|
|
70
|
-
return
|
|
71
|
-
|
|
72
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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 =
|
|
191
|
+
const args = directive.arguments();
|
|
143
192
|
try {
|
|
144
|
-
const url = FeatureUrl.parse(args.
|
|
145
|
-
|
|
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(
|
|
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(
|
|
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
|
|
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<
|
|
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<
|
|
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
|
|
222
|
-
|
|
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
|