@apollo/gateway 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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apollo/gateway",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.3",
|
|
4
4
|
"description": "Apollo Gateway",
|
|
5
5
|
"author": "Apollo <packages@apollographql.com>",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"access": "public"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@apollo/composition": "2.4.
|
|
29
|
-
"@apollo/federation-internals": "2.4.
|
|
30
|
-
"@apollo/query-planner": "2.4.
|
|
28
|
+
"@apollo/composition": "2.4.3",
|
|
29
|
+
"@apollo/federation-internals": "2.4.3",
|
|
30
|
+
"@apollo/query-planner": "2.4.3",
|
|
31
31
|
"@apollo/server-gateway-interface": "^1.1.0",
|
|
32
32
|
"@apollo/usage-reporting-protobuf": "^4.1.0",
|
|
33
33
|
"@apollo/utils.createhash": "^2.0.0",
|
|
@@ -3802,10 +3802,15 @@ describe('executeQueryPlan', () => {
|
|
|
3802
3802
|
s1,
|
|
3803
3803
|
}: {
|
|
3804
3804
|
s1?: {
|
|
3805
|
+
// additional resolvers for interface I
|
|
3805
3806
|
iResolversExtra?: any,
|
|
3807
|
+
// provide a default __resolveReference for the interface
|
|
3806
3808
|
hasIResolveReference?: boolean,
|
|
3809
|
+
// turn an id into extra data returned by __resolveReference (if hasIResolveReference is true)
|
|
3807
3810
|
iResolveReferenceExtra?: (id: string) => { [k: string]: any },
|
|
3811
|
+
// additional resolvers for type A
|
|
3808
3812
|
aResolversExtra?: any,
|
|
3813
|
+
// additional resolvers for type B
|
|
3809
3814
|
bResolversExtra?: any,
|
|
3810
3815
|
}
|
|
3811
3816
|
}) => {
|
|
@@ -4136,7 +4141,75 @@ describe('executeQueryPlan', () => {
|
|
|
4136
4141
|
+ 'Either the object returned by "I.__resolveReference" must include a valid `__typename` field, '
|
|
4137
4142
|
+ 'or the "I" type should provide a "resolveType" function or each possible type should provide an "isTypeOf" function.'
|
|
4138
4143
|
],
|
|
4139
|
-
}
|
|
4144
|
+
}, {
|
|
4145
|
+
name: 'with an async __resolveReference and a non-default __resolveType',
|
|
4146
|
+
s1: {
|
|
4147
|
+
iResolversExtra: {
|
|
4148
|
+
async __resolveReference(ref: { id: string }) {
|
|
4149
|
+
return ref.id === 'idA'
|
|
4150
|
+
? { id: ref.id, x: 1, z: 3 }
|
|
4151
|
+
: { id: ref.id, x: 10, w: 30 };
|
|
4152
|
+
},
|
|
4153
|
+
__resolveType(ref: { id: string }) {
|
|
4154
|
+
switch (ref.id) {
|
|
4155
|
+
case 'idA':
|
|
4156
|
+
return 'A';
|
|
4157
|
+
case 'idB':
|
|
4158
|
+
return 'B';
|
|
4159
|
+
default:
|
|
4160
|
+
throw new Error('Unknown type: ' + ref.id);
|
|
4161
|
+
}
|
|
4162
|
+
},
|
|
4163
|
+
},
|
|
4164
|
+
},
|
|
4165
|
+
}, {
|
|
4166
|
+
name: 'with an async __resolveReference and a non-default async __resolveType',
|
|
4167
|
+
s1: {
|
|
4168
|
+
iResolversExtra: {
|
|
4169
|
+
async __resolveReference(ref: { id: string }) {
|
|
4170
|
+
return ref.id === 'idA'
|
|
4171
|
+
? { id: ref.id, x: 1, z: 3 }
|
|
4172
|
+
: { id: ref.id, x: 10, w: 30 };
|
|
4173
|
+
},
|
|
4174
|
+
async __resolveType(ref: { id: string }) {
|
|
4175
|
+
switch (ref.id) {
|
|
4176
|
+
case 'idA':
|
|
4177
|
+
return 'A';
|
|
4178
|
+
case 'idB':
|
|
4179
|
+
return 'B';
|
|
4180
|
+
default:
|
|
4181
|
+
throw new Error('Unknown type: ' + ref.id);
|
|
4182
|
+
}
|
|
4183
|
+
},
|
|
4184
|
+
},
|
|
4185
|
+
},
|
|
4186
|
+
}, {
|
|
4187
|
+
name: 'with an async __resolveReference and async __isTypeOf on implementations',
|
|
4188
|
+
s1: {
|
|
4189
|
+
iResolversExtra: {
|
|
4190
|
+
async __resolveReference(ref: {
|
|
4191
|
+
id: string;
|
|
4192
|
+
// I don't understand the TypeScript error that occurs when the
|
|
4193
|
+
// return type is removed here (like all the others); it surfaces
|
|
4194
|
+
// because `aResolversExtra` is defined, which I can't explain.
|
|
4195
|
+
}): Promise<Record<string, any>> {
|
|
4196
|
+
return ref.id === 'idA'
|
|
4197
|
+
? { id: ref.id, x: 1, z: 3 }
|
|
4198
|
+
: { id: ref.id, x: 10, w: 30 };
|
|
4199
|
+
},
|
|
4200
|
+
},
|
|
4201
|
+
aResolversExtra: {
|
|
4202
|
+
async __isTypeOf(ref: { id: string }) {
|
|
4203
|
+
return ref.id === 'idA';
|
|
4204
|
+
},
|
|
4205
|
+
},
|
|
4206
|
+
bResolversExtra: {
|
|
4207
|
+
async __isTypeOf(ref: { id: string }) {
|
|
4208
|
+
return ref.id === 'idB';
|
|
4209
|
+
},
|
|
4210
|
+
},
|
|
4211
|
+
},
|
|
4212
|
+
}])('resolving an interface @key $name', async ({ s1, expectedErrors }) => {
|
|
4140
4213
|
const tester = defineSchema({ s1 });
|
|
4141
4214
|
|
|
4142
4215
|
const { plan, response } = await tester(`
|
|
@@ -4863,6 +4936,251 @@ describe('executeQueryPlan', () => {
|
|
|
4863
4936
|
}
|
|
4864
4937
|
`);
|
|
4865
4938
|
});
|
|
4939
|
+
|
|
4940
|
+
test('handles @requires on @interfaceObject that applies to only one of the queried implementation', async () => {
|
|
4941
|
+
// The case this test is that where the @interfaceObject in s2 has a @requires, but the query we send requests the field on which
|
|
4942
|
+
// there is this @require only for one of the implementation type, which it request another field with no require for another implementation.
|
|
4943
|
+
// And we're making sure the requirements only get queried for T1, the first type.
|
|
4944
|
+
const s1 = {
|
|
4945
|
+
name: 's1',
|
|
4946
|
+
typeDefs: gql`
|
|
4947
|
+
extend schema
|
|
4948
|
+
@link(url: "https://specs.apollo.dev/federation/v2.4", import: ["@key"])
|
|
4949
|
+
|
|
4950
|
+
type Query {
|
|
4951
|
+
is: [I!]!
|
|
4952
|
+
}
|
|
4953
|
+
|
|
4954
|
+
interface I @key(fields: "id") {
|
|
4955
|
+
id: ID!
|
|
4956
|
+
name: String
|
|
4957
|
+
req: Req
|
|
4958
|
+
}
|
|
4959
|
+
|
|
4960
|
+
type T1 implements I @key(fields: "id") {
|
|
4961
|
+
id: ID!
|
|
4962
|
+
name: String
|
|
4963
|
+
req: Req
|
|
4964
|
+
}
|
|
4965
|
+
|
|
4966
|
+
type T2 implements I @key(fields: "id") {
|
|
4967
|
+
id: ID!
|
|
4968
|
+
name: String
|
|
4969
|
+
req: Req
|
|
4970
|
+
}
|
|
4971
|
+
|
|
4972
|
+
type Req {
|
|
4973
|
+
id: ID!
|
|
4974
|
+
}
|
|
4975
|
+
`,
|
|
4976
|
+
resolvers: {
|
|
4977
|
+
Query: {
|
|
4978
|
+
is: () => [
|
|
4979
|
+
{ __typename: 'T1', id: '2', name: 'e2', req: { id: 'r1'} },
|
|
4980
|
+
{ __typename: 'T2', id: '4', name: 'e4', req: { id: 'r2'} },
|
|
4981
|
+
{ __typename: 'T1', id: '1', name: 'e1', req: { id: 'r3'} }
|
|
4982
|
+
]
|
|
4983
|
+
},
|
|
4984
|
+
}
|
|
4985
|
+
}
|
|
4986
|
+
|
|
4987
|
+
const s2 = {
|
|
4988
|
+
name: 's2',
|
|
4989
|
+
typeDefs: gql`
|
|
4990
|
+
extend schema
|
|
4991
|
+
@link(url: "https://specs.apollo.dev/federation/v2.4", import: ["@key", "@interfaceObject", "@external", "@requires"])
|
|
4992
|
+
|
|
4993
|
+
type I @key(fields: "id") @interfaceObject {
|
|
4994
|
+
id: ID!
|
|
4995
|
+
req: Req @external
|
|
4996
|
+
v: String! @requires(fields: "req { id }")
|
|
4997
|
+
}
|
|
4998
|
+
|
|
4999
|
+
type Req {
|
|
5000
|
+
id: ID! @external
|
|
5001
|
+
}
|
|
5002
|
+
`,
|
|
5003
|
+
resolvers: {
|
|
5004
|
+
I: {
|
|
5005
|
+
__resolveReference(ref: any) {
|
|
5006
|
+
return {
|
|
5007
|
+
...ref,
|
|
5008
|
+
v: `req=${ref.req.id}`
|
|
5009
|
+
};
|
|
5010
|
+
},
|
|
5011
|
+
}
|
|
5012
|
+
}
|
|
5013
|
+
}
|
|
5014
|
+
|
|
5015
|
+
const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([ s1, s2 ]);
|
|
5016
|
+
|
|
5017
|
+
let operation = parseOp(`
|
|
5018
|
+
{
|
|
5019
|
+
is {
|
|
5020
|
+
... on T1 {
|
|
5021
|
+
v
|
|
5022
|
+
}
|
|
5023
|
+
... on T2 {
|
|
5024
|
+
name
|
|
5025
|
+
}
|
|
5026
|
+
}
|
|
5027
|
+
}
|
|
5028
|
+
`, schema);
|
|
5029
|
+
|
|
5030
|
+
let queryPlan = buildPlan(operation, queryPlanner);
|
|
5031
|
+
expect(queryPlan).toMatchInlineSnapshot(`
|
|
5032
|
+
QueryPlan {
|
|
5033
|
+
Sequence {
|
|
5034
|
+
Fetch(service: "s1") {
|
|
5035
|
+
{
|
|
5036
|
+
is {
|
|
5037
|
+
__typename
|
|
5038
|
+
... on T1 {
|
|
5039
|
+
__typename
|
|
5040
|
+
id
|
|
5041
|
+
req {
|
|
5042
|
+
id
|
|
5043
|
+
}
|
|
5044
|
+
}
|
|
5045
|
+
... on T2 {
|
|
5046
|
+
name
|
|
5047
|
+
}
|
|
5048
|
+
}
|
|
5049
|
+
}
|
|
5050
|
+
},
|
|
5051
|
+
Flatten(path: "is.@") {
|
|
5052
|
+
Fetch(service: "s2") {
|
|
5053
|
+
{
|
|
5054
|
+
... on T1 {
|
|
5055
|
+
__typename
|
|
5056
|
+
id
|
|
5057
|
+
}
|
|
5058
|
+
... on I {
|
|
5059
|
+
__typename
|
|
5060
|
+
req {
|
|
5061
|
+
id
|
|
5062
|
+
}
|
|
5063
|
+
}
|
|
5064
|
+
} =>
|
|
5065
|
+
{
|
|
5066
|
+
... on I {
|
|
5067
|
+
v
|
|
5068
|
+
}
|
|
5069
|
+
}
|
|
5070
|
+
},
|
|
5071
|
+
},
|
|
5072
|
+
},
|
|
5073
|
+
}
|
|
5074
|
+
`);
|
|
5075
|
+
|
|
5076
|
+
let response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
|
|
5077
|
+
expect(response.errors).toBeUndefined();
|
|
5078
|
+
expect(response.data).toMatchInlineSnapshot(`
|
|
5079
|
+
Object {
|
|
5080
|
+
"is": Array [
|
|
5081
|
+
Object {
|
|
5082
|
+
"v": "req=r1",
|
|
5083
|
+
},
|
|
5084
|
+
Object {
|
|
5085
|
+
"name": "e4",
|
|
5086
|
+
},
|
|
5087
|
+
Object {
|
|
5088
|
+
"v": "req=r3",
|
|
5089
|
+
},
|
|
5090
|
+
],
|
|
5091
|
+
}
|
|
5092
|
+
`);
|
|
5093
|
+
|
|
5094
|
+
// Sanity checking that if we ask for `v` (the field with @requires), then everything still works.
|
|
5095
|
+
operation = parseOp(`
|
|
5096
|
+
{
|
|
5097
|
+
is {
|
|
5098
|
+
... on T1 {
|
|
5099
|
+
v
|
|
5100
|
+
}
|
|
5101
|
+
... on T2 {
|
|
5102
|
+
v
|
|
5103
|
+
name
|
|
5104
|
+
}
|
|
5105
|
+
}
|
|
5106
|
+
}
|
|
5107
|
+
`, schema);
|
|
5108
|
+
|
|
5109
|
+
global.console = require('console');
|
|
5110
|
+
queryPlan = buildPlan(operation, queryPlanner);
|
|
5111
|
+
expect(queryPlan).toMatchInlineSnapshot(`
|
|
5112
|
+
QueryPlan {
|
|
5113
|
+
Sequence {
|
|
5114
|
+
Fetch(service: "s1") {
|
|
5115
|
+
{
|
|
5116
|
+
is {
|
|
5117
|
+
__typename
|
|
5118
|
+
... on T1 {
|
|
5119
|
+
__typename
|
|
5120
|
+
id
|
|
5121
|
+
req {
|
|
5122
|
+
id
|
|
5123
|
+
}
|
|
5124
|
+
}
|
|
5125
|
+
... on T2 {
|
|
5126
|
+
__typename
|
|
5127
|
+
id
|
|
5128
|
+
req {
|
|
5129
|
+
id
|
|
5130
|
+
}
|
|
5131
|
+
name
|
|
5132
|
+
}
|
|
5133
|
+
}
|
|
5134
|
+
}
|
|
5135
|
+
},
|
|
5136
|
+
Flatten(path: "is.@") {
|
|
5137
|
+
Fetch(service: "s2") {
|
|
5138
|
+
{
|
|
5139
|
+
... on T1 {
|
|
5140
|
+
__typename
|
|
5141
|
+
id
|
|
5142
|
+
}
|
|
5143
|
+
... on I {
|
|
5144
|
+
__typename
|
|
5145
|
+
req {
|
|
5146
|
+
id
|
|
5147
|
+
}
|
|
5148
|
+
}
|
|
5149
|
+
... on T2 {
|
|
5150
|
+
__typename
|
|
5151
|
+
id
|
|
5152
|
+
}
|
|
5153
|
+
} =>
|
|
5154
|
+
{
|
|
5155
|
+
... on I {
|
|
5156
|
+
v
|
|
5157
|
+
}
|
|
5158
|
+
}
|
|
5159
|
+
},
|
|
5160
|
+
},
|
|
5161
|
+
},
|
|
5162
|
+
}
|
|
5163
|
+
`);
|
|
5164
|
+
|
|
5165
|
+
response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
|
|
5166
|
+
expect(response.errors).toBeUndefined();
|
|
5167
|
+
expect(response.data).toMatchInlineSnapshot(`
|
|
5168
|
+
Object {
|
|
5169
|
+
"is": Array [
|
|
5170
|
+
Object {
|
|
5171
|
+
"v": "req=r1",
|
|
5172
|
+
},
|
|
5173
|
+
Object {
|
|
5174
|
+
"name": "e4",
|
|
5175
|
+
"v": "req=r2",
|
|
5176
|
+
},
|
|
5177
|
+
Object {
|
|
5178
|
+
"v": "req=r3",
|
|
5179
|
+
},
|
|
5180
|
+
],
|
|
5181
|
+
}
|
|
5182
|
+
`);
|
|
5183
|
+
});
|
|
4866
5184
|
});
|
|
4867
5185
|
|
|
4868
5186
|
describe('fields with conflicting types needing aliasing', () => {
|