@apollo/federation-internals 2.0.0-preview.7 → 2.0.0-preview.8

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 (63) hide show
  1. package/CHANGELOG.md +2 -4
  2. package/dist/buildSchema.d.ts.map +1 -1
  3. package/dist/buildSchema.js +50 -40
  4. package/dist/buildSchema.js.map +1 -1
  5. package/dist/coreSpec.d.ts +4 -2
  6. package/dist/coreSpec.d.ts.map +1 -1
  7. package/dist/coreSpec.js +107 -19
  8. package/dist/coreSpec.js.map +1 -1
  9. package/dist/definitions.d.ts +3 -0
  10. package/dist/definitions.d.ts.map +1 -1
  11. package/dist/definitions.js +72 -6
  12. package/dist/definitions.js.map +1 -1
  13. package/dist/error.d.ts +4 -0
  14. package/dist/error.d.ts.map +1 -1
  15. package/dist/error.js +27 -1
  16. package/dist/error.js.map +1 -1
  17. package/dist/federation.d.ts +4 -0
  18. package/dist/federation.d.ts.map +1 -1
  19. package/dist/federation.js +41 -11
  20. package/dist/federation.js.map +1 -1
  21. package/dist/federationSpec.d.ts +4 -0
  22. package/dist/federationSpec.d.ts.map +1 -1
  23. package/dist/federationSpec.js +19 -2
  24. package/dist/federationSpec.js.map +1 -1
  25. package/dist/inaccessibleSpec.d.ts +3 -0
  26. package/dist/inaccessibleSpec.d.ts.map +1 -1
  27. package/dist/inaccessibleSpec.js +22 -3
  28. package/dist/inaccessibleSpec.js.map +1 -1
  29. package/dist/index.d.ts +1 -0
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +1 -0
  32. package/dist/index.js.map +1 -1
  33. package/dist/joinSpec.d.ts +1 -0
  34. package/dist/joinSpec.d.ts.map +1 -1
  35. package/dist/joinSpec.js +18 -0
  36. package/dist/joinSpec.js.map +1 -1
  37. package/dist/knownCoreFeatures.d.ts +4 -0
  38. package/dist/knownCoreFeatures.d.ts.map +1 -0
  39. package/dist/knownCoreFeatures.js +16 -0
  40. package/dist/knownCoreFeatures.js.map +1 -0
  41. package/dist/tagSpec.d.ts +1 -0
  42. package/dist/tagSpec.d.ts.map +1 -1
  43. package/dist/tagSpec.js +5 -0
  44. package/dist/tagSpec.js.map +1 -1
  45. package/package.json +2 -2
  46. package/src/__tests__/coreSpec.test.ts +100 -0
  47. package/src/__tests__/definitions.test.ts +75 -0
  48. package/src/__tests__/removeInaccessibleElements.test.ts +1 -1
  49. package/src/__tests__/schemaUpgrader.test.ts +3 -2
  50. package/src/__tests__/subgraphValidation.test.ts +63 -4
  51. package/src/buildSchema.ts +97 -50
  52. package/src/coreSpec.ts +124 -21
  53. package/src/definitions.ts +99 -7
  54. package/src/error.ts +40 -0
  55. package/src/federation.ts +67 -12
  56. package/src/federationSpec.ts +26 -1
  57. package/src/inaccessibleSpec.ts +26 -10
  58. package/src/index.ts +1 -0
  59. package/src/joinSpec.ts +19 -0
  60. package/src/knownCoreFeatures.ts +13 -0
  61. package/src/tagSpec.ts +7 -0
  62. package/tsconfig.test.tsbuildinfo +1 -1
  63. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"knownCoreFeatures.d.ts","sourceRoot":"","sources":["../src/knownCoreFeatures.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAI/E,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,kBAAkB,QAInE;AAED,wBAAgB,4BAA4B,CAAC,GAAG,EAAE,UAAU,GAAG,iBAAiB,GAAG,SAAS,CAE3F"}
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.coreFeatureDefinitionIfKnown = exports.registerKnownFeature = void 0;
4
+ const registeredFeatures = new Map();
5
+ function registerKnownFeature(definitions) {
6
+ if (!registeredFeatures.has(definitions.identity)) {
7
+ registeredFeatures.set(definitions.identity, definitions);
8
+ }
9
+ }
10
+ exports.registerKnownFeature = registerKnownFeature;
11
+ function coreFeatureDefinitionIfKnown(url) {
12
+ var _a;
13
+ return (_a = registeredFeatures.get(url.identity)) === null || _a === void 0 ? void 0 : _a.find(url.version);
14
+ }
15
+ exports.coreFeatureDefinitionIfKnown = coreFeatureDefinitionIfKnown;
16
+ //# sourceMappingURL=knownCoreFeatures.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"knownCoreFeatures.js","sourceRoot":"","sources":["../src/knownCoreFeatures.ts"],"names":[],"mappings":";;;AAEA,MAAM,kBAAkB,GAAoC,IAAI,GAAG,EAAE,CAAC;AAEtE,SAAgB,oBAAoB,CAAC,WAA+B;IAClE,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE;QACjD,kBAAkB,CAAC,GAAG,CAAC,WAAW,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;KAC3D;AACH,CAAC;AAJD,oDAIC;AAED,SAAgB,4BAA4B,CAAC,GAAe;;IAC1D,OAAO,MAAA,kBAAkB,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,0CAAE,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AACjE,CAAC;AAFD,oEAEC"}
package/dist/tagSpec.d.ts CHANGED
@@ -10,6 +10,7 @@ export declare class TagSpecDefinition extends FeatureDefinition {
10
10
  name: string;
11
11
  }>;
12
12
  checkCompatibleDirective(definition: DirectiveDefinition): GraphQLError | undefined;
13
+ allElementNames(): string[];
13
14
  }
14
15
  export declare const TAG_VERSIONS: FeatureDefinitions<TagSpecDefinition>;
15
16
  //# sourceMappingURL=tagSpec.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tagSpec.d.ts","sourceRoot":"","sources":["../src/tagSpec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAc,cAAc,EAAE,MAAM,YAAY,CAAC;AAC/F,OAAO,EAAE,mBAAmB,EAAe,MAAM,EAAE,MAAM,eAAe,CAAC;AAIzE,eAAO,MAAM,WAAW,iCAAiC,CAAC;AAE1D,eAAO,MAAM,YAAY,qBAKxB,CAAC;AAIF,qBAAa,iBAAkB,SAAQ,iBAAiB;gBAC1C,OAAO,EAAE,cAAc;IAInC,mBAAmB,CAAC,MAAM,EAAE,MAAM;IAMlC,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,mBAAmB,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,CAAC;IAIjE,wBAAwB,CAAC,UAAU,EAAE,mBAAmB,GAAG,YAAY,GAAG,SAAS;CAapF;AAED,eAAO,MAAM,YAAY,uCAC8B,CAAC"}
1
+ {"version":3,"file":"tagSpec.d.ts","sourceRoot":"","sources":["../src/tagSpec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAc,cAAc,EAAE,MAAM,YAAY,CAAC;AAC/F,OAAO,EAAE,mBAAmB,EAAe,MAAM,EAAE,MAAM,eAAe,CAAC;AAKzE,eAAO,MAAM,WAAW,iCAAiC,CAAC;AAE1D,eAAO,MAAM,YAAY,qBAKxB,CAAC;AAIF,qBAAa,iBAAkB,SAAQ,iBAAiB;gBAC1C,OAAO,EAAE,cAAc;IAInC,mBAAmB,CAAC,MAAM,EAAE,MAAM;IAMlC,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,mBAAmB,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,CAAC;IAIjE,wBAAwB,CAAC,UAAU,EAAE,mBAAmB,GAAG,YAAY,GAAG,SAAS;IAcnF,eAAe,IAAI,MAAM,EAAE;CAG5B;AAED,eAAO,MAAM,YAAY,uCAC8B,CAAC"}
package/dist/tagSpec.js CHANGED
@@ -5,6 +5,7 @@ const graphql_1 = require("graphql");
5
5
  const coreSpec_1 = require("./coreSpec");
6
6
  const definitions_1 = require("./definitions");
7
7
  const error_1 = require("./error");
8
+ const knownCoreFeatures_1 = require("./knownCoreFeatures");
8
9
  const types_1 = require("./types");
9
10
  exports.tagIdentity = 'https://specs.apollo.dev/tag';
10
11
  exports.tagLocations = [
@@ -38,8 +39,12 @@ class TagSpecDefinition extends coreSpec_1.FeatureDefinition {
38
39
  }
39
40
  return undefined;
40
41
  }
42
+ allElementNames() {
43
+ return ["@tag"];
44
+ }
41
45
  }
42
46
  exports.TagSpecDefinition = TagSpecDefinition;
43
47
  exports.TAG_VERSIONS = new coreSpec_1.FeatureDefinitions(exports.tagIdentity)
44
48
  .add(new TagSpecDefinition(new coreSpec_1.FeatureVersion(0, 1)));
49
+ (0, knownCoreFeatures_1.registerKnownFeature)(exports.TAG_VERSIONS);
45
50
  //# sourceMappingURL=tagSpec.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tagSpec.js","sourceRoot":"","sources":["../src/tagSpec.ts"],"names":[],"mappings":";;;AAAA,qCAA0D;AAC1D,yCAA+F;AAC/F,+CAAyE;AACzE,mCAAiC;AACjC,mCAAmC;AAEtB,QAAA,WAAW,GAAG,8BAA8B,CAAC;AAE7C,QAAA,YAAY,GAAG;IAC1B,2BAAiB,CAAC,gBAAgB;IAClC,2BAAiB,CAAC,MAAM;IACxB,2BAAiB,CAAC,SAAS;IAC3B,2BAAiB,CAAC,KAAK;CACxB,CAAC;AAEF,MAAM,oBAAoB,GAAG,2FAA2F,CAAC;AAEzH,MAAa,iBAAkB,SAAQ,4BAAiB;IACtD,YAAY,OAAuB;QACjC,KAAK,CAAC,IAAI,qBAAU,CAAC,mBAAW,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;IACrD,CAAC;IAED,mBAAmB,CAAC,MAAc;QAChC,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,YAAY,CAAC,GAAG,oBAAY,CAAC,CAAC;QACjF,SAAS,CAAC,UAAU,GAAG,IAAI,CAAC;QAC5B,SAAS,CAAC,WAAW,CAAC,MAAM,EAAE,IAAI,yBAAW,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;IACtE,CAAC;IAED,YAAY,CAAC,MAAc;QACzB,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,KAAK,CAAE,CAAC;IACxC,CAAC;IAED,wBAAwB,CAAC,UAA+B;QACtD,MAAM,mBAAmB,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;QAC3E,MAAM,OAAO,GAAG,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC5C,MAAM,eAAe,GAAG,OAAO,IAAI,IAAA,gBAAQ,EAAC,OAAO,CAAC,IAAK,EAAE,IAAI,yBAAW,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;QAC9G,MAAM,iBAAiB,GAAG,UAAU,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,oBAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;QACxF,IAAI,mBAAmB,IAAI,CAAC,eAAe,IAAI,CAAC,iBAAiB,EAAE;YACjE,OAAO,cAAM,CAAC,4BAA4B,CAAC,GAAG,CAAC;gBAC7C,OAAO,EAAE,0IAA0I,oBAAoB,EAAE;aAC1K,CACA,CAAC;SACH;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;CACF;AA5BD,8CA4BC;AAEY,QAAA,YAAY,GAAG,IAAI,6BAAkB,CAAoB,mBAAW,CAAC;KAC/E,GAAG,CAAC,IAAI,iBAAiB,CAAC,IAAI,yBAAc,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"tagSpec.js","sourceRoot":"","sources":["../src/tagSpec.ts"],"names":[],"mappings":";;;AAAA,qCAA0D;AAC1D,yCAA+F;AAC/F,+CAAyE;AACzE,mCAAiC;AACjC,2DAA2D;AAC3D,mCAAmC;AAEtB,QAAA,WAAW,GAAG,8BAA8B,CAAC;AAE7C,QAAA,YAAY,GAAG;IAC1B,2BAAiB,CAAC,gBAAgB;IAClC,2BAAiB,CAAC,MAAM;IACxB,2BAAiB,CAAC,SAAS;IAC3B,2BAAiB,CAAC,KAAK;CACxB,CAAC;AAEF,MAAM,oBAAoB,GAAG,2FAA2F,CAAC;AAEzH,MAAa,iBAAkB,SAAQ,4BAAiB;IACtD,YAAY,OAAuB;QACjC,KAAK,CAAC,IAAI,qBAAU,CAAC,mBAAW,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;IACrD,CAAC;IAED,mBAAmB,CAAC,MAAc;QAChC,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,YAAY,CAAC,GAAG,oBAAY,CAAC,CAAC;QACjF,SAAS,CAAC,UAAU,GAAG,IAAI,CAAC;QAC5B,SAAS,CAAC,WAAW,CAAC,MAAM,EAAE,IAAI,yBAAW,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;IACtE,CAAC;IAED,YAAY,CAAC,MAAc;QACzB,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,KAAK,CAAE,CAAC;IACxC,CAAC;IAED,wBAAwB,CAAC,UAA+B;QACtD,MAAM,mBAAmB,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;QAC3E,MAAM,OAAO,GAAG,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC5C,MAAM,eAAe,GAAG,OAAO,IAAI,IAAA,gBAAQ,EAAC,OAAO,CAAC,IAAK,EAAE,IAAI,yBAAW,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;QAC9G,MAAM,iBAAiB,GAAG,UAAU,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,oBAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;QACxF,IAAI,mBAAmB,IAAI,CAAC,eAAe,IAAI,CAAC,iBAAiB,EAAE;YACjE,OAAO,cAAM,CAAC,4BAA4B,CAAC,GAAG,CAAC;gBAC7C,OAAO,EAAE,0IAA0I,oBAAoB,EAAE;aAC1K,CACA,CAAC;SACH;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,eAAe;QACb,OAAO,CAAC,MAAM,CAAC,CAAC;IAClB,CAAC;CACF;AAhCD,8CAgCC;AAEY,QAAA,YAAY,GAAG,IAAI,6BAAkB,CAAoB,mBAAW,CAAC;KAC/E,GAAG,CAAC,IAAI,iBAAiB,CAAC,IAAI,yBAAc,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AAExD,IAAA,wCAAoB,EAAC,oBAAY,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apollo/federation-internals",
3
- "version": "2.0.0-preview.7",
3
+ "version": "2.0.0-preview.8",
4
4
  "description": "Apollo Federation internal utilities",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -33,5 +33,5 @@
33
33
  "peerDependencies": {
34
34
  "graphql": "^16.0.0"
35
35
  },
36
- "gitHead": "8572831c6abf87749ba60dba161ae3ce7fb30ea8"
36
+ "gitHead": "516a30d879b4bde5945adb55b80d0f64118c322f"
37
37
  }
@@ -0,0 +1,100 @@
1
+ import { DocumentNode, GraphQLError } from "graphql";
2
+ import gql from "graphql-tag";
3
+ import { buildSubgraph } from "../federation";
4
+ import { errorCauses } from "../definitions";
5
+ import { assert } from "../utils";
6
+
7
+ function expectErrors(
8
+ subgraphDefs: DocumentNode,
9
+ expectedErrorMessages: string[],
10
+ ) {
11
+ let thrownError: Error | undefined = undefined;
12
+ expect(() => {
13
+ try {
14
+ // Note: we use buildSubgraph because currently it's the only one that does auto-magic import of
15
+ // directive definition, and we don't want to bother with adding the @link definition to every
16
+ // example.
17
+ buildSubgraph('S', '', subgraphDefs)
18
+ } catch (e) {
19
+ // Kind-of ugly, but if Jest has a better option, I haven't found it.
20
+ thrownError = e;
21
+ throw e;
22
+ }
23
+ }).toThrow(GraphQLError);
24
+
25
+ assert(thrownError, 'Should have thrown');
26
+ const causes = errorCauses(thrownError);
27
+ assert(causes, 'Should have some causes');
28
+ // Note: all the received message with start with "[S] <rest of message>", so the `slice` below
29
+ // strips the extra prefix. This avoid leaking the subgraph name to leak to the tests themselves.
30
+ expect(causes.map((e) => e.message.slice(4))).toStrictEqual(expectedErrorMessages);
31
+ }
32
+
33
+ describe('@link(import:) argument', () => {
34
+ test('errors on misformed values', () => {
35
+ const schema = gql`
36
+ extend schema @link(
37
+ url: "https://specs.apollo.dev/federation/v2.0",
38
+ import: [
39
+ 2,
40
+ { foo: "bar" },
41
+ { name: "@key", badName: "foo"},
42
+ { name: 42 },
43
+ { as: "bar" },
44
+ ]
45
+ )
46
+
47
+ type Query {
48
+ q: Int
49
+ }
50
+ `;
51
+
52
+ expectErrors(schema, [
53
+ 'Invalid sub-value 2 for @link(import:) argument: values should be either strings or input object values of the form { name: "<importedElement>", as: "<alias>" }.',
54
+ 'Unknown field "foo" for sub-value {foo: "bar"} of @link(import:) argument.',
55
+ 'Unknown field "badName" for sub-value {name: "@key", badName: "foo"} of @link(import:) argument.',
56
+ 'Invalid value for the "name" field for sub-value {name: 42} of @link(import:) argument: must be a string.',
57
+ 'Invalid sub-value {as: "bar"} for @link(import:) argument: missing mandatory "name" field.',
58
+ ]);
59
+ });
60
+
61
+ test('errors on mismatch between name and alias', () => {
62
+ const schema = gql`
63
+ extend schema @link(
64
+ url: "https://specs.apollo.dev/federation/v2.0",
65
+ import: [
66
+ { name: "@key", as: "myKey" },
67
+ { name: "FieldSet", as: "@fieldSet" },
68
+ ]
69
+ )
70
+
71
+ type Query {
72
+ q: Int
73
+ }
74
+ `;
75
+
76
+ expectErrors(schema, [
77
+ 'Invalid @link import renaming: directive "@key" imported name should start with a \'@\' character, but got "myKey".',
78
+ 'Invalid @link import renaming: type "FieldSet" imported name should not start with a \'@\' character, but got "@fieldSet" (or, if @FieldSet is a directive, then it should be referred to with a \'@\').',
79
+ ]);
80
+ });
81
+
82
+ test('errors on importing unknown elements for known features', () => {
83
+ const schema = gql`
84
+ extend schema @link(
85
+ url: "https://specs.apollo.dev/federation/v2.0",
86
+ import: [ "@foo", "key", { name: "@sharable" } ]
87
+ )
88
+
89
+ type Query {
90
+ q: Int
91
+ }
92
+ `;
93
+
94
+ expectErrors(schema, [
95
+ 'Cannot import unknown element "@foo".',
96
+ 'Cannot import unknown element "key". Did you mean directive "@key"?',
97
+ 'Cannot import unknown element "@sharable\". Did you mean "@shareable"?',
98
+ ]);
99
+ });
100
+ });
@@ -7,6 +7,7 @@ import {
7
7
  EnumType,
8
8
  SchemaElement,
9
9
  UnionType,
10
+ InputObjectType,
10
11
  } from '../../dist/definitions';
11
12
  import {
12
13
  printSchema as printGraphQLjsSchema
@@ -612,3 +613,77 @@ test('correctly convert to a graphQL-js schema', () => {
612
613
  const graphqQLSchema = schema.toGraphQLJSSchema();
613
614
  expect(printGraphQLjsSchema(graphqQLSchema)).toMatchString(sdl);
614
615
  });
616
+
617
+ test('retrieving elements by coordinate', () => {
618
+ const sdl = `
619
+ directive @foo(bar: Int) on FIELD
620
+
621
+ type Query {
622
+ t: T
623
+ }
624
+
625
+ type T {
626
+ f1(x: Int): String
627
+ f2: String
628
+ }
629
+
630
+ interface I {
631
+ i: String
632
+ }
633
+
634
+ input O {
635
+ x: Int
636
+ }
637
+
638
+ enum E {
639
+ FOO
640
+ BAR
641
+ }
642
+
643
+ scalar Date
644
+ `;
645
+ const schema = parseSchema(sdl);
646
+
647
+ expect(schema.elementByCoordinate('Query')).toBe(schema.schemaDefinition.rootType('query'));
648
+ expect(schema.elementByCoordinate('Query.t')).toBe(schema.schemaDefinition.rootType('query')?.field('t'));
649
+
650
+ const typeT = schema.type('T') as ObjectType;
651
+ expect(schema.elementByCoordinate('T')).toBe(typeT);
652
+ expect(schema.elementByCoordinate('T.f1')).toBe(typeT.field('f1'));
653
+ expect(schema.elementByCoordinate('T.f2')).toBe(typeT.field('f2'));
654
+ expect(schema.elementByCoordinate('T.f1(x:)')).toBe(typeT.field('f1')?.argument('x'));
655
+
656
+ const typeI = schema.type('I') as InterfaceType;
657
+ expect(schema.elementByCoordinate('I')).toBe(typeI);
658
+ expect(schema.elementByCoordinate('I.i')).toBe(typeI.field('i'));
659
+
660
+ const typeO = schema.type('O') as InputObjectType;
661
+ expect(schema.elementByCoordinate('O')).toBe(typeO);
662
+ expect(schema.elementByCoordinate('O.x')).toBe(typeO.field('x'));
663
+
664
+ const typeE = schema.type('E') as EnumType;
665
+ expect(schema.elementByCoordinate('E')).toBe(typeE);
666
+ expect(schema.elementByCoordinate('E.FOO')).toBe(typeE.value('FOO'));
667
+
668
+ expect(schema.elementByCoordinate('Date')).toBe(schema.type('Date'));
669
+
670
+ const directiveFoo = schema.directive('foo')!;
671
+ expect(schema.elementByCoordinate('@foo')).toBe(directiveFoo);
672
+ expect(schema.elementByCoordinate('@foo(bar:)')).toBe(directiveFoo.argument('bar'));
673
+
674
+ expect(schema.elementByCoordinate('SomeType')).toBeUndefined();
675
+ expect(schema.elementByCoordinate('T.f3')).toBeUndefined();
676
+ expect(schema.elementByCoordinate('T.f1(y:)')).toBeUndefined();
677
+ expect(schema.elementByCoordinate('I.j')).toBeUndefined();
678
+ expect(schema.elementByCoordinate('I.j(x:)')).toBeUndefined();
679
+ expect(schema.elementByCoordinate('O.z')).toBeUndefined();
680
+ expect(schema.elementByCoordinate('@bar')).toBeUndefined();
681
+ expect(schema.elementByCoordinate('@foo(unknown:)')).toBeUndefined();
682
+
683
+ expect(() => schema.elementByCoordinate('foo bar')).toThrow();
684
+ expect(() => schema.elementByCoordinate('@foo.bar')).toThrow();
685
+ // Note that because 'O' is an input type, it's field can't have args
686
+ expect(() => schema.elementByCoordinate('O.x(foo:)')).toThrow();
687
+ // Note that because 'Date' is a scalar, it cannot have fields
688
+ expect(() => schema.elementByCoordinate('Date.bar')).toThrow();
689
+ })
@@ -181,7 +181,7 @@ describe('removeInaccessibleElements', () => {
181
181
  expect(() => {
182
182
  removeInaccessibleElements(schema);
183
183
  }).toThrow(
184
- `Field Query.fooField returns an @inaccessible type without being marked @inaccessible itself`,
184
+ `Field "Query.fooField" returns @inaccessible type "Foo" without being marked @inaccessible itself`,
185
185
  );
186
186
  });
187
187
 
@@ -1,3 +1,4 @@
1
+ import { FEDERATION2_LINK_WTH_FULL_IMPORTS } from '..';
1
2
  import { buildSubgraph, Subgraphs } from '../federation';
2
3
  import { UpgradeChangeID, UpgradeResult, upgradeSubgraphsIfNecessary } from '../schemaUpgrader';
3
4
  import './matchers';
@@ -91,7 +92,7 @@ test('upgrade complex schema', () => {
91
92
  expect(res.subgraphs?.get('s1')?.toString()).toMatchString(`
92
93
  schema
93
94
  @link(url: "https://specs.apollo.dev/link/v1.0")
94
- @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key", "@requires", "@provides", "@external", "@shareable", "@tag", "@extends"])
95
+ ${FEDERATION2_LINK_WTH_FULL_IMPORTS}
95
96
  {
96
97
  query: Query
97
98
  }
@@ -148,7 +149,7 @@ test('update federation directive non-string arguments', () => {
148
149
  expect(res.subgraphs?.get('s')?.toString()).toMatchString(`
149
150
  schema
150
151
  @link(url: "https://specs.apollo.dev/link/v1.0")
151
- @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key", "@requires", "@provides", "@external", "@shareable", "@tag", "@extends"])
152
+ ${FEDERATION2_LINK_WTH_FULL_IMPORTS}
152
153
  {
153
154
  query: Query
154
155
  }
@@ -6,10 +6,17 @@ import { asFed2SubgraphDocument, buildSubgraph } from "../federation"
6
6
  // Builds the provided subgraph (using name 'S' for the subgraph) and, if the
7
7
  // subgraph is invalid/has errors, return those errors as a list of [code, message].
8
8
  // If the subgraph is valid, return undefined.
9
- function buildForErrors(subgraphDefs: DocumentNode, subgraphName: string = 'S'): [string, string][] | undefined {
9
+ function buildForErrors(
10
+ subgraphDefs: DocumentNode,
11
+ options?: {
12
+ subgraphName?: string,
13
+ asFed2?: boolean,
14
+ }
15
+ ): [string, string][] | undefined {
10
16
  try {
11
- const doc = asFed2SubgraphDocument(subgraphDefs);
12
- buildSubgraph(subgraphName, `http://${subgraphName}`, doc).validate();
17
+ const doc = (options?.asFed2 ?? true) ? asFed2SubgraphDocument(subgraphDefs) : subgraphDefs;
18
+ const name = options?.subgraphName ?? 'S';
19
+ buildSubgraph(name, `http://${name}`, doc).validate();
13
20
  return undefined;
14
21
  } catch (e) {
15
22
  const causes = errorCauses(e);
@@ -436,7 +443,6 @@ describe('root types', () => {
436
443
  });
437
444
  });
438
445
 
439
-
440
446
  it('validates all implementations of interface field have same type if any has @external', () => {
441
447
  const subgraph = gql`
442
448
  type Query {
@@ -464,3 +470,56 @@ it('validates all implementations of interface field have same type if any has @
464
470
  ['INTERFACE_FIELD_IMPLEM_TYPE_MISMATCH', '[S] Some of the runtime implementations of interface field "I.f" are marked @external or have a @require ("T3.f") so all the implementations should use the same type (a current limitation of federation; see https://github.com/apollographql/federation/issues/1257), but "T1.f" and "T3.f" have type "Int" while "T2.f" has type "Int!".'],
465
471
  ]);
466
472
  })
473
+
474
+ describe('custom error message for misnamed directives', () => {
475
+ it.each([
476
+ { name: 'fed1', extraMsg: ' If so, note that it is a federation 2 directive but this schema is a federation 1 one. To be a federation 2 schema, it needs to @link to the federation specifcation v2.' },
477
+ { name: 'fed2', extraMsg: '' },
478
+
479
+ ])('has suggestions if a federation directive name is mispelled in $name', ({name, extraMsg}) => {
480
+ const subgraph = gql`
481
+ type T @keys(fields: "id") {
482
+ id: Int @foo
483
+ foo: String @sharable
484
+ }
485
+ `;
486
+
487
+ expect(buildForErrors(subgraph, { asFed2 : name === 'fed2' })).toStrictEqual([
488
+ ['INVALID_GRAPHQL', `[S] Unknown directive "@foo".`],
489
+ ['INVALID_GRAPHQL', `[S] Unknown directive "@sharable". Did you mean "@shareable"?${extraMsg}`],
490
+ ['INVALID_GRAPHQL', `[S] Unknown directive "@keys". Did you mean "@key"?`],
491
+ ]);
492
+ });
493
+
494
+ it('has suggestions if a fed2 directive is used in fed1', () => {
495
+ const subgraph = gql`
496
+ type T @key(fields: "id") {
497
+ id: Int
498
+ foo: String @shareable
499
+ }
500
+ `;
501
+
502
+ expect(buildForErrors(subgraph, { asFed2 : false })).toStrictEqual([
503
+ ['INVALID_GRAPHQL', `[S] Unknown directive "@shareable". If you meant the \"@shareable\" federation 2 directive, note that this schema is a federation 1 schema. To be a federation 2 schema, it needs to @link to the federation specifcation v2.`],
504
+ ]);
505
+ });
506
+
507
+ it('has suggestions if a fed2 directive is used under the wrong name (for the schema)', () => {
508
+ const subgraph = gql`
509
+ extend schema
510
+ @link(url: "https://specs.apollo.dev/federation/v2.0",
511
+ import: [ { name: "@key", as: "@myKey"} ])
512
+
513
+ type T @key(fields: "id") {
514
+ id: Int
515
+ foo: String @shareable
516
+ }
517
+ `;
518
+
519
+ // Note: it's a fed2 schema, but we manually add the @link, so we pass `asFed2: false` to avoid having added twice.
520
+ expect(buildForErrors(subgraph, { asFed2 : false })).toStrictEqual([
521
+ ['INVALID_GRAPHQL', `[S] Unknown directive "@shareable". If you meant the \"@shareable\" federation directive, you should use fully-qualified name "@federation__shareable" or add "@shareable" to the \`import\` argument of the @link to the federation specification.`],
522
+ ['INVALID_GRAPHQL', `[S] Unknown directive "@key". If you meant the "@key" federation directive, you should use "@myKey" as it is imported under that name in the @link to the federation specification of this schema.`],
523
+ ]);
524
+ });
525
+ });