@apollo/federation-internals 2.0.0-preview.7 → 2.0.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 (109) hide show
  1. package/CHANGELOG.md +32 -3
  2. package/dist/buildSchema.d.ts.map +1 -1
  3. package/dist/buildSchema.js +51 -41
  4. package/dist/buildSchema.js.map +1 -1
  5. package/dist/coreSpec.d.ts +16 -8
  6. package/dist/coreSpec.d.ts.map +1 -1
  7. package/dist/coreSpec.js +205 -53
  8. package/dist/coreSpec.js.map +1 -1
  9. package/dist/definitions.d.ts +28 -11
  10. package/dist/definitions.d.ts.map +1 -1
  11. package/dist/definitions.js +185 -67
  12. package/dist/definitions.js.map +1 -1
  13. package/dist/directiveAndTypeSpecification.d.ts +11 -1
  14. package/dist/directiveAndTypeSpecification.d.ts.map +1 -1
  15. package/dist/directiveAndTypeSpecification.js +77 -20
  16. package/dist/directiveAndTypeSpecification.js.map +1 -1
  17. package/dist/error.d.ts +17 -0
  18. package/dist/error.d.ts.map +1 -1
  19. package/dist/error.js +54 -2
  20. package/dist/error.js.map +1 -1
  21. package/dist/extractSubgraphsFromSupergraph.d.ts.map +1 -1
  22. package/dist/extractSubgraphsFromSupergraph.js +7 -1
  23. package/dist/extractSubgraphsFromSupergraph.js.map +1 -1
  24. package/dist/federation.d.ts +22 -5
  25. package/dist/federation.d.ts.map +1 -1
  26. package/dist/federation.js +143 -86
  27. package/dist/federation.js.map +1 -1
  28. package/dist/federationSpec.d.ts +6 -2
  29. package/dist/federationSpec.d.ts.map +1 -1
  30. package/dist/federationSpec.js +47 -22
  31. package/dist/federationSpec.js.map +1 -1
  32. package/dist/inaccessibleSpec.d.ts +10 -2
  33. package/dist/inaccessibleSpec.d.ts.map +1 -1
  34. package/dist/inaccessibleSpec.js +634 -16
  35. package/dist/inaccessibleSpec.js.map +1 -1
  36. package/dist/index.d.ts +2 -0
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +2 -0
  39. package/dist/index.js.map +1 -1
  40. package/dist/introspection.d.ts.map +1 -1
  41. package/dist/introspection.js +8 -3
  42. package/dist/introspection.js.map +1 -1
  43. package/dist/joinSpec.d.ts +5 -1
  44. package/dist/joinSpec.d.ts.map +1 -1
  45. package/dist/joinSpec.js +21 -0
  46. package/dist/joinSpec.js.map +1 -1
  47. package/dist/knownCoreFeatures.d.ts +4 -0
  48. package/dist/knownCoreFeatures.d.ts.map +1 -0
  49. package/dist/knownCoreFeatures.js +16 -0
  50. package/dist/knownCoreFeatures.js.map +1 -0
  51. package/dist/operations.d.ts +1 -0
  52. package/dist/operations.d.ts.map +1 -1
  53. package/dist/operations.js +16 -1
  54. package/dist/operations.js.map +1 -1
  55. package/dist/{sharing.d.ts → precompute.d.ts} +1 -1
  56. package/dist/precompute.d.ts.map +1 -0
  57. package/dist/{sharing.js → precompute.js} +3 -3
  58. package/dist/precompute.js.map +1 -0
  59. package/dist/schemaUpgrader.d.ts.map +1 -1
  60. package/dist/schemaUpgrader.js +17 -7
  61. package/dist/schemaUpgrader.js.map +1 -1
  62. package/dist/suggestions.d.ts +1 -1
  63. package/dist/suggestions.d.ts.map +1 -1
  64. package/dist/suggestions.js.map +1 -1
  65. package/dist/supergraphs.d.ts.map +1 -1
  66. package/dist/supergraphs.js +2 -0
  67. package/dist/supergraphs.js.map +1 -1
  68. package/dist/tagSpec.d.ts +7 -2
  69. package/dist/tagSpec.d.ts.map +1 -1
  70. package/dist/tagSpec.js +35 -14
  71. package/dist/tagSpec.js.map +1 -1
  72. package/dist/validate.js +13 -7
  73. package/dist/validate.js.map +1 -1
  74. package/dist/values.d.ts +2 -2
  75. package/dist/values.d.ts.map +1 -1
  76. package/dist/values.js +13 -11
  77. package/dist/values.js.map +1 -1
  78. package/package.json +4 -4
  79. package/src/__tests__/coreSpec.test.ts +212 -0
  80. package/src/__tests__/definitions.test.ts +75 -0
  81. package/src/__tests__/removeInaccessibleElements.test.ts +2229 -137
  82. package/src/__tests__/schemaUpgrader.test.ts +3 -2
  83. package/src/__tests__/subgraphValidation.test.ts +419 -4
  84. package/src/__tests__/values.test.ts +315 -3
  85. package/src/buildSchema.ts +98 -51
  86. package/src/coreSpec.ts +277 -65
  87. package/src/definitions.ts +317 -92
  88. package/src/directiveAndTypeSpecification.ts +98 -21
  89. package/src/error.ts +119 -1
  90. package/src/extractSubgraphsFromSupergraph.ts +7 -1
  91. package/src/federation.ts +184 -102
  92. package/src/federationSpec.ts +56 -24
  93. package/src/inaccessibleSpec.ts +985 -39
  94. package/src/index.ts +2 -0
  95. package/src/introspection.ts +8 -3
  96. package/src/joinSpec.ts +33 -3
  97. package/src/knownCoreFeatures.ts +13 -0
  98. package/src/operations.ts +15 -0
  99. package/src/{sharing.ts → precompute.ts} +3 -6
  100. package/src/schemaUpgrader.ts +29 -13
  101. package/src/suggestions.ts +1 -1
  102. package/src/supergraphs.ts +2 -0
  103. package/src/tagSpec.ts +49 -16
  104. package/src/validate.ts +20 -9
  105. package/src/values.ts +39 -12
  106. package/tsconfig.test.tsbuildinfo +1 -1
  107. package/tsconfig.tsbuildinfo +1 -1
  108. package/dist/sharing.d.ts.map +0 -1
  109. package/dist/sharing.js.map +0 -1
@@ -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
  }
@@ -1,15 +1,25 @@
1
1
  import { DocumentNode } from 'graphql';
2
2
  import gql from 'graphql-tag';
3
+ import { Subgraph } from '..';
3
4
  import { errorCauses } from '../definitions';
4
5
  import { asFed2SubgraphDocument, buildSubgraph } from "../federation"
6
+ import { defaultPrintOptions, printSchema } from '../print';
7
+ import './matchers';
5
8
 
6
9
  // Builds the provided subgraph (using name 'S' for the subgraph) and, if the
7
10
  // subgraph is invalid/has errors, return those errors as a list of [code, message].
8
11
  // If the subgraph is valid, return undefined.
9
- function buildForErrors(subgraphDefs: DocumentNode, subgraphName: string = 'S'): [string, string][] | undefined {
12
+ export function buildForErrors(
13
+ subgraphDefs: DocumentNode,
14
+ options?: {
15
+ subgraphName?: string,
16
+ asFed2?: boolean,
17
+ }
18
+ ): [string, string][] | undefined {
10
19
  try {
11
- const doc = asFed2SubgraphDocument(subgraphDefs);
12
- buildSubgraph(subgraphName, `http://${subgraphName}`, doc).validate();
20
+ const doc = (options?.asFed2 ?? true) ? asFed2SubgraphDocument(subgraphDefs) : subgraphDefs;
21
+ const name = options?.subgraphName ?? 'S';
22
+ buildSubgraph(name, `http://${name}`, doc).validate();
13
23
  return undefined;
14
24
  } catch (e) {
15
25
  const causes = errorCauses(e);
@@ -436,7 +446,6 @@ describe('root types', () => {
436
446
  });
437
447
  });
438
448
 
439
-
440
449
  it('validates all implementations of interface field have same type if any has @external', () => {
441
450
  const subgraph = gql`
442
451
  type Query {
@@ -464,3 +473,409 @@ it('validates all implementations of interface field have same type if any has @
464
473
  ['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
474
  ]);
466
475
  })
476
+
477
+ describe('custom error message for misnamed directives', () => {
478
+ it.each([
479
+ { 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.' },
480
+ { name: 'fed2', extraMsg: '' },
481
+
482
+ ])('has suggestions if a federation directive name is mispelled in $name', ({name, extraMsg}) => {
483
+ const subgraph = gql`
484
+ type T @keys(fields: "id") {
485
+ id: Int @foo
486
+ foo: String @sharable
487
+ }
488
+ `;
489
+
490
+ expect(buildForErrors(subgraph, { asFed2 : name === 'fed2' })).toStrictEqual([
491
+ ['INVALID_GRAPHQL', `[S] Unknown directive "@foo".`],
492
+ ['INVALID_GRAPHQL', `[S] Unknown directive "@sharable". Did you mean "@shareable"?${extraMsg}`],
493
+ ['INVALID_GRAPHQL', `[S] Unknown directive "@keys". Did you mean "@key"?`],
494
+ ]);
495
+ });
496
+
497
+ it('has suggestions if a fed2 directive is used in fed1', () => {
498
+ const subgraph = gql`
499
+ type T @key(fields: "id") {
500
+ id: Int
501
+ foo: String @shareable
502
+ }
503
+ `;
504
+
505
+ expect(buildForErrors(subgraph, { asFed2 : false })).toStrictEqual([
506
+ ['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.`],
507
+ ]);
508
+ });
509
+
510
+ it('has suggestions if a fed2 directive is used under the wrong name (for the schema)', () => {
511
+ const subgraph = gql`
512
+ extend schema
513
+ @link(url: "https://specs.apollo.dev/federation/v2.0",
514
+ import: [ { name: "@key", as: "@myKey"} ])
515
+
516
+ type T @key(fields: "id") {
517
+ id: Int
518
+ foo: String @shareable
519
+ }
520
+ `;
521
+
522
+ // Note: it's a fed2 schema, but we manually add the @link, so we pass `asFed2: false` to avoid having added twice.
523
+ expect(buildForErrors(subgraph, { asFed2 : false })).toStrictEqual([
524
+ ['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.`],
525
+ ['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.`],
526
+ ]);
527
+ });
528
+ });
529
+
530
+ function buildAndValidate(doc: DocumentNode): Subgraph {
531
+ const name = 'S';
532
+ return buildSubgraph(name, `http://${name}`, doc).validate();
533
+ }
534
+
535
+ describe('@core/@link handling', () => {
536
+ const expectedFullSchema = `
537
+ schema
538
+ @link(url: "https://specs.apollo.dev/link/v1.0")
539
+ @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key"])
540
+ {
541
+ query: Query
542
+ }
543
+
544
+ directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA
545
+
546
+ directive @key(fields: federation__FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE
547
+
548
+ directive @federation__requires(fields: federation__FieldSet!) on FIELD_DEFINITION
549
+
550
+ directive @federation__provides(fields: federation__FieldSet!) on FIELD_DEFINITION
551
+
552
+ directive @federation__external(reason: String) on OBJECT | FIELD_DEFINITION
553
+
554
+ directive @federation__tag(name: String!) repeatable on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
555
+
556
+ directive @federation__extends on OBJECT | INTERFACE
557
+
558
+ directive @federation__shareable on OBJECT | FIELD_DEFINITION
559
+
560
+ directive @federation__inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
561
+
562
+ directive @federation__override(from: String!) on FIELD_DEFINITION
563
+
564
+ type T
565
+ @key(fields: "k")
566
+ {
567
+ k: ID!
568
+ }
569
+
570
+ enum link__Purpose {
571
+ """
572
+ \`SECURITY\` features provide metadata necessary to securely resolve fields.
573
+ """
574
+ SECURITY
575
+
576
+ """
577
+ \`EXECUTION\` features provide metadata necessary for operation execution.
578
+ """
579
+ EXECUTION
580
+ }
581
+
582
+ scalar link__Import
583
+
584
+ scalar federation__FieldSet
585
+
586
+ scalar _Any
587
+
588
+ type _Service {
589
+ sdl: String
590
+ }
591
+
592
+ union _Entity = T
593
+
594
+ type Query {
595
+ _entities(representations: [_Any!]!): [_Entity]!
596
+ _service: _Service!
597
+ }
598
+ `
599
+ const validateFullSchema = (subgraph: Subgraph) => {
600
+ // Note: we merge types and extensions to avoid having to care whether the @link are on a schema definition or schema extension
601
+ // as 1) this will vary (we add them to extensions in our test, but when auto-added, they are added to the schema definition)
602
+ // and 2) it doesn't matter in practice, it's valid in all cases.
603
+ expect(printSchema(subgraph.schema, { ...defaultPrintOptions, mergeTypesAndExtensions: true})).toMatchString(expectedFullSchema);
604
+ }
605
+
606
+ it('expands everything if only the federation spec is linked', () => {
607
+ const doc = gql`
608
+ extend schema
609
+ @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key"])
610
+
611
+ type T @key(fields: "k") {
612
+ k: ID!
613
+ }
614
+ `;
615
+
616
+ validateFullSchema(buildAndValidate(doc));
617
+ });
618
+
619
+ it('expands definitions if both the federation spec and link spec are linked', () => {
620
+ const doc = gql`
621
+ extend schema
622
+ @link(url: "https://specs.apollo.dev/link/v1.0")
623
+ @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key"])
624
+
625
+ type T @key(fields: "k") {
626
+ k: ID!
627
+ }
628
+ `;
629
+
630
+ validateFullSchema(buildAndValidate(doc));
631
+ });
632
+
633
+ it('is valid if a schema is complete from the get-go', () => {
634
+ validateFullSchema(buildAndValidate(gql(expectedFullSchema)));
635
+ });
636
+
637
+ it('expands missing definitions when some are partially provided', () => {
638
+ const docs = [
639
+ gql`
640
+ extend schema
641
+ @link(url: "https://specs.apollo.dev/link/v1.0")
642
+ @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key"])
643
+
644
+ type T @key(fields: "k") {
645
+ k: ID!
646
+ }
647
+
648
+ directive @key(fields: federation__FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE
649
+
650
+ scalar federation__FieldSet
651
+
652
+ scalar link__Import
653
+ `,
654
+ gql`
655
+ extend schema
656
+ @link(url: "https://specs.apollo.dev/link/v1.0")
657
+ @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key"])
658
+
659
+ type T @key(fields: "k") {
660
+ k: ID!
661
+ }
662
+
663
+ scalar link__Import
664
+ `,
665
+ gql`
666
+ extend schema
667
+ @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key"])
668
+
669
+ type T @key(fields: "k") {
670
+ k: ID!
671
+ }
672
+
673
+ scalar link__Import
674
+ `,
675
+ gql`
676
+ extend schema
677
+ @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key"])
678
+
679
+ type T @key(fields: "k") {
680
+ k: ID!
681
+ }
682
+
683
+ directive @federation__external(reason: String) on OBJECT | FIELD_DEFINITION
684
+ `,
685
+ ];
686
+
687
+ // Note that we cannot use `validateFullSchema` as-is for those examples because the order or directive is going
688
+ // to be different. But that's ok, we mostly care that the validation doesn't throw since validation ultimately
689
+ // calls the graphQL-js validation, so we can be somewhat sure that if something necessary wasn't expanded
690
+ // properly, we would have an issue. The main reason we did validate the full schema in prior tests is
691
+ // so we had at least one full example of a subgraph expansion in the tests.
692
+ docs.forEach((doc) => buildAndValidate(doc));
693
+ });
694
+
695
+ it('allows known directives with incomplete but compatible definitions', () => {
696
+ const docs = [
697
+ // @key has a `resolvable` argument in its full definition, but it is optional.
698
+ gql`
699
+ extend schema
700
+ @link(url: "https://specs.apollo.dev/link/v1.0")
701
+ @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key"])
702
+
703
+ type T @key(fields: "k") {
704
+ k: ID!
705
+ }
706
+
707
+ directive @key(fields: federation__FieldSet!) repeatable on OBJECT | INTERFACE
708
+
709
+ scalar federation__FieldSet
710
+ `,
711
+ // @inacessible can be put in a bunch of locations, but you're welcome to restrict yourself to just fields.
712
+ gql`
713
+ extend schema
714
+ @link(url: "https://specs.apollo.dev/link/v1.0")
715
+ @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@inaccessible"])
716
+
717
+ type T {
718
+ k: ID! @inaccessible
719
+ }
720
+
721
+ directive @inaccessible on FIELD_DEFINITION
722
+ `,
723
+ // @key is repeatable, but you're welcome to restrict yourself to never repeating it.
724
+ gql`
725
+ extend schema
726
+ @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key"])
727
+
728
+ type T @key(fields: "k") {
729
+ k: ID!
730
+ }
731
+
732
+ directive @key(fields: federation__FieldSet!, resolvable: Boolean = true) on OBJECT | INTERFACE
733
+
734
+ scalar federation__FieldSet
735
+ `,
736
+ // @key `resolvable` argument is optional, but you're welcome to force users to always provide it.
737
+ gql`
738
+ extend schema
739
+ @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key"])
740
+
741
+ type T @key(fields: "k", resolvable: true) {
742
+ k: ID!
743
+ }
744
+
745
+ directive @key(fields: federation__FieldSet!, resolvable: Boolean!) repeatable on OBJECT | INTERFACE
746
+
747
+ scalar federation__FieldSet
748
+ `,
749
+ // @link `url` argument is allowed to be `null` now, but it used not too, so making sure we still
750
+ // accept definition where it's mandatory.
751
+ gql`
752
+ extend schema
753
+ @link(url: "https://specs.apollo.dev/link/v1.0")
754
+ @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key"])
755
+
756
+ type T @key(fields: "k") {
757
+ k: ID!
758
+ }
759
+
760
+ directive @link(url: String!, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA
761
+
762
+ scalar link__Import
763
+ scalar link__Purpose
764
+ `,
765
+ ];
766
+
767
+ // Like above, we really only care that the examples validate.
768
+ docs.forEach((doc) => buildAndValidate(doc));
769
+ });
770
+
771
+ it('errors on invalid known directive location', () => {
772
+ const doc = gql`
773
+ extend schema
774
+ @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key"])
775
+
776
+ type T @key(fields: "k") {
777
+ k: ID!
778
+ }
779
+
780
+ directive @federation__external(reason: String) on OBJECT | FIELD_DEFINITION | SCHEMA
781
+ `;
782
+
783
+ // @external is not allowed on 'schema' and likely never will.
784
+ expect(buildForErrors(doc, { asFed2: false })).toStrictEqual([[
785
+ 'DIRECTIVE_DEFINITION_INVALID',
786
+ '[S] Invalid definition for directive "@federation__external": "@federation__external" should have locations OBJECT, FIELD_DEFINITION, but found (non-subset) OBJECT, FIELD_DEFINITION, SCHEMA',
787
+ ]]);
788
+ });
789
+
790
+ it('errors on invalid non-repeatable directive marked repeateable', () => {
791
+ const doc = gql`
792
+ extend schema
793
+ @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key"])
794
+
795
+ type T @key(fields: "k") {
796
+ k: ID!
797
+ }
798
+
799
+ directive @federation__external repeatable on OBJECT | FIELD_DEFINITION
800
+ `;
801
+
802
+ // @external is not repeatable (and has no reason to be since it has no arguments).
803
+ expect(buildForErrors(doc, { asFed2: false })).toStrictEqual([[
804
+ 'DIRECTIVE_DEFINITION_INVALID',
805
+ '[S] Invalid definition for directive "@federation__external": "@federation__external" should not be repeatable',
806
+ ]]);
807
+ });
808
+
809
+ it('errors on unknown argument of known directive', () => {
810
+ const doc = gql`
811
+ extend schema
812
+ @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key"])
813
+
814
+ type T @key(fields: "k") {
815
+ k: ID!
816
+ }
817
+
818
+ directive @federation__external(foo: Int) on OBJECT | FIELD_DEFINITION
819
+ `;
820
+
821
+ expect(buildForErrors(doc, { asFed2: false })).toStrictEqual([[
822
+ 'DIRECTIVE_DEFINITION_INVALID',
823
+ '[S] Invalid definition for directive "@federation__external": unknown/unsupported argument "foo"',
824
+ ]]);
825
+ });
826
+
827
+ it('errors on invalid type for a known argument', () => {
828
+ const doc = gql`
829
+ extend schema
830
+ @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key"])
831
+
832
+ type T @key(fields: "k") {
833
+ k: ID!
834
+ }
835
+
836
+ directive @key(fields: String!, resolvable: String) repeatable on OBJECT | INTERFACE
837
+ `;
838
+
839
+ expect(buildForErrors(doc, { asFed2: false })).toStrictEqual([[
840
+ 'DIRECTIVE_DEFINITION_INVALID',
841
+ '[S] Invalid definition for directive "@key": argument "resolvable" should have type "Boolean" but found type "String"',
842
+ ]]);
843
+ });
844
+
845
+ it('errors on a required argument defined as optional', () => {
846
+ const doc = gql`
847
+ extend schema
848
+ @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key"])
849
+
850
+ type T @key(fields: "k") {
851
+ k: ID!
852
+ }
853
+
854
+ directive @key(fields: federation__FieldSet, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE
855
+
856
+ scalar federation__FieldSet
857
+ `;
858
+
859
+ expect(buildForErrors(doc, { asFed2: false })).toStrictEqual([[
860
+ 'DIRECTIVE_DEFINITION_INVALID',
861
+ '[S] Invalid definition for directive "@key": argument "fields" should have type "federation__FieldSet!" but found type "federation__FieldSet"',
862
+ ]]);
863
+ });
864
+
865
+ it('allows any (non-scalar) type in redefinition when expected type is a scalar', () => {
866
+ const doc = gql`
867
+ extend schema
868
+ @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key"])
869
+
870
+ type T @key(fields: "k") {
871
+ k: ID!
872
+ }
873
+
874
+ # 'fields' should be of type 'federation_FieldSet!', but ensure we allow 'String!' alternatively.
875
+ directive @key(fields: String!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE
876
+ `;
877
+
878
+ // Just making sure this don't error out.
879
+ buildAndValidate(doc);
880
+ });
881
+ });