@apollo/federation-internals 2.7.2 → 2.7.3-testing.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.
@@ -0,0 +1,12 @@
1
+ import { GraphQLError } from 'graphql';
2
+ import { CorePurpose, FeatureDefinition, FeatureDefinitions, FeatureVersion } from "./coreSpec";
3
+ import { Schema } from '../definitions';
4
+ export declare const connectIdentity = "https://specs.apollo.dev/connect";
5
+ export declare class ConnectSpecDefinition extends FeatureDefinition {
6
+ readonly minimumFederationVersion: FeatureVersion;
7
+ constructor(version: FeatureVersion, minimumFederationVersion: FeatureVersion);
8
+ addElementsToSchema(schema: Schema): GraphQLError[];
9
+ get defaultCorePurpose(): CorePurpose;
10
+ }
11
+ export declare const CONNECT_VERSIONS: FeatureDefinitions<ConnectSpecDefinition>;
12
+ //# sourceMappingURL=connectSpec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connectSpec.d.ts","sourceRoot":"","sources":["../../src/specs/connectSpec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,YAAY,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,kBAAkB,EAAc,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5G,OAAO,EACL,MAAM,EAKP,MAAM,gBAAgB,CAAC;AAIxB,eAAO,MAAM,eAAe,qCAAqC,CAAC;AAUlE,qBAAa,qBAAsB,SAAQ,iBAAiB;IACrB,QAAQ,CAAC,wBAAwB,EAAE,cAAc;gBAA1E,OAAO,EAAE,cAAc,EAAW,wBAAwB,EAAE,cAAc;IA2BtF,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,EAAE;IAuFnD,IAAI,kBAAkB,IAAI,WAAW,CAEpC;CACF;AAED,eAAO,MAAM,gBAAgB,2CACwD,CAAC"}
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CONNECT_VERSIONS = exports.ConnectSpecDefinition = exports.connectIdentity = void 0;
4
+ const graphql_1 = require("graphql");
5
+ const coreSpec_1 = require("./coreSpec");
6
+ const definitions_1 = require("../definitions");
7
+ const knownCoreFeatures_1 = require("../knownCoreFeatures");
8
+ const directiveAndTypeSpecification_1 = require("../directiveAndTypeSpecification");
9
+ exports.connectIdentity = 'https://specs.apollo.dev/connect';
10
+ const CONNECT = "connect";
11
+ const SOURCE = "source";
12
+ const URL_PATH_TEMPLATE = "URLPathTemplate";
13
+ const JSON_SELECTION = "JSONSelection";
14
+ const CONNECT_HTTP = "ConnectHTTP";
15
+ const SOURCE_HTTP = "SourceHTTP";
16
+ const HTTP_HEADER_MAPPING = "HTTPHeaderMapping";
17
+ class ConnectSpecDefinition extends coreSpec_1.FeatureDefinition {
18
+ constructor(version, minimumFederationVersion) {
19
+ super(new coreSpec_1.FeatureUrl(exports.connectIdentity, 'connect', version), minimumFederationVersion);
20
+ this.minimumFederationVersion = minimumFederationVersion;
21
+ this.registerDirective((0, directiveAndTypeSpecification_1.createDirectiveSpecification)({
22
+ name: CONNECT,
23
+ locations: [graphql_1.DirectiveLocation.FIELD_DEFINITION],
24
+ repeatable: true,
25
+ composes: false,
26
+ }));
27
+ this.registerDirective((0, directiveAndTypeSpecification_1.createDirectiveSpecification)({
28
+ name: SOURCE,
29
+ locations: [graphql_1.DirectiveLocation.SCHEMA],
30
+ repeatable: true,
31
+ composes: false,
32
+ }));
33
+ this.registerType({ name: URL_PATH_TEMPLATE, checkOrAdd: () => [] });
34
+ this.registerType({ name: JSON_SELECTION, checkOrAdd: () => [] });
35
+ this.registerType({ name: CONNECT_HTTP, checkOrAdd: () => [] });
36
+ this.registerType({ name: SOURCE_HTTP, checkOrAdd: () => [] });
37
+ this.registerType({ name: HTTP_HEADER_MAPPING, checkOrAdd: () => [] });
38
+ }
39
+ addElementsToSchema(schema) {
40
+ const URLPathTemplate = this.addScalarType(schema, URL_PATH_TEMPLATE);
41
+ const JSONSelection = this.addScalarType(schema, JSON_SELECTION);
42
+ const connect = this.addDirective(schema, CONNECT).addLocations(graphql_1.DirectiveLocation.FIELD_DEFINITION);
43
+ connect.repeatable = true;
44
+ connect.addArgument(SOURCE, schema.stringType());
45
+ const HTTPHeaderMapping = schema.addType(new definitions_1.InputObjectType(this.typeNameInSchema(schema, HTTP_HEADER_MAPPING)));
46
+ HTTPHeaderMapping.addField(new definitions_1.InputFieldDefinition('name')).type =
47
+ new definitions_1.NonNullType(schema.stringType());
48
+ HTTPHeaderMapping.addField(new definitions_1.InputFieldDefinition('as')).type =
49
+ schema.stringType();
50
+ HTTPHeaderMapping.addField(new definitions_1.InputFieldDefinition('value')).type =
51
+ schema.stringType();
52
+ const ConnectHTTP = schema.addType(new definitions_1.InputObjectType(this.typeNameInSchema(schema, CONNECT_HTTP)));
53
+ ConnectHTTP.addField(new definitions_1.InputFieldDefinition('GET')).type = URLPathTemplate;
54
+ ConnectHTTP.addField(new definitions_1.InputFieldDefinition('POST')).type = URLPathTemplate;
55
+ ConnectHTTP.addField(new definitions_1.InputFieldDefinition('PUT')).type = URLPathTemplate;
56
+ ConnectHTTP.addField(new definitions_1.InputFieldDefinition('PATCH')).type = URLPathTemplate;
57
+ ConnectHTTP.addField(new definitions_1.InputFieldDefinition('DELETE')).type = URLPathTemplate;
58
+ ConnectHTTP.addField(new definitions_1.InputFieldDefinition('body')).type = JSONSelection;
59
+ ConnectHTTP.addField(new definitions_1.InputFieldDefinition('headers')).type =
60
+ new definitions_1.ListType(new definitions_1.NonNullType(HTTPHeaderMapping));
61
+ connect.addArgument('http', ConnectHTTP);
62
+ connect.addArgument('selection', JSONSelection);
63
+ const source = this.addDirective(schema, SOURCE).addLocations(graphql_1.DirectiveLocation.SCHEMA);
64
+ source.repeatable = true;
65
+ source.addArgument('name', new definitions_1.NonNullType(schema.stringType()));
66
+ const SourceHTTP = schema.addType(new definitions_1.InputObjectType(this.typeNameInSchema(schema, SOURCE_HTTP)));
67
+ SourceHTTP.addField(new definitions_1.InputFieldDefinition('baseURL')).type =
68
+ new definitions_1.NonNullType(schema.stringType());
69
+ SourceHTTP.addField(new definitions_1.InputFieldDefinition('headers')).type =
70
+ new definitions_1.ListType(new definitions_1.NonNullType(HTTPHeaderMapping));
71
+ source.addArgument('http', SourceHTTP);
72
+ return [];
73
+ }
74
+ get defaultCorePurpose() {
75
+ return 'EXECUTION';
76
+ }
77
+ }
78
+ exports.ConnectSpecDefinition = ConnectSpecDefinition;
79
+ exports.CONNECT_VERSIONS = new coreSpec_1.FeatureDefinitions(exports.connectIdentity)
80
+ .add(new ConnectSpecDefinition(new coreSpec_1.FeatureVersion(0, 1), new coreSpec_1.FeatureVersion(2, 8)));
81
+ (0, knownCoreFeatures_1.registerKnownFeature)(exports.CONNECT_VERSIONS);
82
+ //# sourceMappingURL=connectSpec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connectSpec.js","sourceRoot":"","sources":["../../src/specs/connectSpec.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apollo/federation-internals",
3
- "version": "2.7.2",
3
+ "version": "2.7.3-testing.0",
4
4
  "description": "Apollo Federation internal utilities",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -392,26 +392,11 @@ export class DirectiveTargetElement<T extends DirectiveTargetElement<T>> {
392
392
  }
393
393
 
394
394
  appliedDirectivesToDirectiveNodes() : ConstDirectiveNode[] | undefined {
395
- if (this.appliedDirectives.length == 0) {
396
- return undefined;
397
- }
398
-
399
- return this.appliedDirectives.map(directive => {
400
- return {
401
- kind: Kind.DIRECTIVE,
402
- name: {
403
- kind: Kind.NAME,
404
- value: directive.name,
405
- },
406
- arguments: directive.argumentsToAST()
407
- };
408
- });
395
+ return directivesToDirectiveNodes(this.appliedDirectives);
409
396
  }
410
397
 
411
398
  appliedDirectivesToString(): string {
412
- return this.appliedDirectives.length == 0
413
- ? ''
414
- : ' ' + this.appliedDirectives.join(' ');
399
+ return directivesToString(this.appliedDirectives);
415
400
  }
416
401
 
417
402
  collectVariablesInAppliedDirectives(collector: VariableCollector) {
@@ -3257,6 +3242,37 @@ export class Directive<
3257
3242
  }
3258
3243
  }
3259
3244
 
3245
+ /**
3246
+ * Formats a Directive array as a string (with a leading space, if present).
3247
+ */
3248
+ export function directivesToString(directives?: readonly Directive<any>[])
3249
+ : string
3250
+ {
3251
+ return (!directives || directives.length == 0)
3252
+ ? ''
3253
+ : ' ' + directives.join(' ');
3254
+ }
3255
+
3256
+ /**
3257
+ * Converts a Directive array into DirectiveNode array.
3258
+ */
3259
+ export function directivesToDirectiveNodes(directives?: readonly Directive<any>[])
3260
+ : ConstDirectiveNode[] | undefined
3261
+ {
3262
+ return (!directives || directives.length === 0)
3263
+ ? undefined
3264
+ : directives.map(directive => {
3265
+ return {
3266
+ kind: Kind.DIRECTIVE,
3267
+ name: {
3268
+ kind: Kind.NAME,
3269
+ value: directive.name,
3270
+ },
3271
+ arguments: directive.argumentsToAST()
3272
+ };
3273
+ });
3274
+ }
3275
+
3260
3276
  /**
3261
3277
  * Checks if 2 directive applications should be considered equal.
3262
3278
  *
package/src/index.ts CHANGED
@@ -24,3 +24,4 @@ export * from './specs/authenticatedSpec';
24
24
  export * from './specs/requiresScopesSpec';
25
25
  export * from './specs/policySpec';
26
26
  export * from './specs/sourceSpec';
27
+ export * from './specs/connectSpec';
package/src/operations.ts CHANGED
@@ -48,6 +48,8 @@ import {
48
48
  isObjectType,
49
49
  NamedType,
50
50
  isUnionType,
51
+ directivesToString,
52
+ directivesToDirectiveNodes,
51
53
  } from "./definitions";
52
54
  import { isInterfaceObjectType } from "./federation";
53
55
  import { ERRORS } from "./error";
@@ -877,7 +879,6 @@ function computeFragmentsToKeep(
877
879
  return toExpand.size === 0 ? fragments : fragments.filter((f) => !toExpand.has(f.name));
878
880
  }
879
881
 
880
- // TODO Operations can also have directives
881
882
  export class Operation {
882
883
  constructor(
883
884
  readonly schema: Schema,
@@ -885,7 +886,8 @@ export class Operation {
885
886
  readonly selectionSet: SelectionSet,
886
887
  readonly variableDefinitions: VariableDefinitions,
887
888
  readonly fragments?: NamedFragments,
888
- readonly name?: string) {
889
+ readonly name?: string,
890
+ readonly directives?: readonly Directive<any>[]) {
889
891
  }
890
892
 
891
893
  // Returns a copy of this operation with the provided updated selection set.
@@ -901,7 +903,8 @@ export class Operation {
901
903
  newSelectionSet,
902
904
  this.variableDefinitions,
903
905
  this.fragments,
904
- this.name
906
+ this.name,
907
+ this.directives
905
908
  );
906
909
  }
907
910
 
@@ -917,7 +920,8 @@ export class Operation {
917
920
  newSelectionSet,
918
921
  this.variableDefinitions,
919
922
  newFragments,
920
- this.name
923
+ this.name,
924
+ this.directives
921
925
  );
922
926
  }
923
927
 
@@ -982,6 +986,7 @@ export class Operation {
982
986
  this.variableDefinitions,
983
987
  fragments,
984
988
  this.name,
989
+ this.directives
985
990
  );
986
991
  }
987
992
 
@@ -1053,7 +1058,7 @@ export class Operation {
1053
1058
  }
1054
1059
 
1055
1060
  toString(expandFragments: boolean = false, prettyPrint: boolean = true): string {
1056
- return this.selectionSet.toOperationString(this.rootKind, this.variableDefinitions, this.fragments, this.name, expandFragments, prettyPrint);
1061
+ return this.selectionSet.toOperationString(this.rootKind, this.variableDefinitions, this.fragments, this.name, this.directives, expandFragments, prettyPrint);
1057
1062
  }
1058
1063
  }
1059
1064
 
@@ -1219,6 +1224,14 @@ export class NamedFragmentDefinition extends DirectiveTargetElement<NamedFragmen
1219
1224
  const expandedSelectionSet = this.expandedSelectionSet();
1220
1225
  const selectionSet = expandedSelectionSet.normalize({ parentType: type });
1221
1226
 
1227
+ if (!isObjectType(this.typeCondition)) {
1228
+ // When the type condition of the fragment is not an object type, the `FieldsInSetCanMerge` rule is more
1229
+ // restrictive and any fields can create conflicts. Thus, we have to use the full validator in this case.
1230
+ // (see https://github.com/graphql/graphql-spec/issues/1085 for details.)
1231
+ const validator = FieldsConflictValidator.build(expandedSelectionSet);
1232
+ return { selectionSet, validator };
1233
+ }
1234
+
1222
1235
  // Note that `trimmed` is the difference of 2 selections that may not have been normalized on the same parent type,
1223
1236
  // so in practice, it is possible that `trimmed` contains some of the selections that `selectionSet` contains, but
1224
1237
  // that they have been simplified in `selectionSet` in such a way that the `minus` call does not see it. However,
@@ -2077,6 +2090,7 @@ export class SelectionSet {
2077
2090
  variableDefinitions: VariableDefinitions,
2078
2091
  fragments: NamedFragments | undefined,
2079
2092
  operationName?: string,
2093
+ directives?: readonly Directive<any>[],
2080
2094
  expandFragments: boolean = false,
2081
2095
  prettyPrint: boolean = true
2082
2096
  ): string {
@@ -2090,7 +2104,8 @@ export class SelectionSet {
2090
2104
  const nameAndVariables = operationName
2091
2105
  ? " " + (operationName + (variableDefinitions.isEmpty() ? "" : variableDefinitions.toString()))
2092
2106
  : (variableDefinitions.isEmpty() ? "" : " " + variableDefinitions.toString());
2093
- return fragmentsDefinitions + rootKind + nameAndVariables + " " + this.toString(expandFragments, true, indent);
2107
+ const directives_str = directivesToString(directives);
2108
+ return fragmentsDefinitions + rootKind + nameAndVariables + directives_str + " " + this.toString(expandFragments, true, indent);
2094
2109
  }
2095
2110
 
2096
2111
  /**
@@ -2873,7 +2888,7 @@ class FieldsConflictValidator {
2873
2888
  continue;
2874
2889
  }
2875
2890
 
2876
- // We're basically checking [FieldInSetCanMerge](https://spec.graphql.org/draft/#FieldsInSetCanMerge()),
2891
+ // We're basically checking [FieldsInSetCanMerge](https://spec.graphql.org/draft/#FieldsInSetCanMerge()),
2877
2892
  // but from 2 set of fields (`thisFields` and `thatFields`) of the same response that we know individually
2878
2893
  // merge already.
2879
2894
  for (const [thisField, thisValidator] of thisFields.entries()) {
@@ -3562,7 +3577,7 @@ class FragmentSpreadSelection extends FragmentSelection {
3562
3577
 
3563
3578
  key(): string {
3564
3579
  if (!this.computedKey) {
3565
- this.computedKey = '...' + this.namedFragment.name + (this.spreadDirectives.length === 0 ? '' : ' ' + this.spreadDirectives.join(' '));
3580
+ this.computedKey = '...' + this.namedFragment.name + directivesToString(this.spreadDirectives);
3566
3581
  }
3567
3582
  return this.computedKey;
3568
3583
  }
@@ -3588,18 +3603,7 @@ class FragmentSpreadSelection extends FragmentSelection {
3588
3603
  }
3589
3604
 
3590
3605
  toSelectionNode(): FragmentSpreadNode {
3591
- const directiveNodes = this.spreadDirectives.length === 0
3592
- ? undefined
3593
- : this.spreadDirectives.map(directive => {
3594
- return {
3595
- kind: Kind.DIRECTIVE,
3596
- name: {
3597
- kind: Kind.NAME,
3598
- value: directive.name,
3599
- },
3600
- arguments: directive.argumentsToAST()
3601
- } as DirectiveNode;
3602
- });
3606
+ const directiveNodes = directivesToDirectiveNodes(this.spreadDirectives);
3603
3607
  return {
3604
3608
  kind: Kind.FRAGMENT_SPREAD,
3605
3609
  name: { kind: Kind.NAME, value: this.namedFragment.name },
@@ -3744,9 +3748,7 @@ class FragmentSpreadSelection extends FragmentSelection {
3744
3748
  if (expandFragments) {
3745
3749
  return (indent ?? '') + this.element + ' ' + this.selectionSet.toString(true, true, indent);
3746
3750
  } else {
3747
- const directives = this.spreadDirectives;
3748
- const directiveString = directives.length == 0 ? '' : ' ' + directives.join(' ');
3749
- return (indent ?? '') + '...' + this.namedFragment.name + directiveString;
3751
+ return (indent ?? '') + '...' + this.namedFragment.name + directivesToString(this.spreadDirectives);
3750
3752
  }
3751
3753
  }
3752
3754
  }
@@ -3832,6 +3834,7 @@ export function operationFromDocument(
3832
3834
  }
3833
3835
  ) : Operation {
3834
3836
  let operation: OperationDefinitionNode | undefined;
3837
+ let operation_directives: Directive<any>[] | undefined; // the directives on `operation`
3835
3838
  const operationName = options?.operationName;
3836
3839
  const fragments = new NamedFragments();
3837
3840
  // We do a first pass to collect the operation, and create all named fragment, but without their selection set yet.
@@ -3842,6 +3845,7 @@ export function operationFromDocument(
3842
3845
  validate(!operation || operationName, () => 'Must provide operation name if query contains multiple operations.');
3843
3846
  if (!operationName || (definition.name && definition.name.value === operationName)) {
3844
3847
  operation = definition;
3848
+ operation_directives = directivesOfNodes(schema, definition.directives);
3845
3849
  }
3846
3850
  break;
3847
3851
  case Kind.FRAGMENT_DEFINITION:
@@ -3875,18 +3879,20 @@ export function operationFromDocument(
3875
3879
  }
3876
3880
  });
3877
3881
  fragments.validate(variableDefinitions);
3878
- return operationFromAST({schema, operation, variableDefinitions, fragments, validateInput: options?.validate});
3882
+ return operationFromAST({schema, operation, operation_directives, variableDefinitions, fragments, validateInput: options?.validate});
3879
3883
  }
3880
3884
 
3881
3885
  function operationFromAST({
3882
3886
  schema,
3883
3887
  operation,
3888
+ operation_directives,
3884
3889
  variableDefinitions,
3885
3890
  fragments,
3886
3891
  validateInput,
3887
3892
  }:{
3888
3893
  schema: Schema,
3889
3894
  operation: OperationDefinitionNode,
3895
+ operation_directives?: Directive<any>[],
3890
3896
  variableDefinitions: VariableDefinitions,
3891
3897
  fragments: NamedFragments,
3892
3898
  validateInput?: boolean,
@@ -3906,7 +3912,8 @@ function operationFromAST({
3906
3912
  }),
3907
3913
  variableDefinitions,
3908
3914
  fragmentsIfAny,
3909
- operation.name?.value
3915
+ operation.name?.value,
3916
+ operation_directives
3910
3917
  );
3911
3918
  }
3912
3919
 
@@ -3961,6 +3968,7 @@ export function operationToDocument(operation: Operation): DocumentNode {
3961
3968
  name: operation.name ? { kind: Kind.NAME, value: operation.name } : undefined,
3962
3969
  selectionSet: operation.selectionSet.toSelectionSetNode(),
3963
3970
  variableDefinitions: operation.variableDefinitions.toVariableDefinitionNodes(),
3971
+ directives: directivesToDirectiveNodes(operation.directives),
3964
3972
  };
3965
3973
  const fragmentASTs: DefinitionNode[] = operation.fragments
3966
3974
  ? operation.fragments?.toFragmentDefinitionNodes()
@@ -0,0 +1,146 @@
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', 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', SourceHTTP);
134
+
135
+ return [];
136
+ }
137
+
138
+ get defaultCorePurpose(): CorePurpose {
139
+ return 'EXECUTION';
140
+ }
141
+ }
142
+
143
+ export const CONNECT_VERSIONS = new FeatureDefinitions<ConnectSpecDefinition>(connectIdentity)
144
+ .add(new ConnectSpecDefinition(new FeatureVersion(0, 1), new FeatureVersion(2, 8)));
145
+
146
+ registerKnownFeature(CONNECT_VERSIONS);