@apollo/federation-internals 2.13.3 → 2.14.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/dist/definitions.d.ts +24 -2
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js +428 -37
- package/dist/definitions.js.map +1 -1
- package/dist/federation.d.ts +2 -2
- package/dist/federation.js +2 -2
- package/dist/schemaUpgrader.d.ts.map +1 -1
- package/dist/schemaUpgrader.js +19 -7
- package/dist/schemaUpgrader.js.map +1 -1
- package/dist/specs/connectSpec.js +1 -1
- package/dist/specs/connectSpec.js.map +1 -1
- package/dist/specs/coreSpec.d.ts.map +1 -1
- package/dist/specs/coreSpec.js +9 -0
- package/dist/specs/coreSpec.js.map +1 -1
- package/dist/specs/federationSpec.d.ts.map +1 -1
- package/dist/specs/federationSpec.js +2 -1
- package/dist/specs/federationSpec.js.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +4 -3
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/definitions.ts +758 -47
- package/src/federation.ts +2 -2
- package/src/schemaUpgrader.ts +27 -12
- package/src/specs/connectSpec.ts +2 -2
- package/src/specs/coreSpec.ts +17 -0
- package/src/specs/federationSpec.ts +2 -1
- package/src/utils.ts +6 -3
package/src/federation.ts
CHANGED
|
@@ -1964,9 +1964,9 @@ export function setSchemaAsFed2Subgraph(schema: Schema, useLatest: boolean = fal
|
|
|
1964
1964
|
|
|
1965
1965
|
// This is the full @link declaration as added by `asFed2SubgraphDocument`. It's here primarily for uses by tests that print and match
|
|
1966
1966
|
// subgraph schema to avoid having to update 20+ tests every time we use a new directive or the order of import changes ...
|
|
1967
|
-
export const FEDERATION2_LINK_WITH_FULL_IMPORTS = '@link(url: "https://specs.apollo.dev/federation/v2.
|
|
1967
|
+
export const FEDERATION2_LINK_WITH_FULL_IMPORTS = '@link(url: "https://specs.apollo.dev/federation/v2.14", import: ["@key", "@requires", "@provides", "@external", "@tag", "@extends", "@shareable", "@inaccessible", "@override", "@composeDirective", "@interfaceObject", "@authenticated", "@requiresScopes", "@policy", "@context", "@fromContext", "@cost", "@listSize", "@cacheTag"])';
|
|
1968
1968
|
// This is the full @link declaration that is added when upgrading fed v1 subgraphs to v2 version. It should only be used by tests.
|
|
1969
|
-
export const FEDERATION2_LINK_WITH_AUTO_EXPANDED_IMPORTS = '@link(url: "https://specs.apollo.dev/federation/v2.
|
|
1969
|
+
export const FEDERATION2_LINK_WITH_AUTO_EXPANDED_IMPORTS = '@link(url: "https://specs.apollo.dev/federation/v2.14", import: ["@key", "@requires", "@provides", "@external", "@tag", "@extends", "@shareable", "@inaccessible", "@override", "@composeDirective", "@interfaceObject"])';
|
|
1970
1970
|
|
|
1971
1971
|
// This is the federation @link for tests that go through the SchemaUpgrader.
|
|
1972
1972
|
export const FEDERATION2_LINK_WITH_AUTO_EXPANDED_IMPORTS_UPGRADED = '@link(url: "https://specs.apollo.dev/federation/v2.4", import: ["@key", "@requires", "@provides", "@external", "@tag", "@extends", "@shareable", "@inaccessible", "@override", "@composeDirective", "@interfaceObject"])';
|
package/src/schemaUpgrader.ts
CHANGED
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
Subgraph,
|
|
33
33
|
Subgraphs,
|
|
34
34
|
} from "./federation";
|
|
35
|
-
import { assert, firstOf, MultiMap } from "./utils";
|
|
35
|
+
import { assert, firstOf, MultiMap, SetMultiMap } from "./utils";
|
|
36
36
|
import { valueEquals } from "./values";
|
|
37
37
|
import { FEDERATION1_TYPES } from "./specs/federationSpec";
|
|
38
38
|
|
|
@@ -230,7 +230,8 @@ export function upgradeSubgraphsIfNecessary(inputs: Subgraphs): UpgradeResult {
|
|
|
230
230
|
|
|
231
231
|
const subgraphs = new Subgraphs();
|
|
232
232
|
let errors: GraphQLError[] = [];
|
|
233
|
-
const
|
|
233
|
+
const fed2InterfaceObjectTypesToSubgraphs = new SetMultiMap<string, string>();
|
|
234
|
+
const fed1InterfaceKeyTypesToSubgraphs = new SetMultiMap<string, string>();
|
|
234
235
|
|
|
235
236
|
// build a data structure to help us do computation only once
|
|
236
237
|
const objectTypeMap = new Map<string, Map<string, [ObjectType | InterfaceType, FederationMetadata]>>();
|
|
@@ -256,8 +257,9 @@ export function upgradeSubgraphsIfNecessary(inputs: Subgraphs): UpgradeResult {
|
|
|
256
257
|
for (const subgraph of inputs.values()) {
|
|
257
258
|
if (subgraph.isFed2Subgraph()) {
|
|
258
259
|
subgraphs.add(subgraph);
|
|
259
|
-
|
|
260
|
-
|
|
260
|
+
for (const application of subgraph.metadata().interfaceObjectDirective().applications()) {
|
|
261
|
+
const typeName = (application.parent as NamedType).name;
|
|
262
|
+
fed2InterfaceObjectTypesToSubgraphs.add(typeName, subgraph.name);
|
|
261
263
|
}
|
|
262
264
|
} else {
|
|
263
265
|
const res = new SchemaUpgrader(subgraph, inputs.values(), objectTypeMap).upgrade();
|
|
@@ -266,16 +268,19 @@ export function upgradeSubgraphsIfNecessary(inputs: Subgraphs): UpgradeResult {
|
|
|
266
268
|
} else {
|
|
267
269
|
subgraphs.add(res.upgraded);
|
|
268
270
|
changes.set(subgraph.name, res.changes);
|
|
271
|
+
for (const typeName of res.interfaceKeyTypes) {
|
|
272
|
+
fed1InterfaceKeyTypesToSubgraphs.add(typeName, subgraph.name);
|
|
273
|
+
}
|
|
269
274
|
}
|
|
270
275
|
}
|
|
271
276
|
}
|
|
272
|
-
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
277
|
+
for (const [typeName, interfaceObjectSubgraphs] of fed2InterfaceObjectTypesToSubgraphs) {
|
|
278
|
+
const interfaceKeySubgraphs = fed1InterfaceKeyTypesToSubgraphs.get(typeName);
|
|
279
|
+
if (interfaceKeySubgraphs) {
|
|
280
|
+
errors.push(ERRORS.INTERFACE_OBJECT_USAGE_ERROR.err(
|
|
281
|
+
`The @interfaceObject directive is used on type "${typeName}" in ${printSubgraphNames([...interfaceObjectSubgraphs])}, which requires other subgraphs to resolve its type name via an interface @key. However, @key on an interface in a federation 1 subgraph does not mean it can fulfill the __typename-resolution requirement that @interfaceObject depends on. For ${printSubgraphNames([...interfaceKeySubgraphs])}, either upgrade them to federation 2 subgraphs or remove @key from the type.`,
|
|
282
|
+
));
|
|
283
|
+
}
|
|
279
284
|
}
|
|
280
285
|
|
|
281
286
|
return errors.length === 0 ? { subgraphs, changes } : { errors };
|
|
@@ -324,6 +329,7 @@ class SchemaUpgrader {
|
|
|
324
329
|
private readonly subgraph: Subgraph;
|
|
325
330
|
private readonly metadata: FederationMetadata;
|
|
326
331
|
private readonly errors: GraphQLError[] = [];
|
|
332
|
+
private readonly interfaceKeyTypes: Set<string> = new Set();
|
|
327
333
|
|
|
328
334
|
constructor(private readonly originalSubgraph: Subgraph, private readonly allSubgraphs: readonly Subgraph[], private readonly objectTypeMap: Map<string, Map<string, [ObjectType | InterfaceType, FederationMetadata]>>) {
|
|
329
335
|
// Note that as we clone the original schema, the 'sourceAST' values in the elements of the new schema will be those of the original schema
|
|
@@ -413,7 +419,14 @@ class SchemaUpgrader {
|
|
|
413
419
|
}
|
|
414
420
|
}
|
|
415
421
|
|
|
416
|
-
upgrade(): { upgraded: Subgraph, changes: UpgradeChanges, errors?: never } | { errors: GraphQLError[] } {
|
|
422
|
+
upgrade(): { upgraded: Subgraph, changes: UpgradeChanges, interfaceKeyTypes: Set<string>, errors?: never } | { errors: GraphQLError[] } {
|
|
423
|
+
// If there's already errors from constructor, we may not have inserted all
|
|
424
|
+
// directive definitions we need for upgrading (which may cause the methods
|
|
425
|
+
// below to throw). To avoid this, we return early in that case.
|
|
426
|
+
if (this.errors.length > 0) {
|
|
427
|
+
return { errors: this.errors };
|
|
428
|
+
}
|
|
429
|
+
|
|
417
430
|
this.preUpgradeValidations();
|
|
418
431
|
|
|
419
432
|
this.fixFederationDirectivesArguments();
|
|
@@ -452,6 +465,7 @@ class SchemaUpgrader {
|
|
|
452
465
|
return {
|
|
453
466
|
upgraded: this.subgraph,
|
|
454
467
|
changes: this.changes,
|
|
468
|
+
interfaceKeyTypes: this.interfaceKeyTypes,
|
|
455
469
|
};
|
|
456
470
|
} catch (e) {
|
|
457
471
|
const errors = errorCauses(e);
|
|
@@ -679,6 +693,7 @@ class SchemaUpgrader {
|
|
|
679
693
|
for (const type of this.schema.interfaceTypes()) {
|
|
680
694
|
for (const application of type.appliedDirectivesOf(this.metadata.keyDirective())) {
|
|
681
695
|
this.addChange(new KeyOnInterfaceRemoval(type.name));
|
|
696
|
+
this.interfaceKeyTypes.add(type.name);
|
|
682
697
|
application.remove();
|
|
683
698
|
}
|
|
684
699
|
for (const field of type.fields()) {
|
package/src/specs/connectSpec.ts
CHANGED
|
@@ -249,7 +249,7 @@ export class ConnectSpecDefinition extends FeatureDefinition {
|
|
|
249
249
|
directive @connect(
|
|
250
250
|
source: String
|
|
251
251
|
id: String
|
|
252
|
-
http: ConnectHTTP
|
|
252
|
+
http: ConnectHTTP
|
|
253
253
|
batch: ConnectBatch
|
|
254
254
|
errors: ConnectorErrors
|
|
255
255
|
selection: JSONSelection!
|
|
@@ -277,7 +277,7 @@ export class ConnectSpecDefinition extends FeatureDefinition {
|
|
|
277
277
|
type: (schema, feature) => {
|
|
278
278
|
const connectHttpType =
|
|
279
279
|
lookupFeatureTypeInSchema<InputObjectType>(CONNECT_HTTP, 'InputObjectType', schema, feature);
|
|
280
|
-
return
|
|
280
|
+
return connectHttpType;
|
|
281
281
|
}
|
|
282
282
|
},
|
|
283
283
|
{
|
package/src/specs/coreSpec.ts
CHANGED
|
@@ -209,6 +209,9 @@ export type CoreImport = {
|
|
|
209
209
|
as?: string,
|
|
210
210
|
};
|
|
211
211
|
|
|
212
|
+
// The RegExp for a GraphQL name, plus an optional leading "@".
|
|
213
|
+
const importRegExp = /^@?[_A-Za-z][_0-9A-Za-z]*$/;
|
|
214
|
+
|
|
212
215
|
export function extractCoreFeatureImports(url: FeatureUrl, directive: Directive<SchemaDefinition, CoreOrLinkDirectiveArgs>): CoreImport[] {
|
|
213
216
|
// Note: up to this point, we've kind of cheated with typing and force-casted the arguments to `CoreOrLinkDirectiveArgs`, and while this
|
|
214
217
|
// graphQL type validations ensure this is "mostly" true, the `import' arg is an exception becuse it uses the `link__Import` scalar,
|
|
@@ -249,6 +252,13 @@ export function extractCoreFeatureImports(url: FeatureUrl, directive: Directive<
|
|
|
249
252
|
));
|
|
250
253
|
continue importArgLoop;
|
|
251
254
|
}
|
|
255
|
+
if (!importRegExp.test(value)) {
|
|
256
|
+
errors.push(ERRORS.INVALID_LINK_DIRECTIVE_USAGE.err(
|
|
257
|
+
`Invalid value for the "name" field for sub-value ${valueToString(elt)} of @link(import:) argument: must use a GraphQL name.`,
|
|
258
|
+
{ nodes: directive.sourceAST },
|
|
259
|
+
));
|
|
260
|
+
continue importArgLoop;
|
|
261
|
+
}
|
|
252
262
|
name = value;
|
|
253
263
|
break;
|
|
254
264
|
case 'as':
|
|
@@ -259,6 +269,13 @@ export function extractCoreFeatureImports(url: FeatureUrl, directive: Directive<
|
|
|
259
269
|
));
|
|
260
270
|
continue importArgLoop;
|
|
261
271
|
}
|
|
272
|
+
if (!importRegExp.test(value)) {
|
|
273
|
+
errors.push(ERRORS.INVALID_LINK_DIRECTIVE_USAGE.err(
|
|
274
|
+
`Invalid value for the "as" field for sub-value ${valueToString(elt)} of @link(import:) argument: must use a GraphQL name.`,
|
|
275
|
+
{ nodes: directive.sourceAST },
|
|
276
|
+
));
|
|
277
|
+
continue importArgLoop;
|
|
278
|
+
}
|
|
262
279
|
break;
|
|
263
280
|
default:
|
|
264
281
|
errors.push(ERRORS.INVALID_LINK_DIRECTIVE_USAGE.err(
|
|
@@ -212,6 +212,7 @@ export const FEDERATION_VERSIONS = new FeatureDefinitions<FederationSpecDefiniti
|
|
|
212
212
|
.add(new FederationSpecDefinition(new FeatureVersion(2, 10)))
|
|
213
213
|
.add(new FederationSpecDefinition(new FeatureVersion(2, 11)))
|
|
214
214
|
.add(new FederationSpecDefinition(new FeatureVersion(2, 12)))
|
|
215
|
-
.add(new FederationSpecDefinition(new FeatureVersion(2, 13)))
|
|
215
|
+
.add(new FederationSpecDefinition(new FeatureVersion(2, 13)))
|
|
216
|
+
.add(new FederationSpecDefinition(new FeatureVersion(2, 14)));
|
|
216
217
|
|
|
217
218
|
registerKnownFeature(FEDERATION_VERSIONS);
|
package/src/utils.ts
CHANGED
|
@@ -375,15 +375,18 @@ export function printHumanReadableList(
|
|
|
375
375
|
|
|
376
376
|
const { lastIdx } = names.reduce(
|
|
377
377
|
({ lastIdx, length }, name) => {
|
|
378
|
-
|
|
378
|
+
const newLength = length + name.length;
|
|
379
|
+
if (newLength > cutoff) {
|
|
380
|
+
// there is no short-circuit logic in reduce
|
|
381
|
+
// if we already exceeded the cutoff length we need to pass the length that exceeded cutoff
|
|
379
382
|
return {
|
|
380
383
|
lastIdx,
|
|
381
|
-
length,
|
|
384
|
+
length: newLength,
|
|
382
385
|
};
|
|
383
386
|
}
|
|
384
387
|
return {
|
|
385
388
|
lastIdx: lastIdx + 1,
|
|
386
|
-
length:
|
|
389
|
+
length: newLength,
|
|
387
390
|
};
|
|
388
391
|
},
|
|
389
392
|
{ lastIdx: 0, length: 0}
|