@apollo/federation-internals 2.4.1 → 2.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/dist/argumentCompositionStrategies.d.ts +34 -0
- package/dist/argumentCompositionStrategies.d.ts.map +1 -0
- package/dist/argumentCompositionStrategies.js +35 -0
- package/dist/argumentCompositionStrategies.js.map +1 -0
- package/dist/coreSpec.d.ts +12 -3
- package/dist/coreSpec.d.ts.map +1 -1
- package/dist/coreSpec.js +68 -17
- package/dist/coreSpec.js.map +1 -1
- package/dist/definitions.d.ts +1 -0
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js +30 -27
- package/dist/definitions.js.map +1 -1
- package/dist/directiveAndTypeSpecification.d.ts +26 -7
- package/dist/directiveAndTypeSpecification.d.ts.map +1 -1
- package/dist/directiveAndTypeSpecification.js +56 -4
- package/dist/directiveAndTypeSpecification.js.map +1 -1
- package/dist/federation.d.ts.map +1 -1
- package/dist/federation.js +24 -2
- package/dist/federation.js.map +1 -1
- package/dist/federationSpec.d.ts +2 -13
- package/dist/federationSpec.d.ts.map +1 -1
- package/dist/federationSpec.js +10 -60
- package/dist/federationSpec.js.map +1 -1
- package/dist/inaccessibleSpec.d.ts +0 -2
- package/dist/inaccessibleSpec.d.ts.map +1 -1
- package/dist/inaccessibleSpec.js +3 -6
- package/dist/inaccessibleSpec.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/knownCoreFeatures.d.ts +1 -0
- package/dist/knownCoreFeatures.d.ts.map +1 -1
- package/dist/knownCoreFeatures.js +5 -1
- package/dist/knownCoreFeatures.js.map +1 -1
- package/dist/operations.d.ts +18 -6
- package/dist/operations.d.ts.map +1 -1
- package/dist/operations.js +102 -37
- package/dist/operations.js.map +1 -1
- package/dist/print.d.ts +7 -1
- package/dist/print.d.ts.map +1 -1
- package/dist/print.js +33 -5
- package/dist/print.js.map +1 -1
- package/dist/tagSpec.d.ts +0 -2
- package/dist/tagSpec.d.ts.map +1 -1
- package/dist/tagSpec.js +4 -10
- package/dist/tagSpec.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/directiveAndTypeSpecifications.test.ts +41 -0
- package/src/__tests__/operations.test.ts +175 -10
- package/src/argumentCompositionStrategies.ts +39 -0
- package/src/coreSpec.ts +94 -34
- package/src/definitions.ts +35 -29
- package/src/directiveAndTypeSpecification.ts +101 -14
- package/src/federation.ts +33 -4
- package/src/federationSpec.ts +13 -73
- package/src/inaccessibleSpec.ts +4 -11
- package/src/index.ts +3 -0
- package/src/knownCoreFeatures.ts +9 -0
- package/src/operations.ts +198 -40
- package/src/print.ts +39 -4
- package/src/tagSpec.ts +4 -12
- package/tsconfig.tsbuildinfo +1 -1
package/dist/tagSpec.js
CHANGED
|
@@ -31,11 +31,11 @@ class TagSpecDefinition extends coreSpec_1.FeatureDefinition {
|
|
|
31
31
|
name: 'tag',
|
|
32
32
|
locations: this.tagLocations,
|
|
33
33
|
repeatable: true,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}),
|
|
34
|
+
args: [{ name: 'name', type: (schema) => new definitions_1.NonNullType(schema.stringType()) }],
|
|
35
|
+
composes: true,
|
|
36
|
+
supergraphSpecification: () => exports.TAG_VERSIONS.latest(),
|
|
38
37
|
});
|
|
38
|
+
this.registerDirective(this.tagDirectiveSpec);
|
|
39
39
|
}
|
|
40
40
|
isV01() {
|
|
41
41
|
return this.version.equals(new coreSpec_1.FeatureVersion(0, 1));
|
|
@@ -43,9 +43,6 @@ class TagSpecDefinition extends coreSpec_1.FeatureDefinition {
|
|
|
43
43
|
isV02() {
|
|
44
44
|
return this.version.equals(new coreSpec_1.FeatureVersion(0, 2));
|
|
45
45
|
}
|
|
46
|
-
addElementsToSchema(schema) {
|
|
47
|
-
return this.addDirectiveSpec(schema, this.tagDirectiveSpec);
|
|
48
|
-
}
|
|
49
46
|
tagDirective(schema) {
|
|
50
47
|
return this.directive(schema, 'tag');
|
|
51
48
|
}
|
|
@@ -59,9 +56,6 @@ class TagSpecDefinition extends coreSpec_1.FeatureDefinition {
|
|
|
59
56
|
}
|
|
60
57
|
return undefined;
|
|
61
58
|
}
|
|
62
|
-
allElementNames() {
|
|
63
|
-
return ["@tag"];
|
|
64
|
-
}
|
|
65
59
|
}
|
|
66
60
|
exports.TagSpecDefinition = TagSpecDefinition;
|
|
67
61
|
exports.TAG_VERSIONS = new coreSpec_1.FeatureDefinitions(exports.tagIdentity)
|
package/dist/tagSpec.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tagSpec.js","sourceRoot":"","sources":["../src/tagSpec.ts"],"names":[],"mappings":";;;AAAA,qCAA0D;AAC1D,yCAA+F;AAC/F,+CAAyE;AACzE,mFAAuG;AACvG,mCAAiC;AACjC,2DAA2D;AAC3D,mCAAmC;AAEtB,QAAA,WAAW,GAAG,8BAA8B,CAAC;AAE1D,MAAa,iBAAkB,SAAQ,4BAAiB;IAKtD,YAAY,OAAuB;QACjC,KAAK,CAAC,IAAI,qBAAU,CAAC,mBAAW,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;QACnD,IAAI,CAAC,YAAY,GAAG;YAClB,2BAAiB,CAAC,gBAAgB;YAClC,2BAAiB,CAAC,MAAM;YACxB,2BAAiB,CAAC,SAAS;YAC3B,2BAAiB,CAAC,KAAK;SACxB,CAAC;QACF,IAAI,CAAC,oBAAoB,GAAG,2FAA2F,CAAC;QACxH,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE;YACjB,IAAI,CAAC,YAAY,CAAC,IAAI,CACpB,2BAAiB,CAAC,mBAAmB,EACrC,2BAAiB,CAAC,MAAM,EACxB,2BAAiB,CAAC,IAAI,EACtB,2BAAiB,CAAC,UAAU,EAC5B,2BAAiB,CAAC,YAAY,EAC9B,2BAAiB,CAAC,sBAAsB,CACzC,CAAC;YACF,IAAI,CAAC,oBAAoB,GAAG,sLAAsL,CAAC;YACnN,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE;gBACjB,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,2BAAiB,CAAC,MAAM,CAAC,CAAC;gBACjD,IAAI,CAAC,oBAAoB,GAAG,+LAA+L,CAAC;aAC7N;SACF;QACD,IAAI,CAAC,gBAAgB,GAAG,IAAA,4DAA4B,EAAC;YACnD,IAAI,EAAC,KAAK;YACV,SAAS,EAAE,IAAI,CAAC,YAAY;YAC5B,UAAU,EAAE,IAAI;YAChB,
|
|
1
|
+
{"version":3,"file":"tagSpec.js","sourceRoot":"","sources":["../src/tagSpec.ts"],"names":[],"mappings":";;;AAAA,qCAA0D;AAC1D,yCAA+F;AAC/F,+CAAyE;AACzE,mFAAuG;AACvG,mCAAiC;AACjC,2DAA2D;AAC3D,mCAAmC;AAEtB,QAAA,WAAW,GAAG,8BAA8B,CAAC;AAE1D,MAAa,iBAAkB,SAAQ,4BAAiB;IAKtD,YAAY,OAAuB;QACjC,KAAK,CAAC,IAAI,qBAAU,CAAC,mBAAW,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;QACnD,IAAI,CAAC,YAAY,GAAG;YAClB,2BAAiB,CAAC,gBAAgB;YAClC,2BAAiB,CAAC,MAAM;YACxB,2BAAiB,CAAC,SAAS;YAC3B,2BAAiB,CAAC,KAAK;SACxB,CAAC;QACF,IAAI,CAAC,oBAAoB,GAAG,2FAA2F,CAAC;QACxH,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE;YACjB,IAAI,CAAC,YAAY,CAAC,IAAI,CACpB,2BAAiB,CAAC,mBAAmB,EACrC,2BAAiB,CAAC,MAAM,EACxB,2BAAiB,CAAC,IAAI,EACtB,2BAAiB,CAAC,UAAU,EAC5B,2BAAiB,CAAC,YAAY,EAC9B,2BAAiB,CAAC,sBAAsB,CACzC,CAAC;YACF,IAAI,CAAC,oBAAoB,GAAG,sLAAsL,CAAC;YACnN,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE;gBACjB,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,2BAAiB,CAAC,MAAM,CAAC,CAAC;gBACjD,IAAI,CAAC,oBAAoB,GAAG,+LAA+L,CAAC;aAC7N;SACF;QACD,IAAI,CAAC,gBAAgB,GAAG,IAAA,4DAA4B,EAAC;YACnD,IAAI,EAAC,KAAK;YACV,SAAS,EAAE,IAAI,CAAC,YAAY;YAC5B,UAAU,EAAE,IAAI;YAChB,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,yBAAW,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,EAAE,CAAC;YAChF,QAAQ,EAAE,IAAI;YACd,uBAAuB,EAAE,GAAG,EAAE,CAAC,oBAAY,CAAC,MAAM,EAAE;SACrD,CAAC,CAAC;QACH,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAChD,CAAC;IAEO,KAAK;QACX,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,yBAAc,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACvD,CAAC;IAEO,KAAK;QACX,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,yBAAc,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;IACtD,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,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7F,IAAI,mBAAmB,IAAI,CAAC,eAAe,IAAI,CAAC,iBAAiB,EAAE;YACjE,OAAO,cAAM,CAAC,4BAA4B,CAAC,GAAG,CAC5C,0IAA0I,IAAI,CAAC,oBAAoB,EAAE,CACtK,CAAC;SACH;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;CACF;AAhED,8CAgEC;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;KACpD,GAAG,CAAC,IAAI,iBAAiB,CAAC,IAAI,yBAAc,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;KACpD,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
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { DirectiveLocation } from "graphql";
|
|
2
|
+
import "../definitions";
|
|
3
|
+
import { createDirectiveSpecification } from "../directiveAndTypeSpecification";
|
|
4
|
+
import { ARGUMENT_COMPOSITION_STRATEGIES } from "../argumentCompositionStrategies";
|
|
5
|
+
import { TAG_VERSIONS } from "../tagSpec";
|
|
6
|
+
|
|
7
|
+
const supergraphSpecification = () => TAG_VERSIONS.latest();
|
|
8
|
+
|
|
9
|
+
test('must have supergraph link if composed', () => {
|
|
10
|
+
expect(() => createDirectiveSpecification({
|
|
11
|
+
name: 'foo',
|
|
12
|
+
locations: [DirectiveLocation.OBJECT],
|
|
13
|
+
composes: true,
|
|
14
|
+
})).toThrow('Should provide a @link specification to use in supergraph for directive @foo if it composes');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test('must have a merge strategy on all arguments if any', () => {
|
|
18
|
+
expect(() => createDirectiveSpecification({
|
|
19
|
+
name: 'foo',
|
|
20
|
+
locations: [DirectiveLocation.OBJECT],
|
|
21
|
+
composes: true,
|
|
22
|
+
supergraphSpecification,
|
|
23
|
+
args: [
|
|
24
|
+
{ name: "v1", type: (schema) => schema.intType(), compositionStrategy: ARGUMENT_COMPOSITION_STRATEGIES.MAX },
|
|
25
|
+
{ name: "v2", type: (schema) => schema.intType() }
|
|
26
|
+
],
|
|
27
|
+
})).toThrow('Invalid directive specification for @foo: not all arguments define a composition strategy');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('must be not be repeatable if it has a merge strategy', () => {
|
|
31
|
+
expect(() => createDirectiveSpecification({
|
|
32
|
+
name: 'foo',
|
|
33
|
+
locations: [DirectiveLocation.OBJECT],
|
|
34
|
+
composes: true,
|
|
35
|
+
repeatable: true,
|
|
36
|
+
supergraphSpecification,
|
|
37
|
+
args: [
|
|
38
|
+
{ name: "v", type: (schema) => schema.intType(), compositionStrategy: ARGUMENT_COMPOSITION_STRATEGIES.MAX },
|
|
39
|
+
],
|
|
40
|
+
})).toThrow('Invalid directive specification for @foo: @foo is repeatable and should not define composition strategy for its arguments');
|
|
41
|
+
});
|
|
@@ -154,8 +154,6 @@ describe('fragments optimization', () => {
|
|
|
154
154
|
`);
|
|
155
155
|
|
|
156
156
|
const optimized = withoutFragments.optimize(operation.selectionSet.fragments!);
|
|
157
|
-
// Note that while we didn't use `onU` for `t` in the query, it's technically ok to use
|
|
158
|
-
// it and it makes the query smaller, so it gets used.
|
|
159
157
|
expect(optimized.toString()).toMatchString(`
|
|
160
158
|
fragment OnT1 on T1 {
|
|
161
159
|
a
|
|
@@ -171,17 +169,15 @@ describe('fragments optimization', () => {
|
|
|
171
169
|
b
|
|
172
170
|
}
|
|
173
171
|
|
|
174
|
-
fragment OnU on U {
|
|
175
|
-
...OnI
|
|
176
|
-
...OnT1
|
|
177
|
-
...OnT2
|
|
178
|
-
}
|
|
179
|
-
|
|
180
172
|
{
|
|
181
173
|
t {
|
|
182
|
-
...
|
|
174
|
+
...OnI
|
|
175
|
+
...OnT1
|
|
176
|
+
...OnT2
|
|
183
177
|
u {
|
|
184
|
-
...
|
|
178
|
+
...OnI
|
|
179
|
+
...OnT1
|
|
180
|
+
...OnT2
|
|
185
181
|
}
|
|
186
182
|
}
|
|
187
183
|
}
|
|
@@ -579,6 +575,175 @@ describe('fragments optimization', () => {
|
|
|
579
575
|
});
|
|
580
576
|
});
|
|
581
577
|
|
|
578
|
+
test('handles fragment matching at the top level of another fragment', () => {
|
|
579
|
+
const schema = parseSchema(`
|
|
580
|
+
type Query {
|
|
581
|
+
t: T
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
type T {
|
|
585
|
+
a: String
|
|
586
|
+
u: U
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
type U {
|
|
590
|
+
x: String
|
|
591
|
+
y: String
|
|
592
|
+
}
|
|
593
|
+
`);
|
|
594
|
+
|
|
595
|
+
testFragmentsRoundtrip({
|
|
596
|
+
schema,
|
|
597
|
+
query: `
|
|
598
|
+
fragment Frag1 on T {
|
|
599
|
+
a
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
fragment Frag2 on T {
|
|
603
|
+
u {
|
|
604
|
+
x
|
|
605
|
+
y
|
|
606
|
+
}
|
|
607
|
+
...Frag1
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
fragment Frag3 on Query {
|
|
611
|
+
t {
|
|
612
|
+
...Frag2
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
{
|
|
617
|
+
...Frag3
|
|
618
|
+
}
|
|
619
|
+
`,
|
|
620
|
+
expanded: `
|
|
621
|
+
{
|
|
622
|
+
t {
|
|
623
|
+
u {
|
|
624
|
+
x
|
|
625
|
+
y
|
|
626
|
+
}
|
|
627
|
+
a
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
`,
|
|
631
|
+
});
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
test('handles fragments used in a context where they get trimmed', () => {
|
|
635
|
+
const schema = parseSchema(`
|
|
636
|
+
type Query {
|
|
637
|
+
t1: T1
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
interface I {
|
|
641
|
+
x: Int
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
type T1 implements I {
|
|
645
|
+
x: Int
|
|
646
|
+
y: Int
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
type T2 implements I {
|
|
650
|
+
x: Int
|
|
651
|
+
z: Int
|
|
652
|
+
}
|
|
653
|
+
`);
|
|
654
|
+
|
|
655
|
+
testFragmentsRoundtrip({
|
|
656
|
+
schema,
|
|
657
|
+
query: `
|
|
658
|
+
fragment FragOnI on I {
|
|
659
|
+
... on T1 {
|
|
660
|
+
y
|
|
661
|
+
}
|
|
662
|
+
... on T2 {
|
|
663
|
+
z
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
{
|
|
668
|
+
t1 {
|
|
669
|
+
...FragOnI
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
`,
|
|
673
|
+
expanded: `
|
|
674
|
+
{
|
|
675
|
+
t1 {
|
|
676
|
+
y
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
`,
|
|
680
|
+
});
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
test('handles fragments used in the context of non-intersecting abstract types', () => {
|
|
684
|
+
const schema = parseSchema(`
|
|
685
|
+
type Query {
|
|
686
|
+
i2: I2
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
interface I1 {
|
|
690
|
+
x: Int
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
interface I2 {
|
|
694
|
+
y: Int
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
interface I3 {
|
|
698
|
+
z: Int
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
type T1 implements I1 & I2 {
|
|
702
|
+
x: Int
|
|
703
|
+
y: Int
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
type T2 implements I1 & I3 {
|
|
707
|
+
x: Int
|
|
708
|
+
z: Int
|
|
709
|
+
}
|
|
710
|
+
`);
|
|
711
|
+
|
|
712
|
+
testFragmentsRoundtrip({
|
|
713
|
+
schema,
|
|
714
|
+
query: `
|
|
715
|
+
fragment FragOnI1 on I1 {
|
|
716
|
+
... on I2 {
|
|
717
|
+
y
|
|
718
|
+
}
|
|
719
|
+
... on I3 {
|
|
720
|
+
z
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
{
|
|
725
|
+
i2 {
|
|
726
|
+
...FragOnI1
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
`,
|
|
730
|
+
expanded: `
|
|
731
|
+
{
|
|
732
|
+
i2 {
|
|
733
|
+
... on I1 {
|
|
734
|
+
... on I2 {
|
|
735
|
+
y
|
|
736
|
+
}
|
|
737
|
+
... on I3 {
|
|
738
|
+
z
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
`,
|
|
744
|
+
});
|
|
745
|
+
});
|
|
746
|
+
|
|
582
747
|
describe('applied directives', () => {
|
|
583
748
|
test('reuse fragments with directives on the fragment, but only when there is those directives', () => {
|
|
584
749
|
const schema = parseSchema(`
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { InputType, ListType, NonNullType, Schema } from "./definitions"
|
|
2
|
+
|
|
3
|
+
export type ArgumentCompositionStrategy = {
|
|
4
|
+
name: string,
|
|
5
|
+
supportedTypes: (schema: Schema) => InputType[],
|
|
6
|
+
mergeValues: (values: any[]) => any,
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const ARGUMENT_COMPOSITION_STRATEGIES = {
|
|
10
|
+
MAX: {
|
|
11
|
+
name: 'MAX',
|
|
12
|
+
supportedTypes: (schema: Schema) => [new NonNullType(schema.intType())],
|
|
13
|
+
mergeValues: (values: any[]) => Math.max(...values),
|
|
14
|
+
},
|
|
15
|
+
MIN: {
|
|
16
|
+
name: 'MIN',
|
|
17
|
+
supportedTypes: (schema: Schema) => [new NonNullType(schema.intType())],
|
|
18
|
+
mergeValues: (values: any[]) => Math.min(...values),
|
|
19
|
+
},
|
|
20
|
+
SUM: {
|
|
21
|
+
name: 'SUM',
|
|
22
|
+
supportedTypes: (schema: Schema) => [new NonNullType(schema.intType())],
|
|
23
|
+
mergeValues: (values: any[]) => values.reduce((acc, val) => acc + val, 0),
|
|
24
|
+
},
|
|
25
|
+
INTERSECTION: {
|
|
26
|
+
name: 'INTERSECTION',
|
|
27
|
+
supportedTypes: (schema: Schema) => schema.builtInScalarTypes().map((t) => new NonNullType(new ListType(new NonNullType(t)))),
|
|
28
|
+
mergeValues: (values: any[]) => values.reduce((acc, val) => acc.filter((v: any) => val.includes(v)), values[0]),
|
|
29
|
+
},
|
|
30
|
+
UNION: {
|
|
31
|
+
name: 'UNION',
|
|
32
|
+
supportedTypes: (schema: Schema) => schema.builtInScalarTypes().map((t) => new NonNullType(new ListType(new NonNullType(t)))),
|
|
33
|
+
mergeValues: (values: any[]) =>
|
|
34
|
+
values.reduce((acc, val) => {
|
|
35
|
+
const newValues = val.filter((v: any) => !acc.includes(v));
|
|
36
|
+
return acc.concat(newValues);
|
|
37
|
+
}, []),
|
|
38
|
+
},
|
|
39
|
+
}
|
package/src/coreSpec.ts
CHANGED
|
@@ -2,12 +2,12 @@ import { ASTNode, DirectiveLocation, GraphQLError, StringValueNode } from "graph
|
|
|
2
2
|
import { URL } from "url";
|
|
3
3
|
import { CoreFeature, Directive, DirectiveDefinition, EnumType, ErrGraphQLAPISchemaValidationFailed, ErrGraphQLValidationFailed, InputType, ListType, NamedType, NonNullType, ScalarType, Schema, SchemaDefinition, SchemaElement, sourceASTs } from "./definitions";
|
|
4
4
|
import { sameType } from "./types";
|
|
5
|
-
import { assert, firstOf } from './utils';
|
|
5
|
+
import { assert, firstOf, MapWithCachedArrays } from './utils';
|
|
6
6
|
import { aggregateError, ERRORS } from "./error";
|
|
7
7
|
import { valueToString } from "./values";
|
|
8
8
|
import { coreFeatureDefinitionIfKnown, registerKnownFeature } from "./knownCoreFeatures";
|
|
9
9
|
import { didYouMean, suggestionList } from "./suggestions";
|
|
10
|
-
import { ArgumentSpecification, createDirectiveSpecification, createEnumTypeSpecification, createScalarTypeSpecification, DirectiveSpecification, TypeSpecification } from "./directiveAndTypeSpecification";
|
|
10
|
+
import { ArgumentSpecification, createDirectiveSpecification, createEnumTypeSpecification, createScalarTypeSpecification, DirectiveCompositionSpecification, DirectiveSpecification, TypeSpecification } from "./directiveAndTypeSpecification";
|
|
11
11
|
|
|
12
12
|
export const coreIdentity = 'https://specs.apollo.dev/core';
|
|
13
13
|
export const linkIdentity = 'https://specs.apollo.dev/link';
|
|
@@ -38,10 +38,37 @@ function purposesDescription(purpose: CorePurpose) {
|
|
|
38
38
|
export abstract class FeatureDefinition {
|
|
39
39
|
readonly url: FeatureUrl;
|
|
40
40
|
|
|
41
|
+
private readonly _directiveSpecs = new MapWithCachedArrays<string, DirectiveSpecification>();
|
|
42
|
+
private readonly _typeSpecs = new MapWithCachedArrays<string, TypeSpecification>();
|
|
43
|
+
|
|
41
44
|
constructor(url: FeatureUrl | string) {
|
|
42
45
|
this.url = typeof url === 'string' ? FeatureUrl.parse(url) : url;
|
|
43
46
|
}
|
|
44
47
|
|
|
48
|
+
protected registerDirective(spec: DirectiveSpecification) {
|
|
49
|
+
this._directiveSpecs.set(spec.name, spec);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
protected registerType(spec: TypeSpecification) {
|
|
53
|
+
this._typeSpecs.set(spec.name, spec);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
directiveSpecs(): readonly DirectiveSpecification[] {
|
|
57
|
+
return this._directiveSpecs.values();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
directiveSpec(name: string): DirectiveSpecification | undefined {
|
|
61
|
+
return this._directiveSpecs.get(name);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
typeSpecs(): readonly TypeSpecification[] {
|
|
65
|
+
return this._typeSpecs.values();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
typeSpec(name: string): TypeSpecification | undefined {
|
|
69
|
+
return this._typeSpecs.get(name);
|
|
70
|
+
}
|
|
71
|
+
|
|
45
72
|
get identity(): string {
|
|
46
73
|
return this.url.identity;
|
|
47
74
|
}
|
|
@@ -60,9 +87,25 @@ export abstract class FeatureDefinition {
|
|
|
60
87
|
return nameInSchema != undefined && (directive.name === nameInSchema || directive.name.startsWith(`${nameInSchema}__`));
|
|
61
88
|
}
|
|
62
89
|
|
|
63
|
-
|
|
90
|
+
addElementsToSchema(schema: Schema): GraphQLError[] {
|
|
91
|
+
const feature = this.featureInSchema(schema);
|
|
92
|
+
assert(feature, 'The federation specification should have been added to the schema before this is called');
|
|
64
93
|
|
|
65
|
-
|
|
94
|
+
let errors: GraphQLError[] = [];
|
|
95
|
+
for (const type of this.typeSpecs()) {
|
|
96
|
+
errors = errors.concat(this.addTypeSpec(schema, type));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
for (const directive of this.directiveSpecs()) {
|
|
100
|
+
errors = errors.concat(this.addDirectiveSpec(schema, directive));
|
|
101
|
+
}
|
|
102
|
+
return errors;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
allElementNames(): string[] {
|
|
106
|
+
return this.directiveSpecs().map((spec) => `@${spec.name}`)
|
|
107
|
+
.concat(this.typeSpecs().map((spec) => spec.name));
|
|
108
|
+
}
|
|
66
109
|
|
|
67
110
|
protected nameInSchema(schema: Schema): string | undefined {
|
|
68
111
|
const feature = this.featureInSchema(schema);
|
|
@@ -79,12 +122,12 @@ export abstract class FeatureDefinition {
|
|
|
79
122
|
return feature ? feature.typeNameInSchema(typeName) : undefined;
|
|
80
123
|
}
|
|
81
124
|
|
|
82
|
-
protected rootDirective<TApplicationArgs extends {[key: string]: any}>(schema: Schema): DirectiveDefinition<TApplicationArgs> | undefined {
|
|
125
|
+
protected rootDirective<TApplicationArgs extends { [key: string]: any }>(schema: Schema): DirectiveDefinition<TApplicationArgs> | undefined {
|
|
83
126
|
const name = this.nameInSchema(schema);
|
|
84
127
|
return name ? schema.directive(name) as DirectiveDefinition<TApplicationArgs> | undefined : undefined;
|
|
85
128
|
}
|
|
86
129
|
|
|
87
|
-
protected directive<TApplicationArgs extends {[key: string]: any}>(schema: Schema, elementName: string): DirectiveDefinition<TApplicationArgs> | undefined {
|
|
130
|
+
protected directive<TApplicationArgs extends { [key: string]: any }>(schema: Schema, elementName: string): DirectiveDefinition<TApplicationArgs> | undefined {
|
|
88
131
|
const name = this.directiveNameInSchema(schema, elementName);
|
|
89
132
|
return name ? schema.directive(name) as DirectiveDefinition<TApplicationArgs> | undefined : undefined;
|
|
90
133
|
}
|
|
@@ -130,11 +173,17 @@ export abstract class FeatureDefinition {
|
|
|
130
173
|
return undefined;
|
|
131
174
|
}
|
|
132
175
|
|
|
176
|
+
compositionSpecification(directiveNameInFeature: string): DirectiveCompositionSpecification | undefined {
|
|
177
|
+
const spec = this._directiveSpecs.get(directiveNameInFeature);
|
|
178
|
+
return spec?.composition;
|
|
179
|
+
}
|
|
180
|
+
|
|
133
181
|
toString(): string {
|
|
134
182
|
return `${this.identity}/${this.version}`
|
|
135
183
|
}
|
|
136
184
|
}
|
|
137
185
|
|
|
186
|
+
|
|
138
187
|
export type CoreDirectiveArgs = {
|
|
139
188
|
url: undefined,
|
|
140
189
|
feature: string,
|
|
@@ -289,7 +338,7 @@ export function isCoreSpecDirectiveApplication(directive: Directive<SchemaDefini
|
|
|
289
338
|
if (url.identity === coreIdentity) {
|
|
290
339
|
return directive.name === (args.as ?? 'core');
|
|
291
340
|
} else {
|
|
292
|
-
return url.identity === linkIdentity &&
|
|
341
|
+
return url.identity === linkIdentity && directive.name === (args.as ?? linkDirectiveDefaultName);
|
|
293
342
|
}
|
|
294
343
|
} catch (err) {
|
|
295
344
|
return false;
|
|
@@ -307,7 +356,7 @@ function isValidUrlArgumentType(type: InputType, schema: Schema): boolean {
|
|
|
307
356
|
|
|
308
357
|
const linkPurposeTypeSpec = createEnumTypeSpecification({
|
|
309
358
|
name: 'Purpose',
|
|
310
|
-
values: corePurposes.map((name) => ({ name, description: purposesDescription(name)}))
|
|
359
|
+
values: corePurposes.map((name) => ({ name, description: purposesDescription(name) }))
|
|
311
360
|
});
|
|
312
361
|
|
|
313
362
|
const linkImportTypeSpec = createScalarTypeSpecification({ name: 'Import' });
|
|
@@ -321,32 +370,43 @@ export class CoreSpecDefinition extends FeatureDefinition {
|
|
|
321
370
|
name,
|
|
322
371
|
locations: [DirectiveLocation.SCHEMA],
|
|
323
372
|
repeatable: true,
|
|
324
|
-
|
|
373
|
+
args: this.createDefinitionArgumentSpecifications(),
|
|
325
374
|
});
|
|
375
|
+
this.registerDirective(this.directiveDefinitionSpec);
|
|
326
376
|
}
|
|
327
377
|
|
|
328
|
-
private createDefinitionArgumentSpecifications(
|
|
378
|
+
private createDefinitionArgumentSpecifications(): ArgumentSpecification[] {
|
|
329
379
|
const args: ArgumentSpecification[] = [
|
|
330
|
-
{ name: this.urlArgName(), type: schema.stringType() },
|
|
331
|
-
{ name: 'as', type: schema.stringType() },
|
|
380
|
+
{ name: this.urlArgName(), type: (schema) => schema.stringType() },
|
|
381
|
+
{ name: 'as', type: (schema) => schema.stringType() },
|
|
332
382
|
];
|
|
333
383
|
if (this.supportPurposes()) {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
384
|
+
args.push({
|
|
385
|
+
name: 'for',
|
|
386
|
+
type: (schema, nameInSchema) => {
|
|
387
|
+
const purposeName = `${nameInSchema ?? this.url.name}__${linkPurposeTypeSpec.name}`;
|
|
388
|
+
const errors = linkPurposeTypeSpec.checkOrAdd(schema, purposeName);
|
|
389
|
+
if (errors.length > 0) {
|
|
390
|
+
return errors;
|
|
391
|
+
}
|
|
392
|
+
return schema.type(purposeName) as InputType;
|
|
393
|
+
},
|
|
394
|
+
});
|
|
340
395
|
}
|
|
341
396
|
if (this.supportImport()) {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
397
|
+
args.push({
|
|
398
|
+
name: 'import',
|
|
399
|
+
type: (schema, nameInSchema) => {
|
|
400
|
+
const importName = `${nameInSchema ?? this.url.name}__${linkImportTypeSpec.name}`;
|
|
401
|
+
const errors = linkImportTypeSpec.checkOrAdd(schema, importName);
|
|
402
|
+
if (errors.length > 0) {
|
|
403
|
+
return errors;
|
|
404
|
+
}
|
|
405
|
+
return new ListType(schema.type(importName)!);
|
|
406
|
+
}
|
|
407
|
+
});
|
|
348
408
|
}
|
|
349
|
-
return
|
|
409
|
+
return args;
|
|
350
410
|
}
|
|
351
411
|
|
|
352
412
|
addElementsToSchema(_: Schema): GraphQLError[] {
|
|
@@ -394,7 +454,7 @@ export class CoreSpecDefinition extends FeatureDefinition {
|
|
|
394
454
|
//
|
|
395
455
|
// So instead, we put the directive on the schema definition unless some extensions exists but no
|
|
396
456
|
// definition does (that is, no non-extension elements are populated).
|
|
397
|
-
const schemaDef =
|
|
457
|
+
const schemaDef = schema.schemaDefinition;
|
|
398
458
|
// Side-note: this test must be done _before_ we call `applyDirective`, otherwise it would take it into
|
|
399
459
|
// account.
|
|
400
460
|
const hasDefinition = schemaDef.hasNonExtensionElements();
|
|
@@ -429,7 +489,7 @@ export class CoreSpecDefinition extends FeatureDefinition {
|
|
|
429
489
|
* must start with a `@`.
|
|
430
490
|
*/
|
|
431
491
|
allElementNames(): string[] {
|
|
432
|
-
const names = [
|
|
492
|
+
const names = [`@${this.url.name}`];
|
|
433
493
|
if (this.supportPurposes()) {
|
|
434
494
|
names.push('Purpose');
|
|
435
495
|
}
|
|
@@ -533,7 +593,7 @@ export class FeatureDefinitions<T extends FeatureDefinition = FeatureDefinition>
|
|
|
533
593
|
* Versions are a (major, minor) number pair.
|
|
534
594
|
*/
|
|
535
595
|
export class FeatureVersion {
|
|
536
|
-
constructor(public readonly major: number, public readonly minor: number) {}
|
|
596
|
+
constructor(public readonly major: number, public readonly minor: number) { }
|
|
537
597
|
|
|
538
598
|
/**
|
|
539
599
|
* Parse a version specifier of the form "v(major).(minor)" or throw
|
|
@@ -565,8 +625,8 @@ export class FeatureVersion {
|
|
|
565
625
|
* ```
|
|
566
626
|
**/
|
|
567
627
|
public satisfies(required: FeatureVersion): boolean {
|
|
568
|
-
const {major, minor} = this
|
|
569
|
-
const {major: rMajor, minor: rMinor} = required
|
|
628
|
+
const { major, minor } = this
|
|
629
|
+
const { major: rMajor, minor: rMinor } = required
|
|
570
630
|
return rMajor == major && (
|
|
571
631
|
major == 0
|
|
572
632
|
? rMinor == minor
|
|
@@ -580,7 +640,7 @@ export class FeatureVersion {
|
|
|
580
640
|
* of compatibility, so those will just return the same thing as `this.toString()`.
|
|
581
641
|
*/
|
|
582
642
|
public get series() {
|
|
583
|
-
const {major} = this
|
|
643
|
+
const { major } = this
|
|
584
644
|
return major > 0 ? `${major}.x` : String(this)
|
|
585
645
|
}
|
|
586
646
|
|
|
@@ -650,7 +710,7 @@ export class FeatureUrl {
|
|
|
650
710
|
public readonly name: string,
|
|
651
711
|
public readonly version: FeatureVersion,
|
|
652
712
|
public readonly element?: string,
|
|
653
|
-
) {}
|
|
713
|
+
) { }
|
|
654
714
|
|
|
655
715
|
/// Parse a spec URL or throw
|
|
656
716
|
public static parse(input: string, node?: ASTNode): FeatureUrl {
|
|
@@ -668,7 +728,7 @@ export class FeatureUrl {
|
|
|
668
728
|
if (!name) {
|
|
669
729
|
throw ERRORS.INVALID_LINK_IDENTIFIER.err(`Missing feature name component in feature url '${url}'`, { nodes: node })
|
|
670
730
|
}
|
|
671
|
-
const element = url.hash ? url.hash.slice(1): undefined
|
|
731
|
+
const element = url.hash ? url.hash.slice(1) : undefined
|
|
672
732
|
url.hash = ''
|
|
673
733
|
url.search = ''
|
|
674
734
|
url.password = ''
|
|
@@ -690,7 +750,7 @@ export class FeatureUrl {
|
|
|
690
750
|
*/
|
|
691
751
|
public satisfies(requested: FeatureUrl): boolean {
|
|
692
752
|
return requested.identity === this.identity &&
|
|
693
|
-
|
|
753
|
+
this.version.satisfies(requested.version)
|
|
694
754
|
}
|
|
695
755
|
|
|
696
756
|
public equals(other: FeatureUrl) {
|