@apollo/federation-internals 2.9.0 → 2.10.0-alpha.1
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/error.d.ts +0 -19
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +0 -38
- package/dist/error.js.map +1 -1
- package/dist/federation.d.ts +2 -6
- package/dist/federation.d.ts.map +1 -1
- package/dist/federation.js +2 -24
- package/dist/federation.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/knownCoreFeatures.d.ts +0 -3
- package/dist/knownCoreFeatures.d.ts.map +1 -1
- package/dist/knownCoreFeatures.js +1 -12
- package/dist/knownCoreFeatures.js.map +1 -1
- package/dist/specs/connectSpec.d.ts +12 -0
- package/dist/specs/connectSpec.d.ts.map +1 -0
- package/dist/specs/connectSpec.js +83 -0
- package/dist/specs/connectSpec.js.map +1 -0
- package/dist/specs/coreSpec.d.ts +0 -1
- package/dist/specs/coreSpec.d.ts.map +1 -1
- package/dist/specs/coreSpec.js +2 -5
- package/dist/specs/coreSpec.js.map +1 -1
- package/dist/specs/federationSpec.d.ts +0 -3
- package/dist/specs/federationSpec.d.ts.map +1 -1
- package/dist/specs/federationSpec.js +3 -9
- package/dist/specs/federationSpec.js.map +1 -1
- package/dist/supergraphs.d.ts +1 -1
- package/dist/supergraphs.d.ts.map +1 -1
- package/dist/supergraphs.js +1 -0
- package/dist/supergraphs.js.map +1 -1
- package/package.json +1 -1
- package/src/error.ts +4 -137
- package/src/federation.ts +24 -59
- package/src/index.ts +1 -1
- package/src/knownCoreFeatures.ts +0 -15
- package/src/specs/connectSpec.ts +148 -0
- package/src/specs/coreSpec.ts +2 -7
- package/src/specs/federationSpec.ts +3 -10
- package/src/supergraphs.ts +2 -1
- package/dist/specs/sourceSpec.d.ts +0 -69
- package/dist/specs/sourceSpec.d.ts.map +0 -1
- package/dist/specs/sourceSpec.js +0 -345
- package/dist/specs/sourceSpec.js.map +0 -1
- package/src/specs/sourceSpec.ts +0 -607
package/src/federation.ts
CHANGED
|
@@ -93,13 +93,8 @@ import {
|
|
|
93
93
|
import { defaultPrintOptions, PrintOptions as PrintOptions, printSchema } from "./print";
|
|
94
94
|
import { createObjectTypeSpecification, createScalarTypeSpecification, createUnionTypeSpecification } from "./directiveAndTypeSpecification";
|
|
95
95
|
import { didYouMean, suggestionList } from "./suggestions";
|
|
96
|
-
import { coreFeatureDefinitionIfKnown
|
|
96
|
+
import { coreFeatureDefinitionIfKnown } from "./knownCoreFeatures";
|
|
97
97
|
import { joinIdentity } from "./specs/joinSpec";
|
|
98
|
-
import {
|
|
99
|
-
SourceAPIDirectiveArgs,
|
|
100
|
-
SourceFieldDirectiveArgs,
|
|
101
|
-
SourceTypeDirectiveArgs,
|
|
102
|
-
} from "./specs/sourceSpec";
|
|
103
98
|
import { CostDirectiveArguments, ListSizeDirectiveArguments } from "./specs/costSpec";
|
|
104
99
|
|
|
105
100
|
const linkSpec = LINK_VERSIONS.latest();
|
|
@@ -398,10 +393,10 @@ const validateFieldValueType = ({
|
|
|
398
393
|
fromContextParent: ArgumentDefinition<FieldDefinition<ObjectType | InterfaceType | UnionType>>,
|
|
399
394
|
}): { resolvedType: InputType | undefined } => {
|
|
400
395
|
const selections = selectionSet.selections();
|
|
401
|
-
|
|
396
|
+
|
|
402
397
|
// ensure that type is not an interfaceObject
|
|
403
398
|
const interfaceObjectDirective = metadata.interfaceObjectDirective();
|
|
404
|
-
if (currentType.kind === 'ObjectType' && isFederationDirectiveDefinedInSchema(interfaceObjectDirective) && (currentType.appliedDirectivesOf(interfaceObjectDirective).length > 0)) {
|
|
399
|
+
if (currentType.kind === 'ObjectType' && isFederationDirectiveDefinedInSchema(interfaceObjectDirective) && (currentType.appliedDirectivesOf(interfaceObjectDirective).length > 0)) {
|
|
405
400
|
errorCollector.push(ERRORS.CONTEXT_INVALID_SELECTION.err(
|
|
406
401
|
`Context "is used in "${fromContextParent.coordinate}" but the selection is invalid: One of the types in the selection is an interfaceObject: "${currentType.name}"`,
|
|
407
402
|
{ nodes: sourceASTs(fromContextParent) }
|
|
@@ -595,7 +590,7 @@ function validateFieldValue({
|
|
|
595
590
|
if (selectionType === 'error') {
|
|
596
591
|
return;
|
|
597
592
|
}
|
|
598
|
-
|
|
593
|
+
|
|
599
594
|
const usedTypeConditions = new Set<string>;
|
|
600
595
|
for (const location of setContextLocations) {
|
|
601
596
|
// for each location, we need to validate that the selection will result in exactly one field being selected
|
|
@@ -622,7 +617,7 @@ function validateFieldValue({
|
|
|
622
617
|
{ nodes: sourceASTs(fromContextParent) }
|
|
623
618
|
));
|
|
624
619
|
}
|
|
625
|
-
|
|
620
|
+
|
|
626
621
|
if (selectionType === 'field') {
|
|
627
622
|
const { resolvedType } = validateFieldValueType({
|
|
628
623
|
currentType: location,
|
|
@@ -795,13 +790,13 @@ export function collectUsedFields(metadata: FederationMetadata): Set<FieldDefini
|
|
|
795
790
|
},
|
|
796
791
|
usedFields,
|
|
797
792
|
);
|
|
798
|
-
|
|
793
|
+
|
|
799
794
|
// also for @fromContext
|
|
800
795
|
collectUsedFieldsForFromContext<CompositeType>(
|
|
801
796
|
metadata,
|
|
802
797
|
usedFields,
|
|
803
798
|
);
|
|
804
|
-
|
|
799
|
+
|
|
805
800
|
// Collects all fields used to satisfy an interface constraint
|
|
806
801
|
for (const itfType of metadata.schema.interfaceTypes()) {
|
|
807
802
|
const runtimeTypes = itfType.possibleRuntimeTypes();
|
|
@@ -824,12 +819,12 @@ function collectUsedFieldsForFromContext<TParent extends SchemaElement<any, any>
|
|
|
824
819
|
) {
|
|
825
820
|
const fromContextDirective = metadata.fromContextDirective();
|
|
826
821
|
const contextDirective = metadata.contextDirective();
|
|
827
|
-
|
|
822
|
+
|
|
828
823
|
// if one of the directives is not defined, there's nothing to validate
|
|
829
824
|
if (!isFederationDirectiveDefinedInSchema(fromContextDirective) || !isFederationDirectiveDefinedInSchema(contextDirective)) {
|
|
830
|
-
return;
|
|
825
|
+
return;
|
|
831
826
|
}
|
|
832
|
-
|
|
827
|
+
|
|
833
828
|
// build the list of context entry points
|
|
834
829
|
const entryPoints = new Map<string, Set<CompositeType>>();
|
|
835
830
|
for (const application of contextDirective.applications()) {
|
|
@@ -842,9 +837,9 @@ function collectUsedFieldsForFromContext<TParent extends SchemaElement<any, any>
|
|
|
842
837
|
if (!entryPoints.has(context)) {
|
|
843
838
|
entryPoints.set(context, new Set());
|
|
844
839
|
}
|
|
845
|
-
entryPoints.get(context)!.add(type as CompositeType);
|
|
840
|
+
entryPoints.get(context)!.add(type as CompositeType);
|
|
846
841
|
}
|
|
847
|
-
|
|
842
|
+
|
|
848
843
|
for (const application of fromContextDirective.applications()) {
|
|
849
844
|
const type = application.parent as TParent;
|
|
850
845
|
if (!type) {
|
|
@@ -854,20 +849,20 @@ function collectUsedFieldsForFromContext<TParent extends SchemaElement<any, any>
|
|
|
854
849
|
|
|
855
850
|
const fieldValue = application.arguments().field;
|
|
856
851
|
const { context, selection } = parseContext(fieldValue);
|
|
857
|
-
|
|
852
|
+
|
|
858
853
|
if (!context) {
|
|
859
854
|
continue;
|
|
860
855
|
}
|
|
861
|
-
|
|
856
|
+
|
|
862
857
|
// now we need to collect all the fields used for every type that they could be used for
|
|
863
858
|
const contextTypes = entryPoints.get(context);
|
|
864
859
|
if (!contextTypes) {
|
|
865
860
|
continue;
|
|
866
861
|
}
|
|
867
|
-
|
|
862
|
+
|
|
868
863
|
for (const contextType of contextTypes) {
|
|
869
864
|
try {
|
|
870
|
-
// helper function
|
|
865
|
+
// helper function
|
|
871
866
|
const fieldAccessor = (t: CompositeType, f: string) => {
|
|
872
867
|
const field = t.field(f);
|
|
873
868
|
if (field) {
|
|
@@ -883,7 +878,7 @@ function collectUsedFieldsForFromContext<TParent extends SchemaElement<any, any>
|
|
|
883
878
|
}
|
|
884
879
|
return field;
|
|
885
880
|
};
|
|
886
|
-
|
|
881
|
+
|
|
887
882
|
parseSelectionSet({ parentType: contextType, source: selection, fieldAccessor });
|
|
888
883
|
} catch (e) {
|
|
889
884
|
// ignore the error, it will be caught later
|
|
@@ -1256,18 +1251,6 @@ export class FederationMetadata {
|
|
|
1256
1251
|
return this.getPost20FederationDirective(FederationDirectiveName.POLICY);
|
|
1257
1252
|
}
|
|
1258
1253
|
|
|
1259
|
-
sourceAPIDirective(): Post20FederationDirectiveDefinition<SourceAPIDirectiveArgs> {
|
|
1260
|
-
return this.getPost20FederationDirective(FederationDirectiveName.SOURCE_API);
|
|
1261
|
-
}
|
|
1262
|
-
|
|
1263
|
-
sourceTypeDirective(): Post20FederationDirectiveDefinition<SourceTypeDirectiveArgs> {
|
|
1264
|
-
return this.getPost20FederationDirective(FederationDirectiveName.SOURCE_TYPE);
|
|
1265
|
-
}
|
|
1266
|
-
|
|
1267
|
-
sourceFieldDirective(): Post20FederationDirectiveDefinition<SourceFieldDirectiveArgs> {
|
|
1268
|
-
return this.getPost20FederationDirective(FederationDirectiveName.SOURCE_FIELD);
|
|
1269
|
-
}
|
|
1270
|
-
|
|
1271
1254
|
fromContextDirective(): Post20FederationDirectiveDefinition<{ field: string }> {
|
|
1272
1255
|
return this.getPost20FederationDirective(FederationDirectiveName.FROM_CONTEXT);
|
|
1273
1256
|
}
|
|
@@ -1324,19 +1307,6 @@ export class FederationMetadata {
|
|
|
1324
1307
|
baseDirectives.push(policyDirective);
|
|
1325
1308
|
}
|
|
1326
1309
|
|
|
1327
|
-
const sourceAPIDirective = this.sourceAPIDirective();
|
|
1328
|
-
if (isFederationDirectiveDefinedInSchema(sourceAPIDirective)) {
|
|
1329
|
-
baseDirectives.push(sourceAPIDirective);
|
|
1330
|
-
}
|
|
1331
|
-
const sourceTypeDirective = this.sourceTypeDirective();
|
|
1332
|
-
if (isFederationDirectiveDefinedInSchema(sourceTypeDirective)) {
|
|
1333
|
-
baseDirectives.push(sourceTypeDirective);
|
|
1334
|
-
}
|
|
1335
|
-
const sourceFieldDirective = this.sourceFieldDirective();
|
|
1336
|
-
if (isFederationDirectiveDefinedInSchema(sourceFieldDirective)) {
|
|
1337
|
-
baseDirectives.push(sourceFieldDirective);
|
|
1338
|
-
}
|
|
1339
|
-
|
|
1340
1310
|
const contextDirective = this.contextDirective();
|
|
1341
1311
|
if (isFederationDirectiveDefinedInSchema(contextDirective)) {
|
|
1342
1312
|
baseDirectives.push(contextDirective);
|
|
@@ -1595,7 +1565,6 @@ export class FederationBlueprint extends SchemaBlueprint {
|
|
|
1595
1565
|
for (const application of contextDirective.applications()) {
|
|
1596
1566
|
const parent = application.parent;
|
|
1597
1567
|
const name = application.arguments().name as string;
|
|
1598
|
-
|
|
1599
1568
|
const match = name.match(/^([A-Za-z]\w*)$/);
|
|
1600
1569
|
if (name.includes('_')) {
|
|
1601
1570
|
errorCollector.push(ERRORS.CONTEXT_NAME_INVALID.err(
|
|
@@ -1621,7 +1590,7 @@ export class FederationBlueprint extends SchemaBlueprint {
|
|
|
1621
1590
|
for (const application of fromContextDirective.applications()) {
|
|
1622
1591
|
const { field } = application.arguments();
|
|
1623
1592
|
const { context, selection } = parseContext(field);
|
|
1624
|
-
|
|
1593
|
+
|
|
1625
1594
|
// error if parent's parent is a directive definition
|
|
1626
1595
|
if (application.parent.parent.kind === 'DirectiveDefinition') {
|
|
1627
1596
|
errorCollector.push(ERRORS.CONTEXT_NOT_SET.err(
|
|
@@ -1653,14 +1622,14 @@ export class FederationBlueprint extends SchemaBlueprint {
|
|
|
1653
1622
|
));
|
|
1654
1623
|
}
|
|
1655
1624
|
}
|
|
1656
|
-
|
|
1625
|
+
|
|
1657
1626
|
if (parent.defaultValue !== undefined) {
|
|
1658
1627
|
errorCollector.push(ERRORS.CONTEXT_NOT_SET.err(
|
|
1659
1628
|
`@fromContext arguments may not have a default value: "${parent.coordinate}".`,
|
|
1660
1629
|
{ nodes: sourceASTs(application) }
|
|
1661
|
-
));
|
|
1630
|
+
));
|
|
1662
1631
|
}
|
|
1663
|
-
|
|
1632
|
+
|
|
1664
1633
|
if (!context || !selection) {
|
|
1665
1634
|
errorCollector.push(ERRORS.NO_CONTEXT_IN_SELECTION.err(
|
|
1666
1635
|
`@fromContext argument does not reference a context "${field}".`,
|
|
@@ -1683,7 +1652,7 @@ export class FederationBlueprint extends SchemaBlueprint {
|
|
|
1683
1652
|
metadata,
|
|
1684
1653
|
});
|
|
1685
1654
|
}
|
|
1686
|
-
|
|
1655
|
+
|
|
1687
1656
|
// validate that there is at least one resolvable key on the type
|
|
1688
1657
|
const keyDirective = metadata.keyDirective();
|
|
1689
1658
|
const keyApplications = objectType.appliedDirectivesOf(keyDirective);
|
|
@@ -1701,10 +1670,6 @@ export class FederationBlueprint extends SchemaBlueprint {
|
|
|
1701
1670
|
validateKeyOnInterfacesAreAlsoOnAllImplementations(metadata, errorCollector);
|
|
1702
1671
|
validateInterfaceObjectsAreOnEntities(metadata, errorCollector);
|
|
1703
1672
|
|
|
1704
|
-
// FeatureDefinition objects passed to registerKnownFeature can register
|
|
1705
|
-
// validation functions for subgraph schemas by overriding the
|
|
1706
|
-
// validateSubgraphSchema method.
|
|
1707
|
-
validateKnownFeatures(schema, errorCollector);
|
|
1708
1673
|
// If tag is redefined by the user, make sure the definition is compatible with what we expect
|
|
1709
1674
|
const tagDirective = metadata.tagDirective();
|
|
1710
1675
|
if (tagDirective) {
|
|
@@ -1850,9 +1815,9 @@ export function setSchemaAsFed2Subgraph(schema: Schema, useLatest: boolean = fal
|
|
|
1850
1815
|
|
|
1851
1816
|
// This is the full @link declaration as added by `asFed2SubgraphDocument`. It's here primarily for uses by tests that print and match
|
|
1852
1817
|
// subgraph schema to avoid having to update 20+ tests every time we use a new directive or the order of import changes ...
|
|
1853
|
-
export const FEDERATION2_LINK_WITH_FULL_IMPORTS = '@link(url: "https://specs.apollo.dev/federation/v2.
|
|
1818
|
+
export const FEDERATION2_LINK_WITH_FULL_IMPORTS = '@link(url: "https://specs.apollo.dev/federation/v2.10", import: ["@key", "@requires", "@provides", "@external", "@tag", "@extends", "@shareable", "@inaccessible", "@override", "@composeDirective", "@interfaceObject", "@authenticated", "@requiresScopes", "@policy", "@context", "@fromContext", "@cost", "@listSize"])';
|
|
1854
1819
|
// This is the full @link declaration that is added when upgrading fed v1 subgraphs to v2 version. It should only be used by tests.
|
|
1855
|
-
export const FEDERATION2_LINK_WITH_AUTO_EXPANDED_IMPORTS = '@link(url: "https://specs.apollo.dev/federation/v2.
|
|
1820
|
+
export const FEDERATION2_LINK_WITH_AUTO_EXPANDED_IMPORTS = '@link(url: "https://specs.apollo.dev/federation/v2.10", import: ["@key", "@requires", "@provides", "@external", "@tag", "@extends", "@shareable", "@inaccessible", "@override", "@composeDirective", "@interfaceObject"])';
|
|
1856
1821
|
|
|
1857
1822
|
// This is the federation @link for tests that go through the SchemaUpgrader.
|
|
1858
1823
|
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/index.ts
CHANGED
|
@@ -24,5 +24,5 @@ export * from './argumentCompositionStrategies';
|
|
|
24
24
|
export * from './specs/authenticatedSpec';
|
|
25
25
|
export * from './specs/requiresScopesSpec';
|
|
26
26
|
export * from './specs/policySpec';
|
|
27
|
-
export * from './specs/
|
|
27
|
+
export * from './specs/connectSpec';
|
|
28
28
|
export * from './specs/costSpec';
|
package/src/knownCoreFeatures.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { GraphQLError } from "graphql";
|
|
2
|
-
import { Schema } from "./definitions";
|
|
3
1
|
import { FeatureDefinition, FeatureDefinitions, FeatureUrl } from "./specs/coreSpec";
|
|
4
2
|
|
|
5
3
|
const registeredFeatures = new Map<string, FeatureDefinitions>();
|
|
@@ -14,19 +12,6 @@ export function coreFeatureDefinitionIfKnown(url: FeatureUrl): FeatureDefinition
|
|
|
14
12
|
return registeredFeatures.get(url.identity)?.find(url.version);
|
|
15
13
|
}
|
|
16
14
|
|
|
17
|
-
export function validateKnownFeatures(
|
|
18
|
-
schema: Schema,
|
|
19
|
-
errorCollector: GraphQLError[] = [],
|
|
20
|
-
): GraphQLError[] {
|
|
21
|
-
registeredFeatures.forEach(definitions => {
|
|
22
|
-
const feature = definitions.latest();
|
|
23
|
-
if (feature.validateSubgraphSchema !== FeatureDefinition.prototype.validateSubgraphSchema) {
|
|
24
|
-
errorCollector.push(...feature.validateSubgraphSchema(schema));
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
return errorCollector;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
15
|
/**
|
|
31
16
|
* Removes a feature from the set of known features.
|
|
32
17
|
*
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import {DirectiveLocation, GraphQLError} from 'graphql';
|
|
2
|
+
import { CorePurpose, FeatureDefinition, FeatureDefinitions, FeatureUrl, FeatureVersion } from "./coreSpec";
|
|
3
|
+
import {
|
|
4
|
+
Schema,
|
|
5
|
+
NonNullType,
|
|
6
|
+
InputObjectType,
|
|
7
|
+
InputFieldDefinition,
|
|
8
|
+
ListType,
|
|
9
|
+
} from '../definitions';
|
|
10
|
+
import { registerKnownFeature } from '../knownCoreFeatures';
|
|
11
|
+
import { createDirectiveSpecification, createScalarTypeSpecification } from '../directiveAndTypeSpecification';
|
|
12
|
+
|
|
13
|
+
export const connectIdentity = 'https://specs.apollo.dev/connect';
|
|
14
|
+
|
|
15
|
+
const CONNECT = "connect";
|
|
16
|
+
const SOURCE = "source";
|
|
17
|
+
const URL_PATH_TEMPLATE = "URLPathTemplate";
|
|
18
|
+
const JSON_SELECTION = "JSONSelection";
|
|
19
|
+
const CONNECT_HTTP = "ConnectHTTP";
|
|
20
|
+
const SOURCE_HTTP = "SourceHTTP";
|
|
21
|
+
const HTTP_HEADER_MAPPING = "HTTPHeaderMapping";
|
|
22
|
+
|
|
23
|
+
export class ConnectSpecDefinition extends FeatureDefinition {
|
|
24
|
+
constructor(version: FeatureVersion, readonly minimumFederationVersion: FeatureVersion) {
|
|
25
|
+
super(new FeatureUrl(connectIdentity, CONNECT, version), minimumFederationVersion);
|
|
26
|
+
|
|
27
|
+
this.registerDirective(createDirectiveSpecification({
|
|
28
|
+
name: CONNECT,
|
|
29
|
+
locations: [DirectiveLocation.FIELD_DEFINITION],
|
|
30
|
+
repeatable: true,
|
|
31
|
+
// We "compose" these directives using the `@join__directive` mechanism,
|
|
32
|
+
// so they do not need to be composed in the way passing `composes: true`
|
|
33
|
+
// here implies.
|
|
34
|
+
composes: false,
|
|
35
|
+
}));
|
|
36
|
+
|
|
37
|
+
this.registerDirective(createDirectiveSpecification({
|
|
38
|
+
name: SOURCE,
|
|
39
|
+
locations: [DirectiveLocation.SCHEMA],
|
|
40
|
+
repeatable: true,
|
|
41
|
+
composes: false,
|
|
42
|
+
}));
|
|
43
|
+
|
|
44
|
+
this.registerType(createScalarTypeSpecification({ name: URL_PATH_TEMPLATE }));
|
|
45
|
+
this.registerType(createScalarTypeSpecification({ name: JSON_SELECTION }));
|
|
46
|
+
this.registerType({ name: CONNECT_HTTP, checkOrAdd: () => [] });
|
|
47
|
+
this.registerType({ name: SOURCE_HTTP, checkOrAdd: () => [] });
|
|
48
|
+
this.registerType({ name: HTTP_HEADER_MAPPING, checkOrAdd: () => [] });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
addElementsToSchema(schema: Schema): GraphQLError[] {
|
|
52
|
+
/* scalar URLPathTemplate */
|
|
53
|
+
const URLPathTemplate = this.addScalarType(schema, URL_PATH_TEMPLATE);
|
|
54
|
+
|
|
55
|
+
/* scalar JSONSelection */
|
|
56
|
+
const JSONSelection = this.addScalarType(schema, JSON_SELECTION);
|
|
57
|
+
|
|
58
|
+
/*
|
|
59
|
+
directive @connect(
|
|
60
|
+
source: String
|
|
61
|
+
http: ConnectHTTP
|
|
62
|
+
selection: JSONSelection!
|
|
63
|
+
entity: Boolean = false
|
|
64
|
+
) repeatable on FIELD_DEFINITION
|
|
65
|
+
*/
|
|
66
|
+
const connect = this.addDirective(schema, CONNECT).addLocations(DirectiveLocation.FIELD_DEFINITION);
|
|
67
|
+
connect.repeatable = true;
|
|
68
|
+
|
|
69
|
+
connect.addArgument(SOURCE, schema.stringType());
|
|
70
|
+
|
|
71
|
+
/*
|
|
72
|
+
input HTTPHeaderMapping {
|
|
73
|
+
name: String!
|
|
74
|
+
from: String
|
|
75
|
+
value: String
|
|
76
|
+
}
|
|
77
|
+
*/
|
|
78
|
+
const HTTPHeaderMapping = schema.addType(new InputObjectType(this.typeNameInSchema(schema, HTTP_HEADER_MAPPING)!));
|
|
79
|
+
HTTPHeaderMapping.addField(new InputFieldDefinition('name')).type =
|
|
80
|
+
new NonNullType(schema.stringType());
|
|
81
|
+
HTTPHeaderMapping.addField(new InputFieldDefinition('from')).type =
|
|
82
|
+
schema.stringType();
|
|
83
|
+
HTTPHeaderMapping.addField(new InputFieldDefinition('value')).type =
|
|
84
|
+
schema.stringType();
|
|
85
|
+
|
|
86
|
+
/*
|
|
87
|
+
input ConnectHTTP {
|
|
88
|
+
GET: URLPathTemplate
|
|
89
|
+
POST: URLPathTemplate
|
|
90
|
+
PUT: URLPathTemplate
|
|
91
|
+
PATCH: URLPathTemplate
|
|
92
|
+
DELETE: URLPathTemplate
|
|
93
|
+
body: JSONSelection
|
|
94
|
+
headers: [HTTPHeaderMapping!]
|
|
95
|
+
}
|
|
96
|
+
*/
|
|
97
|
+
const ConnectHTTP = schema.addType(new InputObjectType(this.typeNameInSchema(schema, CONNECT_HTTP)!));
|
|
98
|
+
ConnectHTTP.addField(new InputFieldDefinition('GET')).type = URLPathTemplate;
|
|
99
|
+
ConnectHTTP.addField(new InputFieldDefinition('POST')).type = URLPathTemplate;
|
|
100
|
+
ConnectHTTP.addField(new InputFieldDefinition('PUT')).type = URLPathTemplate;
|
|
101
|
+
ConnectHTTP.addField(new InputFieldDefinition('PATCH')).type = URLPathTemplate;
|
|
102
|
+
ConnectHTTP.addField(new InputFieldDefinition('DELETE')).type = URLPathTemplate;
|
|
103
|
+
ConnectHTTP.addField(new InputFieldDefinition('body')).type = JSONSelection;
|
|
104
|
+
ConnectHTTP.addField(new InputFieldDefinition('headers')).type =
|
|
105
|
+
new ListType(new NonNullType(HTTPHeaderMapping));
|
|
106
|
+
connect.addArgument('http', new NonNullType(ConnectHTTP));
|
|
107
|
+
|
|
108
|
+
connect.addArgument('selection', new NonNullType(JSONSelection));
|
|
109
|
+
connect.addArgument('entity', schema.booleanType(), false);
|
|
110
|
+
|
|
111
|
+
/*
|
|
112
|
+
directive @source(
|
|
113
|
+
name: String!
|
|
114
|
+
http: ConnectHTTP
|
|
115
|
+
) repeatable on SCHEMA
|
|
116
|
+
*/
|
|
117
|
+
const source = this.addDirective(schema, SOURCE).addLocations(
|
|
118
|
+
DirectiveLocation.SCHEMA,
|
|
119
|
+
);
|
|
120
|
+
source.repeatable = true;
|
|
121
|
+
source.addArgument('name', new NonNullType(schema.stringType()));
|
|
122
|
+
|
|
123
|
+
/*
|
|
124
|
+
input SourceHTTP {
|
|
125
|
+
baseURL: String!
|
|
126
|
+
headers: [HTTPHeaderMapping!]
|
|
127
|
+
}
|
|
128
|
+
*/
|
|
129
|
+
const SourceHTTP = schema.addType(new InputObjectType(this.typeNameInSchema(schema, SOURCE_HTTP)!));
|
|
130
|
+
SourceHTTP.addField(new InputFieldDefinition('baseURL')).type =
|
|
131
|
+
new NonNullType(schema.stringType());
|
|
132
|
+
SourceHTTP.addField(new InputFieldDefinition('headers')).type =
|
|
133
|
+
new ListType(new NonNullType(HTTPHeaderMapping));
|
|
134
|
+
|
|
135
|
+
source.addArgument('http', new NonNullType(SourceHTTP));
|
|
136
|
+
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
get defaultCorePurpose(): CorePurpose {
|
|
141
|
+
return 'EXECUTION';
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export const CONNECT_VERSIONS = new FeatureDefinitions<ConnectSpecDefinition>(connectIdentity)
|
|
146
|
+
.add(new ConnectSpecDefinition(new FeatureVersion(0, 1), new FeatureVersion(2, 10)));
|
|
147
|
+
|
|
148
|
+
registerKnownFeature(CONNECT_VERSIONS);
|
package/src/specs/coreSpec.ts
CHANGED
|
@@ -117,11 +117,6 @@ export abstract class FeatureDefinition {
|
|
|
117
117
|
.concat(this.typeSpecs().map((spec) => spec.name));
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
// No-op implementation that can be overridden by subclasses.
|
|
121
|
-
validateSubgraphSchema(_schema: Schema): GraphQLError[] {
|
|
122
|
-
return [];
|
|
123
|
-
}
|
|
124
|
-
|
|
125
120
|
protected nameInSchema(schema: Schema): string | undefined {
|
|
126
121
|
const feature = this.featureInSchema(schema);
|
|
127
122
|
return feature?.nameInSchema;
|
|
@@ -623,7 +618,7 @@ export class FeatureDefinitions<T extends FeatureDefinition = FeatureDefinition>
|
|
|
623
618
|
// this._definitions is already sorted with the most recent first
|
|
624
619
|
// get the first definition that is compatible with the federation version
|
|
625
620
|
// if the minimum version is not present, assume that we won't look for an older version
|
|
626
|
-
const def = this._definitions.find(def => def.minimumFederationVersion ? fedVersion
|
|
621
|
+
const def = this._definitions.find(def => def.minimumFederationVersion ? fedVersion.gte(def.minimumFederationVersion) : true);
|
|
627
622
|
assert(def, `No compatible definition exists for federation version ${fedVersion}`);
|
|
628
623
|
|
|
629
624
|
// note that it's necessary that we can only get versions that have the same major version as the latest,
|
|
@@ -675,7 +670,7 @@ export class FeatureVersion {
|
|
|
675
670
|
let max: FeatureVersion | undefined;
|
|
676
671
|
|
|
677
672
|
for (const version of versions) {
|
|
678
|
-
if (!max || version
|
|
673
|
+
if (!max || version.gt(max)) {
|
|
679
674
|
max = version;
|
|
680
675
|
}
|
|
681
676
|
}
|
|
@@ -18,7 +18,6 @@ import { INACCESSIBLE_VERSIONS } from "./inaccessibleSpec";
|
|
|
18
18
|
import { AUTHENTICATED_VERSIONS } from "./authenticatedSpec";
|
|
19
19
|
import { REQUIRES_SCOPES_VERSIONS } from "./requiresScopesSpec";
|
|
20
20
|
import { POLICY_VERSIONS } from './policySpec';
|
|
21
|
-
import { SOURCE_VERSIONS } from './sourceSpec';
|
|
22
21
|
import { CONTEXT_VERSIONS } from './contextSpec';
|
|
23
22
|
import { COST_VERSIONS } from "./costSpec";
|
|
24
23
|
|
|
@@ -44,9 +43,6 @@ export enum FederationDirectiveName {
|
|
|
44
43
|
AUTHENTICATED = 'authenticated',
|
|
45
44
|
REQUIRES_SCOPES = 'requiresScopes',
|
|
46
45
|
POLICY = 'policy',
|
|
47
|
-
SOURCE_API = 'sourceAPI',
|
|
48
|
-
SOURCE_TYPE = 'sourceType',
|
|
49
|
-
SOURCE_FIELD = 'sourceField',
|
|
50
46
|
CONTEXT = 'context',
|
|
51
47
|
FROM_CONTEXT = 'fromContext',
|
|
52
48
|
COST = 'cost',
|
|
@@ -135,7 +131,7 @@ export class FederationSpecDefinition extends FeatureDefinition {
|
|
|
135
131
|
|
|
136
132
|
this.registerSubFeature(INACCESSIBLE_VERSIONS.getMinimumRequiredVersion(version));
|
|
137
133
|
|
|
138
|
-
if (version
|
|
134
|
+
if (version.gte(new FeatureVersion(2, 7))) {
|
|
139
135
|
this.registerDirective(createDirectiveSpecification({
|
|
140
136
|
name: FederationDirectiveName.OVERRIDE,
|
|
141
137
|
locations: [DirectiveLocation.FIELD_DEFINITION],
|
|
@@ -178,10 +174,6 @@ export class FederationSpecDefinition extends FeatureDefinition {
|
|
|
178
174
|
this.registerSubFeature(POLICY_VERSIONS.find(new FeatureVersion(0, 1))!);
|
|
179
175
|
}
|
|
180
176
|
|
|
181
|
-
if (version.gte(new FeatureVersion(2, 7))) {
|
|
182
|
-
this.registerSubFeature(SOURCE_VERSIONS.find(new FeatureVersion(0, 1))!);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
177
|
if (version.gte(new FeatureVersion(2, 8))) {
|
|
186
178
|
this.registerSubFeature(CONTEXT_VERSIONS.find(new FeatureVersion(0, 1))!);
|
|
187
179
|
}
|
|
@@ -202,6 +194,7 @@ export const FEDERATION_VERSIONS = new FeatureDefinitions<FederationSpecDefiniti
|
|
|
202
194
|
.add(new FederationSpecDefinition(new FeatureVersion(2, 6)))
|
|
203
195
|
.add(new FederationSpecDefinition(new FeatureVersion(2, 7)))
|
|
204
196
|
.add(new FederationSpecDefinition(new FeatureVersion(2, 8)))
|
|
205
|
-
.add(new FederationSpecDefinition(new FeatureVersion(2, 9)))
|
|
197
|
+
.add(new FederationSpecDefinition(new FeatureVersion(2, 9)))
|
|
198
|
+
.add(new FederationSpecDefinition(new FeatureVersion(2, 10)));
|
|
206
199
|
|
|
207
200
|
registerKnownFeature(FEDERATION_VERSIONS);
|
package/src/supergraphs.ts
CHANGED
|
@@ -41,6 +41,7 @@ export const ROUTER_SUPPORTED_SUPERGRAPH_FEATURES = new Set([
|
|
|
41
41
|
'https://specs.apollo.dev/source/v0.1',
|
|
42
42
|
'https://specs.apollo.dev/context/v0.1',
|
|
43
43
|
'https://specs.apollo.dev/cost/v0.1',
|
|
44
|
+
'https://specs.apollo.dev/connect/v0.1',
|
|
44
45
|
]);
|
|
45
46
|
|
|
46
47
|
const coreVersionZeroDotOneUrl = FeatureUrl.parse('https://specs.apollo.dev/core/v0.1');
|
|
@@ -128,7 +129,7 @@ export class Supergraph {
|
|
|
128
129
|
this.containedSubgraphs = extractSubgraphsNamesAndUrlsFromSupergraph(schema);
|
|
129
130
|
}
|
|
130
131
|
|
|
131
|
-
static build(supergraphSdl: string | DocumentNode, options?: { supportedFeatures?: Set<string
|
|
132
|
+
static build(supergraphSdl: string | DocumentNode, options?: { supportedFeatures?: Set<string> | null, validateSupergraph?: boolean }) {
|
|
132
133
|
// We delay validation because `checkFeatureSupport` in the constructor gives slightly more useful errors if, say, 'for' is used with core v0.1.
|
|
133
134
|
const schema = typeof supergraphSdl === 'string'
|
|
134
135
|
? buildSchema(supergraphSdl, { validate: false })
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { GraphQLError } from 'graphql';
|
|
2
|
-
import { FeatureDefinition, FeatureDefinitions, FeatureVersion } from "./coreSpec";
|
|
3
|
-
import { Schema, DirectiveDefinition } from '../definitions';
|
|
4
|
-
export declare const sourceIdentity = "https://specs.apollo.dev/source";
|
|
5
|
-
export declare class SourceSpecDefinition extends FeatureDefinition {
|
|
6
|
-
readonly minimumFederationVersion: FeatureVersion;
|
|
7
|
-
constructor(version: FeatureVersion, minimumFederationVersion: FeatureVersion);
|
|
8
|
-
addElementsToSchema(schema: Schema): GraphQLError[];
|
|
9
|
-
allElementNames(): string[];
|
|
10
|
-
sourceAPIDirective(schema: Schema): DirectiveDefinition<SourceAPIDirectiveArgs>;
|
|
11
|
-
sourceTypeDirective(schema: Schema): DirectiveDefinition<SourceTypeDirectiveArgs>;
|
|
12
|
-
sourceFieldDirective(schema: Schema): DirectiveDefinition<SourceFieldDirectiveArgs>;
|
|
13
|
-
private getSourceDirectives;
|
|
14
|
-
validateSubgraphSchema(schema: Schema): GraphQLError[];
|
|
15
|
-
private validateSourceAPI;
|
|
16
|
-
private validateSourceType;
|
|
17
|
-
private validateSourceField;
|
|
18
|
-
}
|
|
19
|
-
export type SourceAPIDirectiveArgs = {
|
|
20
|
-
name: string;
|
|
21
|
-
http?: HTTPSourceAPI;
|
|
22
|
-
};
|
|
23
|
-
export type HTTPSourceAPI = {
|
|
24
|
-
baseURL: string;
|
|
25
|
-
headers?: HTTPHeaderMapping[];
|
|
26
|
-
};
|
|
27
|
-
export type HTTPHeaderMapping = {
|
|
28
|
-
name: string;
|
|
29
|
-
as?: string;
|
|
30
|
-
value?: string;
|
|
31
|
-
};
|
|
32
|
-
export type SourceTypeDirectiveArgs = {
|
|
33
|
-
api: string;
|
|
34
|
-
http?: HTTPSourceType;
|
|
35
|
-
selection: JSONSelection;
|
|
36
|
-
keyTypeMap?: KeyTypeMap;
|
|
37
|
-
};
|
|
38
|
-
export type HTTPSourceType = {
|
|
39
|
-
GET?: URLPathTemplate;
|
|
40
|
-
POST?: URLPathTemplate;
|
|
41
|
-
headers?: HTTPHeaderMapping[];
|
|
42
|
-
body?: JSONSelection;
|
|
43
|
-
};
|
|
44
|
-
type URLPathTemplate = string;
|
|
45
|
-
type JSONSelection = string;
|
|
46
|
-
type KeyTypeMap = {
|
|
47
|
-
key: string;
|
|
48
|
-
typeMap: {
|
|
49
|
-
[__typename: string]: string;
|
|
50
|
-
};
|
|
51
|
-
};
|
|
52
|
-
export type SourceFieldDirectiveArgs = {
|
|
53
|
-
api: string;
|
|
54
|
-
http?: HTTPSourceField;
|
|
55
|
-
selection?: JSONSelection;
|
|
56
|
-
keyTypeMap?: KeyTypeMap;
|
|
57
|
-
};
|
|
58
|
-
export type HTTPSourceField = {
|
|
59
|
-
GET?: URLPathTemplate;
|
|
60
|
-
POST?: URLPathTemplate;
|
|
61
|
-
PUT?: URLPathTemplate;
|
|
62
|
-
PATCH?: URLPathTemplate;
|
|
63
|
-
DELETE?: URLPathTemplate;
|
|
64
|
-
body?: JSONSelection;
|
|
65
|
-
headers?: HTTPHeaderMapping[];
|
|
66
|
-
};
|
|
67
|
-
export declare const SOURCE_VERSIONS: FeatureDefinitions<SourceSpecDefinition>;
|
|
68
|
-
export {};
|
|
69
|
-
//# sourceMappingURL=sourceSpec.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sourceSpec.d.ts","sourceRoot":"","sources":["../../src/specs/sourceSpec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,YAAY,EAAQ,MAAM,SAAS,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAc,cAAc,EAAqB,MAAM,YAAY,CAAC;AAClH,OAAO,EACL,MAAM,EAKN,mBAAmB,EAEpB,MAAM,gBAAgB,CAAC;AAKxB,eAAO,MAAM,cAAc,oCAAoC,CAAC;AAEhE,qBAAa,oBAAqB,SAAQ,iBAAiB;IACpB,QAAQ,CAAC,wBAAwB,EAAE,cAAc;gBAA1E,OAAO,EAAE,cAAc,EAAW,wBAAwB,EAAE,cAAc;IA4BtF,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,EAAE;IA2EnD,eAAe,IAAI,MAAM,EAAE;IAgB3B,kBAAkB,CAAC,MAAM,EAAE,MAAM;IAIjC,mBAAmB,CAAC,MAAM,EAAE,MAAM;IAIlC,oBAAoB,CAAC,MAAM,EAAE,MAAM;IAInC,OAAO,CAAC,mBAAmB;IA+BlB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,EAAE;IA+B/D,OAAO,CAAC,iBAAiB;IAkEzB,OAAO,CAAC,kBAAkB;IAiG1B,OAAO,CAAC,mBAAmB;CAiH5B;AA2DD,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,aAAa,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,iBAAiB,EAAE,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,SAAS,EAAE,aAAa,CAAC;IACzB,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,GAAG,CAAC,EAAE,eAAe,CAAC;IACtB,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,OAAO,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAC9B,IAAI,CAAC,EAAE,aAAa,CAAC;CACtB,CAAC;AAEF,KAAK,eAAe,GAAG,MAAM,CAAC;AAC9B,KAAK,aAAa,GAAG,MAAM,CAAC;AAE5B,KAAK,UAAU,GAAG;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE;QACP,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC;KAC9B,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,CAAC,EAAE,eAAe,CAAC;IACtB,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,GAAG,CAAC,EAAE,eAAe,CAAC;IACtB,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,OAAO,CAAC,EAAE,iBAAiB,EAAE,CAAC;CAC/B,CAAC;AAEF,eAAO,MAAM,eAAe,0CACwD,CAAC"}
|