@apollo/federation-internals 2.7.6 → 2.8.0-connectors.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.
Files changed (46) hide show
  1. package/dist/error.d.ts +0 -19
  2. package/dist/error.d.ts.map +1 -1
  3. package/dist/error.js +0 -38
  4. package/dist/error.js.map +1 -1
  5. package/dist/federation.d.ts +1 -5
  6. package/dist/federation.d.ts.map +1 -1
  7. package/dist/federation.js +5 -26
  8. package/dist/federation.js.map +1 -1
  9. package/dist/index.d.ts +1 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +1 -1
  12. package/dist/index.js.map +1 -1
  13. package/dist/knownCoreFeatures.d.ts +0 -3
  14. package/dist/knownCoreFeatures.d.ts.map +1 -1
  15. package/dist/knownCoreFeatures.js +1 -12
  16. package/dist/knownCoreFeatures.js.map +1 -1
  17. package/dist/specs/connectSpec.d.ts +42 -0
  18. package/dist/specs/connectSpec.d.ts.map +1 -0
  19. package/dist/specs/connectSpec.js +82 -0
  20. package/dist/specs/connectSpec.js.map +1 -0
  21. package/dist/specs/coreSpec.d.ts +0 -1
  22. package/dist/specs/coreSpec.d.ts.map +1 -1
  23. package/dist/specs/coreSpec.js +0 -3
  24. package/dist/specs/coreSpec.js.map +1 -1
  25. package/dist/specs/federationSpec.d.ts +1 -4
  26. package/dist/specs/federationSpec.d.ts.map +1 -1
  27. package/dist/specs/federationSpec.js +0 -7
  28. package/dist/specs/federationSpec.js.map +1 -1
  29. package/dist/wasm.d.ts +4 -0
  30. package/dist/wasm.d.ts.map +1 -0
  31. package/dist/wasm.js +62 -0
  32. package/dist/wasm.js.map +1 -0
  33. package/package.json +1 -1
  34. package/src/error.ts +0 -133
  35. package/src/federation.ts +8 -43
  36. package/src/index.ts +1 -1
  37. package/src/knownCoreFeatures.ts +0 -15
  38. package/src/specs/connectSpec.ts +181 -0
  39. package/src/specs/coreSpec.ts +0 -5
  40. package/src/specs/federationSpec.ts +0 -8
  41. package/src/wasm.ts +89 -0
  42. package/dist/specs/sourceSpec.d.ts +0 -69
  43. package/dist/specs/sourceSpec.d.ts.map +0 -1
  44. package/dist/specs/sourceSpec.js +0 -345
  45. package/dist/specs/sourceSpec.js.map +0 -1
  46. package/src/specs/sourceSpec.ts +0 -607
package/src/error.ts CHANGED
@@ -561,119 +561,6 @@ const INTERFACE_KEY_MISSING_IMPLEMENTATION_TYPE = makeCodeDefinition(
561
561
  { addedIn: '2.3.0' },
562
562
  )
563
563
 
564
- const SOURCE_FEDERATION_VERSION_REQUIRED = makeCodeDefinition(
565
- 'SOURCE_FEDERATION_VERSION_REQUIRED',
566
- 'Schemas using `@source{API,Type,Field}` directives must @link-import v2.7 or later of federation',
567
- { addedIn: '2.7.1' },
568
- );
569
-
570
- const SOURCE_API_NAME_INVALID = makeCodeDefinition(
571
- 'SOURCE_API_NAME_INVALID',
572
- 'Each `@sourceAPI` directive must take a unique and valid name as an argument',
573
- { addedIn: '2.7.0' },
574
- );
575
-
576
- const SOURCE_API_PROTOCOL_INVALID = makeCodeDefinition(
577
- 'SOURCE_API_PROTOCOL_INVALID',
578
- 'Each `@sourceAPI` directive must specify exactly one of the known protocols',
579
- { addedIn: '2.7.0' },
580
- );
581
-
582
- const SOURCE_API_HTTP_BASE_URL_INVALID = makeCodeDefinition(
583
- 'SOURCE_API_HTTP_BASE_URL_INVALID',
584
- 'The `@sourceAPI` directive must specify a valid http.baseURL',
585
- { addedIn: '2.7.0' },
586
- );
587
-
588
- const SOURCE_HTTP_HEADERS_INVALID = makeCodeDefinition(
589
- 'SOURCE_HTTP_HEADERS_INVALID',
590
- 'The `http.headers` argument of `@source*` directives must specify valid HTTP headers',
591
- { addedIn: '2.7.0' },
592
- );
593
-
594
- const SOURCE_TYPE_API_ERROR = makeCodeDefinition(
595
- 'SOURCE_TYPE_API_ERROR',
596
- 'The `api` argument of the `@sourceType` directive must match a valid `@sourceAPI` name',
597
- { addedIn: '2.7.0' },
598
- );
599
-
600
- const SOURCE_TYPE_PROTOCOL_INVALID = makeCodeDefinition(
601
- 'SOURCE_TYPE_PROTOCOL_INVALID',
602
- 'The `@sourceType` directive must specify the same protocol as its corresponding `@sourceAPI`',
603
- { addedIn: '2.7.0' },
604
- );
605
-
606
- const SOURCE_TYPE_HTTP_METHOD_INVALID = makeCodeDefinition(
607
- 'SOURCE_TYPE_HTTP_METHOD_INVALID',
608
- 'The `@sourceType` directive must specify exactly one of `http.GET` or `http.POST`',
609
- { addedIn: '2.7.0' },
610
- );
611
-
612
- const SOURCE_TYPE_HTTP_PATH_INVALID = makeCodeDefinition(
613
- 'SOURCE_TYPE_HTTP_PATH_INVALID',
614
- 'The `@sourceType` directive must specify a valid URL template for `http.GET` or `http.POST`',
615
- { addedIn: '2.7.0' },
616
- );
617
-
618
- const SOURCE_TYPE_HTTP_BODY_INVALID = makeCodeDefinition(
619
- 'SOURCE_TYPE_HTTP_BODY_INVALID',
620
- 'If the `@sourceType` specifies `http.body`, it must be a valid `JSONSelection`',
621
- { addedIn: '2.7.0' },
622
- );
623
-
624
- const SOURCE_TYPE_ON_NON_OBJECT_OR_NON_ENTITY = makeCodeDefinition(
625
- 'SOURCE_TYPE_ON_NON_OBJECT_OR_NON_ENTITY',
626
- 'The `@sourceType` directive must be applied to an object or interface type that also has `@key`',
627
- { addedIn: '2.7.0' },
628
- );
629
-
630
- const SOURCE_TYPE_SELECTION_INVALID = makeCodeDefinition(
631
- 'SOURCE_TYPE_SELECTION_INVALID',
632
- 'The `selection` argument of the `@sourceType` directive must be a valid `JSONSelection` that outputs fields of the GraphQL type',
633
- );
634
-
635
- const SOURCE_FIELD_API_ERROR = makeCodeDefinition(
636
- 'SOURCE_FIELD_API_ERROR',
637
- 'The `api` argument of the `@sourceField` directive must match a valid `@sourceAPI` name',
638
- { addedIn: '2.7.0' },
639
- );
640
-
641
- const SOURCE_FIELD_PROTOCOL_INVALID = makeCodeDefinition(
642
- 'SOURCE_FIELD_PROTOCOL_INVALID',
643
- 'If `@sourceField` specifies a protocol, it must match the corresponding `@sourceAPI` protocol',
644
- { addedIn: '2.7.0' },
645
- );
646
-
647
- const SOURCE_FIELD_HTTP_METHOD_INVALID = makeCodeDefinition(
648
- 'SOURCE_FIELD_HTTP_METHOD_INVALID',
649
- 'The `@sourceField` directive must specify at most one of `http.{GET,POST,PUT,PATCH,DELETE}`',
650
- { addedIn: '2.7.0' },
651
- );
652
-
653
- const SOURCE_FIELD_HTTP_PATH_INVALID = makeCodeDefinition(
654
- 'SOURCE_FIELD_HTTP_PATH_INVALID',
655
- 'The `@sourceField` directive must specify a valid URL template for `http.{GET,POST,PUT,PATCH,DELETE}`',
656
- { addedIn: '2.7.0' },
657
- );
658
-
659
- const SOURCE_FIELD_HTTP_BODY_INVALID = makeCodeDefinition(
660
- 'SOURCE_FIELD_HTTP_BODY_INVALID',
661
- 'If `@sourceField` specifies http.body, it must be a valid `JSONSelection` matching available arguments and fields',
662
- { addedIn: '2.7.0' },
663
- );
664
-
665
- const SOURCE_FIELD_SELECTION_INVALID = makeCodeDefinition(
666
- 'SOURCE_FIELD_SELECTION_INVALID',
667
- 'The `selection` argument of the `@sourceField` directive must be a valid `JSONSelection` that outputs fields of the GraphQL type',
668
- { addedIn: '2.7.0' },
669
- );
670
-
671
- const SOURCE_FIELD_NOT_ON_ROOT_OR_ENTITY_FIELD = makeCodeDefinition(
672
- 'SOURCE_FIELD_NOT_ON_ROOT_OR_ENTITY_FIELD',
673
- 'The `@sourceField` directive must be applied to a field of the `Query` or `Mutation` types, or of an entity type',
674
- { addedIn: '2.7.0' },
675
- );
676
-
677
564
  export const ERROR_CATEGORIES = {
678
565
  DIRECTIVE_FIELDS_MISSING_EXTERNAL,
679
566
  DIRECTIVE_UNSUPPORTED_ON_INTERFACE,
@@ -762,26 +649,6 @@ export const ERRORS = {
762
649
  INTERFACE_OBJECT_USAGE_ERROR,
763
650
  INTERFACE_KEY_NOT_ON_IMPLEMENTATION,
764
651
  INTERFACE_KEY_MISSING_IMPLEMENTATION_TYPE,
765
- // Errors related to @sourceAPI, @sourceType, and/or @sourceField
766
- SOURCE_FEDERATION_VERSION_REQUIRED,
767
- SOURCE_API_NAME_INVALID,
768
- SOURCE_API_PROTOCOL_INVALID,
769
- SOURCE_API_HTTP_BASE_URL_INVALID,
770
- SOURCE_HTTP_HEADERS_INVALID,
771
- SOURCE_TYPE_API_ERROR,
772
- SOURCE_TYPE_PROTOCOL_INVALID,
773
- SOURCE_TYPE_HTTP_METHOD_INVALID,
774
- SOURCE_TYPE_HTTP_PATH_INVALID,
775
- SOURCE_TYPE_HTTP_BODY_INVALID,
776
- SOURCE_TYPE_ON_NON_OBJECT_OR_NON_ENTITY,
777
- SOURCE_TYPE_SELECTION_INVALID,
778
- SOURCE_FIELD_API_ERROR,
779
- SOURCE_FIELD_PROTOCOL_INVALID,
780
- SOURCE_FIELD_HTTP_METHOD_INVALID,
781
- SOURCE_FIELD_HTTP_PATH_INVALID,
782
- SOURCE_FIELD_HTTP_BODY_INVALID,
783
- SOURCE_FIELD_SELECTION_INVALID,
784
- SOURCE_FIELD_NOT_ON_ROOT_OR_ENTITY_FIELD,
785
652
  };
786
653
 
787
654
  const codeDefByCode = Object.values(ERRORS).reduce((obj: {[code: string]: ErrorCodeDefinition}, codeDef: ErrorCodeDefinition) => { obj[codeDef.code] = codeDef; return obj; }, {});
package/src/federation.ts CHANGED
@@ -39,15 +39,14 @@ import {
39
39
  KnownTypeNamesRule,
40
40
  PossibleTypeExtensionsRule,
41
41
  print as printAST,
42
- Source,
43
42
  GraphQLErrorOptions,
44
43
  SchemaDefinitionNode,
45
44
  OperationTypeNode,
46
45
  OperationTypeDefinitionNode,
47
- ConstDirectiveNode,
46
+ ConstDirectiveNode, parse,
48
47
  } from "graphql";
49
48
  import { KnownTypeNamesInFederationRule } from "./validation/KnownTypeNamesInFederationRule";
50
- import { buildSchema, buildSchemaFromAST } from "./buildSchema";
49
+ import { buildSchemaFromAST } from "./buildSchema";
51
50
  import { parseSelectionSet, SelectionSet } from './operations';
52
51
  import { TAG_VERSIONS } from "./specs/tagSpec";
53
52
  import {
@@ -84,13 +83,9 @@ import {
84
83
  import { defaultPrintOptions, PrintOptions as PrintOptions, printSchema } from "./print";
85
84
  import { createObjectTypeSpecification, createScalarTypeSpecification, createUnionTypeSpecification } from "./directiveAndTypeSpecification";
86
85
  import { didYouMean, suggestionList } from "./suggestions";
87
- import { coreFeatureDefinitionIfKnown, validateKnownFeatures } from "./knownCoreFeatures";
86
+ import { coreFeatureDefinitionIfKnown } from "./knownCoreFeatures";
88
87
  import { joinIdentity } from "./specs/joinSpec";
89
- import {
90
- SourceAPIDirectiveArgs,
91
- SourceFieldDirectiveArgs,
92
- SourceTypeDirectiveArgs,
93
- } from "./specs/sourceSpec";
88
+ import {validateSubgraphSchema} from "./wasm";
94
89
 
95
90
  const linkSpec = LINK_VERSIONS.latest();
96
91
  const tagSpec = TAG_VERSIONS.latest();
@@ -787,18 +782,6 @@ export class FederationMetadata {
787
782
  return this.getPost20FederationDirective(FederationDirectiveName.POLICY);
788
783
  }
789
784
 
790
- sourceAPIDirective(): Post20FederationDirectiveDefinition<SourceAPIDirectiveArgs> {
791
- return this.getPost20FederationDirective(FederationDirectiveName.SOURCE_API);
792
- }
793
-
794
- sourceTypeDirective(): Post20FederationDirectiveDefinition<SourceTypeDirectiveArgs> {
795
- return this.getPost20FederationDirective(FederationDirectiveName.SOURCE_TYPE);
796
- }
797
-
798
- sourceFieldDirective(): Post20FederationDirectiveDefinition<SourceFieldDirectiveArgs> {
799
- return this.getPost20FederationDirective(FederationDirectiveName.SOURCE_FIELD);
800
- }
801
-
802
785
  allFederationDirectives(): DirectiveDefinition[] {
803
786
  const baseDirectives: DirectiveDefinition[] = [
804
787
  this.keyDirective(),
@@ -839,19 +822,6 @@ export class FederationMetadata {
839
822
  baseDirectives.push(policyDirective);
840
823
  }
841
824
 
842
- const sourceAPIDirective = this.sourceAPIDirective();
843
- if (isFederationDirectiveDefinedInSchema(sourceAPIDirective)) {
844
- baseDirectives.push(sourceAPIDirective);
845
- }
846
- const sourceTypeDirective = this.sourceTypeDirective();
847
- if (isFederationDirectiveDefinedInSchema(sourceTypeDirective)) {
848
- baseDirectives.push(sourceTypeDirective);
849
- }
850
- const sourceFieldDirective = this.sourceFieldDirective();
851
- if (isFederationDirectiveDefinedInSchema(sourceFieldDirective)) {
852
- baseDirectives.push(sourceFieldDirective);
853
- }
854
-
855
825
  return baseDirectives;
856
826
  }
857
827
 
@@ -1089,11 +1059,6 @@ export class FederationBlueprint extends SchemaBlueprint {
1089
1059
  validateKeyOnInterfacesAreAlsoOnAllImplementations(metadata, errorCollector);
1090
1060
  validateInterfaceObjectsAreOnEntities(metadata, errorCollector);
1091
1061
 
1092
- // FeatureDefinition objects passed to registerKnownFeature can register
1093
- // validation functions for subgraph schemas by overriding the
1094
- // validateSubgraphSchema method.
1095
- validateKnownFeatures(schema, errorCollector);
1096
-
1097
1062
  // If tag is redefined by the user, make sure the definition is compatible with what we expect
1098
1063
  const tagDirective = metadata.tagDirective();
1099
1064
  if (tagDirective) {
@@ -1239,7 +1204,7 @@ export function setSchemaAsFed2Subgraph(schema: Schema, useLatest: boolean = fal
1239
1204
 
1240
1205
  // This is the full @link declaration as added by `asFed2SubgraphDocument`. It's here primarily for uses by tests that print and match
1241
1206
  // subgraph schema to avoid having to update 20+ tests every time we use a new directive or the order of import changes ...
1242
- export const FEDERATION2_LINK_WITH_FULL_IMPORTS = '@link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@key", "@requires", "@provides", "@external", "@tag", "@extends", "@shareable", "@inaccessible", "@override", "@composeDirective", "@interfaceObject", "@authenticated", "@requiresScopes", "@policy", "@sourceAPI", "@sourceType", "@sourceField"])';
1207
+ export const FEDERATION2_LINK_WITH_FULL_IMPORTS = '@link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@key", "@requires", "@provides", "@external", "@tag", "@extends", "@shareable", "@inaccessible", "@override", "@composeDirective", "@interfaceObject", "@authenticated", "@requiresScopes", "@policy"])';
1243
1208
 
1244
1209
  // This is the federation @link for tests that go through the asFed2SubgraphDocument function.
1245
1210
  export const FEDERATION2_LINK_WITH_AUTO_EXPANDED_IMPORTS = '@link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@key", "@requires", "@provides", "@external", "@tag", "@extends", "@shareable", "@inaccessible", "@override", "@composeDirective", "@interfaceObject"])';
@@ -1376,9 +1341,9 @@ export function buildSubgraph(
1376
1341
  };
1377
1342
  let subgraph: Subgraph;
1378
1343
  try {
1379
- const schema = typeof source === 'string'
1380
- ? buildSchema(new Source(source, name), buildOptions)
1381
- : buildSchemaFromAST(source, buildOptions)
1344
+ const parsed = typeof source === 'string' ? parse(source) : source;
1345
+ const schema = buildSchemaFromAST(parsed, buildOptions)
1346
+ validateSubgraphSchema(schema, parsed)
1382
1347
  subgraph = new Subgraph(name, url, schema);
1383
1348
  } catch (e) {
1384
1349
  if (e instanceof GraphQLError && name !== FEDERATION_UNNAMED_SUBGRAPH_NAME) {
package/src/index.ts CHANGED
@@ -23,4 +23,4 @@ export * from './argumentCompositionStrategies';
23
23
  export * from './specs/authenticatedSpec';
24
24
  export * from './specs/requiresScopesSpec';
25
25
  export * from './specs/policySpec';
26
- export * from './specs/sourceSpec';
26
+ export * from './specs/connectSpec';
@@ -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,181 @@
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 } 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({ name: URL_PATH_TEMPLATE, checkOrAdd: () => [] });
45
+ this.registerType({ name: JSON_SELECTION, checkOrAdd: () => [] });
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
+ ) repeatable on FIELD_DEFINITION
64
+ */
65
+ const connect = this.addDirective(schema, CONNECT).addLocations(DirectiveLocation.FIELD_DEFINITION);
66
+ connect.repeatable = true;
67
+
68
+ connect.addArgument(SOURCE, schema.stringType());
69
+
70
+ /*
71
+ input HTTPHeaderMapping {
72
+ name: String!
73
+ as: String
74
+ value: String
75
+ }
76
+ */
77
+ const HTTPHeaderMapping = schema.addType(new InputObjectType(this.typeNameInSchema(schema, HTTP_HEADER_MAPPING)!));
78
+ HTTPHeaderMapping.addField(new InputFieldDefinition('name')).type =
79
+ new NonNullType(schema.stringType());
80
+ HTTPHeaderMapping.addField(new InputFieldDefinition('as')).type =
81
+ schema.stringType();
82
+ HTTPHeaderMapping.addField(new InputFieldDefinition('value')).type =
83
+ schema.stringType();
84
+
85
+ /*
86
+ input ConnectHTTP {
87
+ GET: URLPathTemplate
88
+ POST: URLPathTemplate
89
+ PUT: URLPathTemplate
90
+ PATCH: URLPathTemplate
91
+ DELETE: URLPathTemplate
92
+ body: JSONSelection
93
+ headers: [HTTPHeaderMapping!]
94
+ }
95
+ */
96
+ const ConnectHTTP = schema.addType(new InputObjectType(this.typeNameInSchema(schema, CONNECT_HTTP)!));
97
+ ConnectHTTP.addField(new InputFieldDefinition('GET')).type = URLPathTemplate;
98
+ ConnectHTTP.addField(new InputFieldDefinition('POST')).type = URLPathTemplate;
99
+ ConnectHTTP.addField(new InputFieldDefinition('PUT')).type = URLPathTemplate;
100
+ ConnectHTTP.addField(new InputFieldDefinition('PATCH')).type = URLPathTemplate;
101
+ ConnectHTTP.addField(new InputFieldDefinition('DELETE')).type = URLPathTemplate;
102
+ ConnectHTTP.addField(new InputFieldDefinition('body')).type = JSONSelection;
103
+ ConnectHTTP.addField(new InputFieldDefinition('headers')).type =
104
+ new ListType(new NonNullType(HTTPHeaderMapping));
105
+ connect.addArgument('http', new NonNullType(ConnectHTTP));
106
+
107
+ connect.addArgument('selection', JSONSelection);
108
+
109
+ /*
110
+ directive @source(
111
+ name: String!
112
+ http: ConnectHTTP
113
+ ) repeatable on SCHEMA
114
+ */
115
+ const source = this.addDirective(schema, SOURCE).addLocations(
116
+ DirectiveLocation.SCHEMA,
117
+ );
118
+ source.repeatable = true;
119
+ source.addArgument('name', new NonNullType(schema.stringType()));
120
+
121
+ /*
122
+ input SourceHTTP {
123
+ baseURL: String!
124
+ headers: [HTTPHeaderMapping!]
125
+ }
126
+ */
127
+ const SourceHTTP = schema.addType(new InputObjectType(this.typeNameInSchema(schema, SOURCE_HTTP)!));
128
+ SourceHTTP.addField(new InputFieldDefinition('baseURL')).type =
129
+ new NonNullType(schema.stringType());
130
+ SourceHTTP.addField(new InputFieldDefinition('headers')).type =
131
+ new ListType(new NonNullType(HTTPHeaderMapping));
132
+
133
+ source.addArgument('http', new NonNullType(SourceHTTP));
134
+
135
+ return [];
136
+ }
137
+
138
+ get defaultCorePurpose(): CorePurpose {
139
+ return 'EXECUTION';
140
+ }
141
+ }
142
+
143
+ export type SourceDirectiveArgs = {
144
+ name: string;
145
+ http: SourceDirectiveHTTP;
146
+ };
147
+
148
+ export type SourceDirectiveHTTP = {
149
+ baseURL: string;
150
+ headers?: HTTPHeaderMapping[];
151
+ };
152
+
153
+ type HTTPHeaderMapping = {
154
+ name: string;
155
+ as?: string;
156
+ value?: string;
157
+ };
158
+
159
+ type URLPathTemplate = string;
160
+ type JSONSelection = string;
161
+
162
+ export type ConnectDirectiveArgs = {
163
+ source: string;
164
+ http: ConnectDirectiveHTTP;
165
+ selection?: JSONSelection;
166
+ };
167
+
168
+ export type ConnectDirectiveHTTP = {
169
+ GET?: URLPathTemplate;
170
+ POST?: URLPathTemplate;
171
+ PUT?: URLPathTemplate;
172
+ PATCH?: URLPathTemplate;
173
+ DELETE?: URLPathTemplate;
174
+ body?: JSONSelection;
175
+ headers?: HTTPHeaderMapping[];
176
+ };
177
+
178
+ export const CONNECT_VERSIONS = new FeatureDefinitions<ConnectSpecDefinition>(connectIdentity)
179
+ .add(new ConnectSpecDefinition(new FeatureVersion(0, 1), new FeatureVersion(2, 8)));
180
+
181
+ registerKnownFeature(CONNECT_VERSIONS);
@@ -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;
@@ -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
 
23
22
  export const federationIdentity = 'https://specs.apollo.dev/federation';
24
23
 
@@ -41,9 +40,6 @@ export enum FederationDirectiveName {
41
40
  AUTHENTICATED = 'authenticated',
42
41
  REQUIRES_SCOPES = 'requiresScopes',
43
42
  POLICY = 'policy',
44
- SOURCE_API = 'sourceAPI',
45
- SOURCE_TYPE = 'sourceType',
46
- SOURCE_FIELD = 'sourceField',
47
43
  }
48
44
 
49
45
  const fieldSetTypeSpec = createScalarTypeSpecification({ name: FederationTypeName.FIELD_SET });
@@ -170,10 +166,6 @@ export class FederationSpecDefinition extends FeatureDefinition {
170
166
  if (version.gte(new FeatureVersion(2, 6))) {
171
167
  this.registerSubFeature(POLICY_VERSIONS.find(new FeatureVersion(0, 1))!);
172
168
  }
173
-
174
- if (version.gte(new FeatureVersion(2, 7))) {
175
- this.registerSubFeature(SOURCE_VERSIONS.find(new FeatureVersion(0, 1))!);
176
- }
177
169
  }
178
170
  }
179
171
 
package/src/wasm.ts ADDED
@@ -0,0 +1,89 @@
1
+ /**
2
+ * The TypeScript interface to the `wasm` module (implemented in Rust) with all the helpers needed to convert between
3
+ * the two.
4
+ */
5
+
6
+ import {Schema} from "./definitions";
7
+ import {
8
+ ConstDirectiveNode,
9
+ DefinitionNode,
10
+ DocumentNode,
11
+ GraphQLError,
12
+ print,
13
+ } from "graphql/index";
14
+ import {FeatureUrl, LinkDirectiveArgs} from "./specs/coreSpec";
15
+ import {connectIdentity} from "./specs/connectSpec";
16
+ import {aggregateError} from "./error";
17
+ import {ErrorLocation, SourceDirective} from "../wasm/node";
18
+ import {ASTNode, ConstValueNode, SchemaExtensionNode, StringValueNode} from "graphql";
19
+
20
+ const {validate_connect_directives} =
21
+ require('../wasm/node') as typeof import('../wasm/node');
22
+
23
+ /**
24
+ * @throws AggregateGraphQLError
25
+ */
26
+ export function validateSubgraphSchema(schema: Schema, ast: DocumentNode) {
27
+ const errors = [];
28
+ if (hasConnectDirectives(schema)) {
29
+ errors.push(...validateConnectDirectives(ast));
30
+ }
31
+
32
+ if (errors.length > 0) {
33
+ throw aggregateError("FROM_WASM", "WASM validation detected errors", errors)
34
+ }
35
+ }
36
+
37
+ function hasConnectDirectives(schema: Schema): boolean {
38
+ return schema.schemaDefinition.appliedDirectivesOf<LinkDirectiveArgs>('link')
39
+ .some(linkDirective => {
40
+ const {url, import: imports} = linkDirective.arguments();
41
+ const featureUrl = FeatureUrl.maybeParse(url);
42
+ return imports && featureUrl && featureUrl.identity === connectIdentity && imports.some(nameOrRename => {
43
+ const originalName = typeof nameOrRename === 'string' ? nameOrRename : nameOrRename.name;
44
+ return originalName === '@source' || originalName === '@connect';
45
+ });
46
+ });
47
+ }
48
+
49
+ function validateConnectDirectives(ast: DocumentNode): GraphQLError[] {
50
+ // TODO: pass the _original_ source, not the one with added federation stuff (otherwise positions will be wrong)
51
+ // TODO: unless, of course, there's already infrastructure up above which does translation somehow
52
+ const source = print(ast)
53
+ return validate_connect_directives(source).map(
54
+ raw => new GraphQLError(raw.message, {
55
+ // source: new Source(source),
56
+ nodes: raw.location && findNode(ast, raw.location),
57
+ extensions: {code: raw.code},
58
+ }),
59
+ );
60
+ }
61
+
62
+
63
+ function findNode(ast: DocumentNode, location: ErrorLocation): ASTNode | undefined {
64
+ if (location.source) {
65
+ const sourceDirective = findSourceDirective(ast, location.source);
66
+ if (sourceDirective) {
67
+ return sourceDirective;
68
+ }
69
+ }
70
+ return undefined;
71
+ }
72
+
73
+ function findSourceDirective(ast: DocumentNode, sourceDirective: SourceDirective): ConstDirectiveNode | undefined {
74
+ const schema = ast.definitions.find(isSchemaExtension);
75
+ const sourceDirectives = schema?.directives?.
76
+ filter(directive => directive.name.value === "source");
77
+ return sourceDirectives?.find(directive => {
78
+ const nameArg = directive.arguments?.find(arg => arg.name.value === "name");
79
+ return nameArg && isStringValue(nameArg.value) && nameArg.value.value === sourceDirective.name;
80
+ });
81
+ }
82
+
83
+ function isSchemaExtension(node: DefinitionNode): node is SchemaExtensionNode {
84
+ return node.kind === "SchemaExtension";
85
+ }
86
+
87
+ function isStringValue(node: ConstValueNode): node is StringValueNode {
88
+ return node.kind === "StringValue";
89
+ }
@@ -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"}