@apollo/gateway 0.48.0 → 0.49.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.
- package/README.md +7 -5
- package/dist/__generated__/graphqlTypes.d.ts +4 -0
- package/dist/__generated__/graphqlTypes.d.ts.map +1 -1
- package/dist/__generated__/graphqlTypes.js.map +1 -1
- package/dist/config.d.ts +6 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/executeQueryPlan.d.ts.map +1 -1
- package/dist/executeQueryPlan.js +4 -3
- package/dist/executeQueryPlan.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -10
- package/dist/index.js.map +1 -1
- package/dist/schema-helper/index.js +5 -1
- package/dist/schema-helper/index.js.map +1 -1
- package/dist/schema-helper/resolverMap.d.ts +2 -2
- package/dist/schema-helper/resolverMap.d.ts.map +1 -1
- package/dist/supergraphManagers/IntrospectAndCompose/index.js.map +1 -1
- package/dist/supergraphManagers/LegacyFetcher/index.js +1 -1
- package/dist/supergraphManagers/LegacyFetcher/index.js.map +1 -1
- package/dist/supergraphManagers/LocalCompose/index.js +1 -1
- package/dist/supergraphManagers/LocalCompose/index.js.map +1 -1
- package/dist/supergraphManagers/UplinkFetcher/index.d.ts +4 -1
- package/dist/supergraphManagers/UplinkFetcher/index.d.ts.map +1 -1
- package/dist/supergraphManagers/UplinkFetcher/index.js +22 -4
- package/dist/supergraphManagers/UplinkFetcher/index.js.map +1 -1
- package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.d.ts +7 -2
- package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.d.ts.map +1 -1
- package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.js +37 -33
- package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.js.map +1 -1
- package/dist/supergraphManagers/index.d.ts +1 -0
- package/dist/supergraphManagers/index.d.ts.map +1 -1
- package/dist/supergraphManagers/index.js +3 -1
- package/dist/supergraphManagers/index.js.map +1 -1
- package/package.json +6 -5
- package/src/__generated__/graphqlTypes.ts +11 -2
- package/src/__tests__/CucumberREADME.md +1 -0
- package/src/__tests__/build-query-plan-fragmentization.feature +10 -0
- package/src/__tests__/build-query-plan.feature +84 -16
- package/src/__tests__/buildQueryPlan.test.ts +272 -1
- package/src/__tests__/gateway/lifecycle-hooks.test.ts +3 -3
- package/src/__tests__/gateway/reporting.test.ts +4 -0
- package/src/__tests__/gateway/supergraphSdl.test.ts +3 -3
- package/src/__tests__/integration/abstract-types.test.ts +3 -3
- package/src/__tests__/integration/configuration.test.ts +0 -11
- package/src/__tests__/integration/nockMocks.ts +3 -2
- package/src/__tests__/integration/requires.test.ts +1 -1
- package/src/__tests__/integration/value-types.test.ts +1 -1
- package/src/config.ts +11 -6
- package/src/executeQueryPlan.ts +4 -0
- package/src/index.ts +16 -7
- package/src/schema-helper/resolverMap.ts +2 -2
- package/src/supergraphManagers/LegacyFetcher/index.ts +1 -1
- package/src/supergraphManagers/LocalCompose/index.ts +1 -1
- package/src/supergraphManagers/UplinkFetcher/__tests__/loadSupergraphSdlFromStorage.test.ts +123 -28
- package/src/supergraphManagers/UplinkFetcher/index.ts +39 -18
- package/src/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.ts +40 -27
- package/src/supergraphManagers/index.ts +1 -0
|
@@ -7,7 +7,12 @@ import {
|
|
|
7
7
|
fixturesWithUpdate,
|
|
8
8
|
} from 'apollo-federation-integration-testsuite';
|
|
9
9
|
import { getFederatedTestingSchema } from './execution-utils';
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
QueryPlanner,
|
|
12
|
+
FetchNode,
|
|
13
|
+
SequenceNode,
|
|
14
|
+
FlattenNode,
|
|
15
|
+
} from '@apollo/query-planner';
|
|
11
16
|
|
|
12
17
|
expect.addSnapshotSerializer(astSerializer);
|
|
13
18
|
expect.addSnapshotSerializer(queryPlanSerializer);
|
|
@@ -1935,4 +1940,270 @@ describe('buildQueryPlan', () => {
|
|
|
1935
1940
|
);
|
|
1936
1941
|
});
|
|
1937
1942
|
});
|
|
1943
|
+
|
|
1944
|
+
it(`when key is marked external inside another type`, () => {
|
|
1945
|
+
const operationString = `#graphql
|
|
1946
|
+
query {
|
|
1947
|
+
libraryAccount {
|
|
1948
|
+
description
|
|
1949
|
+
library {
|
|
1950
|
+
id
|
|
1951
|
+
name
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
`;
|
|
1956
|
+
|
|
1957
|
+
const operationDocument = gql(operationString);
|
|
1958
|
+
|
|
1959
|
+
const queryPlan = queryPlanner.buildQueryPlan(
|
|
1960
|
+
buildOperationContext({
|
|
1961
|
+
schema,
|
|
1962
|
+
operationDocument,
|
|
1963
|
+
}),
|
|
1964
|
+
);
|
|
1965
|
+
|
|
1966
|
+
expect(queryPlan).toMatchInlineSnapshot(`
|
|
1967
|
+
QueryPlan {
|
|
1968
|
+
Fetch(service: "accounts") {
|
|
1969
|
+
{
|
|
1970
|
+
libraryAccount {
|
|
1971
|
+
description
|
|
1972
|
+
library {
|
|
1973
|
+
id
|
|
1974
|
+
name
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
},
|
|
1979
|
+
}
|
|
1980
|
+
`);
|
|
1981
|
+
});
|
|
1982
|
+
|
|
1983
|
+
describe('fetch operation names', () => {
|
|
1984
|
+
it('handle subgraph with - in the name', () => {
|
|
1985
|
+
const subgraph1 = {
|
|
1986
|
+
name: 'S1',
|
|
1987
|
+
typeDefs: gql`
|
|
1988
|
+
type Query {
|
|
1989
|
+
t: T
|
|
1990
|
+
}
|
|
1991
|
+
type T @key(fields: "id") {
|
|
1992
|
+
id: ID!
|
|
1993
|
+
}
|
|
1994
|
+
`,
|
|
1995
|
+
};
|
|
1996
|
+
|
|
1997
|
+
const subgraph2 = {
|
|
1998
|
+
name: 'non-graphql-name',
|
|
1999
|
+
typeDefs: gql`
|
|
2000
|
+
extend type T @key(fields: "id") {
|
|
2001
|
+
id: ID! @external
|
|
2002
|
+
x: Int
|
|
2003
|
+
}
|
|
2004
|
+
`,
|
|
2005
|
+
};
|
|
2006
|
+
|
|
2007
|
+
({ schema, queryPlanner } = getFederatedTestingSchema([
|
|
2008
|
+
subgraph1,
|
|
2009
|
+
subgraph2,
|
|
2010
|
+
]));
|
|
2011
|
+
|
|
2012
|
+
const operationDocument = gql`
|
|
2013
|
+
query myOp {
|
|
2014
|
+
t {
|
|
2015
|
+
x
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
`;
|
|
2019
|
+
|
|
2020
|
+
const plan = queryPlanner.buildQueryPlan(
|
|
2021
|
+
buildOperationContext({ schema, operationDocument }),
|
|
2022
|
+
);
|
|
2023
|
+
|
|
2024
|
+
expect(plan).toMatchInlineSnapshot(`
|
|
2025
|
+
QueryPlan {
|
|
2026
|
+
Sequence {
|
|
2027
|
+
Fetch(service: "S1") {
|
|
2028
|
+
{
|
|
2029
|
+
t {
|
|
2030
|
+
__typename
|
|
2031
|
+
id
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2034
|
+
},
|
|
2035
|
+
Flatten(path: "t") {
|
|
2036
|
+
Fetch(service: "non-graphql-name") {
|
|
2037
|
+
{
|
|
2038
|
+
... on T {
|
|
2039
|
+
__typename
|
|
2040
|
+
id
|
|
2041
|
+
}
|
|
2042
|
+
} =>
|
|
2043
|
+
{
|
|
2044
|
+
... on T {
|
|
2045
|
+
x
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
},
|
|
2049
|
+
},
|
|
2050
|
+
},
|
|
2051
|
+
}
|
|
2052
|
+
`);
|
|
2053
|
+
const fetch = ((plan.node as SequenceNode).nodes[1] as FlattenNode)
|
|
2054
|
+
.node as FetchNode;
|
|
2055
|
+
expect(fetch.operation).toMatch(/^query myOp__non_graphql_name__1.*/i);
|
|
2056
|
+
});
|
|
2057
|
+
|
|
2058
|
+
it('ensures sanitization applies repeatedly', () => {
|
|
2059
|
+
const subgraph1 = {
|
|
2060
|
+
name: 'S1',
|
|
2061
|
+
typeDefs: gql`
|
|
2062
|
+
type Query {
|
|
2063
|
+
t: T
|
|
2064
|
+
}
|
|
2065
|
+
type T @key(fields: "id") {
|
|
2066
|
+
id: ID!
|
|
2067
|
+
}
|
|
2068
|
+
`,
|
|
2069
|
+
};
|
|
2070
|
+
|
|
2071
|
+
const subgraph2 = {
|
|
2072
|
+
name: 'a-na&me-with-plen&ty-replace*ments',
|
|
2073
|
+
typeDefs: gql`
|
|
2074
|
+
extend type T @key(fields: "id") {
|
|
2075
|
+
id: ID! @external
|
|
2076
|
+
x: Int
|
|
2077
|
+
}
|
|
2078
|
+
`,
|
|
2079
|
+
};
|
|
2080
|
+
|
|
2081
|
+
({ schema, queryPlanner } = getFederatedTestingSchema([
|
|
2082
|
+
subgraph1,
|
|
2083
|
+
subgraph2,
|
|
2084
|
+
]));
|
|
2085
|
+
|
|
2086
|
+
const operationDocument = gql`
|
|
2087
|
+
query myOp {
|
|
2088
|
+
t {
|
|
2089
|
+
x
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
`;
|
|
2093
|
+
|
|
2094
|
+
const plan = queryPlanner.buildQueryPlan(
|
|
2095
|
+
buildOperationContext({ schema, operationDocument }),
|
|
2096
|
+
);
|
|
2097
|
+
|
|
2098
|
+
expect(plan).toMatchInlineSnapshot(`
|
|
2099
|
+
QueryPlan {
|
|
2100
|
+
Sequence {
|
|
2101
|
+
Fetch(service: "S1") {
|
|
2102
|
+
{
|
|
2103
|
+
t {
|
|
2104
|
+
__typename
|
|
2105
|
+
id
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
},
|
|
2109
|
+
Flatten(path: "t") {
|
|
2110
|
+
Fetch(service: "a-na&me-with-plen&ty-replace*ments") {
|
|
2111
|
+
{
|
|
2112
|
+
... on T {
|
|
2113
|
+
__typename
|
|
2114
|
+
id
|
|
2115
|
+
}
|
|
2116
|
+
} =>
|
|
2117
|
+
{
|
|
2118
|
+
... on T {
|
|
2119
|
+
x
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
},
|
|
2123
|
+
},
|
|
2124
|
+
},
|
|
2125
|
+
}
|
|
2126
|
+
`);
|
|
2127
|
+
|
|
2128
|
+
const fetch = ((plan.node as SequenceNode).nodes[1] as FlattenNode)
|
|
2129
|
+
.node as FetchNode;
|
|
2130
|
+
expect(fetch.operation).toMatch(
|
|
2131
|
+
/^query myOp__a_name_with_plenty_replacements__1.*/i,
|
|
2132
|
+
);
|
|
2133
|
+
});
|
|
2134
|
+
|
|
2135
|
+
it('handle very non-graph subgraph name', () => {
|
|
2136
|
+
const subgraph1 = {
|
|
2137
|
+
name: 'S1',
|
|
2138
|
+
typeDefs: gql`
|
|
2139
|
+
type Query {
|
|
2140
|
+
t: T
|
|
2141
|
+
}
|
|
2142
|
+
type T @key(fields: "id") {
|
|
2143
|
+
id: ID!
|
|
2144
|
+
}
|
|
2145
|
+
`,
|
|
2146
|
+
};
|
|
2147
|
+
|
|
2148
|
+
const subgraph2 = {
|
|
2149
|
+
name: '42!',
|
|
2150
|
+
typeDefs: gql`
|
|
2151
|
+
extend type T @key(fields: "id") {
|
|
2152
|
+
id: ID! @external
|
|
2153
|
+
x: Int
|
|
2154
|
+
}
|
|
2155
|
+
`,
|
|
2156
|
+
};
|
|
2157
|
+
|
|
2158
|
+
({ schema, queryPlanner } = getFederatedTestingSchema([
|
|
2159
|
+
subgraph1,
|
|
2160
|
+
subgraph2,
|
|
2161
|
+
]));
|
|
2162
|
+
|
|
2163
|
+
const operationDocument = gql`
|
|
2164
|
+
query myOp {
|
|
2165
|
+
t {
|
|
2166
|
+
x
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
`;
|
|
2170
|
+
|
|
2171
|
+
const plan = queryPlanner.buildQueryPlan(
|
|
2172
|
+
buildOperationContext({ schema, operationDocument }),
|
|
2173
|
+
);
|
|
2174
|
+
expect(plan).toMatchInlineSnapshot(`
|
|
2175
|
+
QueryPlan {
|
|
2176
|
+
Sequence {
|
|
2177
|
+
Fetch(service: "S1") {
|
|
2178
|
+
{
|
|
2179
|
+
t {
|
|
2180
|
+
__typename
|
|
2181
|
+
id
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
},
|
|
2185
|
+
Flatten(path: "t") {
|
|
2186
|
+
Fetch(service: "42!") {
|
|
2187
|
+
{
|
|
2188
|
+
... on T {
|
|
2189
|
+
__typename
|
|
2190
|
+
id
|
|
2191
|
+
}
|
|
2192
|
+
} =>
|
|
2193
|
+
{
|
|
2194
|
+
... on T {
|
|
2195
|
+
x
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
},
|
|
2199
|
+
},
|
|
2200
|
+
},
|
|
2201
|
+
}
|
|
2202
|
+
`);
|
|
2203
|
+
const fetch = ((plan.node as SequenceNode).nodes[1] as FlattenNode)
|
|
2204
|
+
.node as FetchNode;
|
|
2205
|
+
|
|
2206
|
+
expect(fetch.operation).toMatch(/^query myOp___42__1.*/i);
|
|
2207
|
+
});
|
|
2208
|
+
});
|
|
1938
2209
|
});
|
|
@@ -135,13 +135,13 @@ describe('lifecycle hooks', () => {
|
|
|
135
135
|
|
|
136
136
|
const [firstCall, secondCall] = mockDidUpdate.mock.calls;
|
|
137
137
|
|
|
138
|
-
const expectedFirstId = '
|
|
138
|
+
const expectedFirstId = '1cb734eadae95dd1778f3fe3c9df6cbaecb95e80c2bcefed397c63ec72469032'
|
|
139
139
|
expect(firstCall[0]!.compositionId).toEqual(expectedFirstId);
|
|
140
140
|
// first call should have no second "previous" argument
|
|
141
141
|
expect(firstCall[1]).toBeUndefined();
|
|
142
142
|
|
|
143
143
|
expect(secondCall[0]!.compositionId).toEqual(
|
|
144
|
-
'
|
|
144
|
+
'5d24c0f42d0d372c2ad671567cee186e3680400dffbcc3430cb57b9dc04b2be2',
|
|
145
145
|
);
|
|
146
146
|
// second call should have previous info in the second arg
|
|
147
147
|
expect(secondCall[1]!.compositionId).toEqual(expectedFirstId);
|
|
@@ -177,7 +177,7 @@ describe('lifecycle hooks', () => {
|
|
|
177
177
|
);
|
|
178
178
|
});
|
|
179
179
|
|
|
180
|
-
it('registers schema change callbacks when
|
|
180
|
+
it('registers schema change callbacks when pollIntervalInMs is set for unmanaged configs', async () => {
|
|
181
181
|
const experimental_updateServiceDefinitions: Experimental_UpdateServiceDefinitions =
|
|
182
182
|
jest.fn(async (_config) => {
|
|
183
183
|
return { serviceDefinitions, isNewSchema: true };
|
|
@@ -299,6 +299,7 @@ describe('reporting', () => {
|
|
|
299
299
|
"nanos": 123000000,
|
|
300
300
|
"seconds": "1562203363",
|
|
301
301
|
},
|
|
302
|
+
"fieldExecutionWeight": 1,
|
|
302
303
|
"root": Object {
|
|
303
304
|
"child": Array [
|
|
304
305
|
Object {
|
|
@@ -364,6 +365,7 @@ describe('reporting', () => {
|
|
|
364
365
|
"nanos": 123000000,
|
|
365
366
|
"seconds": "1562203363",
|
|
366
367
|
},
|
|
368
|
+
"fieldExecutionWeight": 1,
|
|
367
369
|
"root": Object {
|
|
368
370
|
"child": Array [
|
|
369
371
|
Object {
|
|
@@ -465,6 +467,7 @@ describe('reporting', () => {
|
|
|
465
467
|
"nanos": 123000000,
|
|
466
468
|
"seconds": "1562203363",
|
|
467
469
|
},
|
|
470
|
+
"fieldExecutionWeight": 1,
|
|
468
471
|
"root": Object {
|
|
469
472
|
"child": Array [
|
|
470
473
|
Object {
|
|
@@ -568,6 +571,7 @@ describe('reporting', () => {
|
|
|
568
571
|
"nanos": 123000000,
|
|
569
572
|
"seconds": "1562203363",
|
|
570
573
|
},
|
|
574
|
+
"fieldExecutionWeight": 1,
|
|
571
575
|
"root": Object {
|
|
572
576
|
"child": Array [
|
|
573
577
|
Object {
|
|
@@ -184,7 +184,7 @@ describe('Using supergraphSdl dynamic configuration', () => {
|
|
|
184
184
|
const { state, compositionId } = gateway.__testing();
|
|
185
185
|
expect(state.phase).toEqual('loaded');
|
|
186
186
|
expect(compositionId).toEqual(
|
|
187
|
-
'
|
|
187
|
+
'1cb734eadae95dd1778f3fe3c9df6cbaecb95e80c2bcefed397c63ec72469032',
|
|
188
188
|
);
|
|
189
189
|
|
|
190
190
|
await gateway.stop();
|
|
@@ -209,7 +209,7 @@ describe('Using supergraphSdl dynamic configuration', () => {
|
|
|
209
209
|
const { state, compositionId } = gateway.__testing();
|
|
210
210
|
expect(state.phase).toEqual('loaded');
|
|
211
211
|
expect(compositionId).toEqual(
|
|
212
|
-
'
|
|
212
|
+
'1cb734eadae95dd1778f3fe3c9df6cbaecb95e80c2bcefed397c63ec72469032',
|
|
213
213
|
);
|
|
214
214
|
|
|
215
215
|
await expect(healthCheckCallback!(supergraphSdl)).resolves.toBeUndefined();
|
|
@@ -291,7 +291,7 @@ describe('Using supergraphSdl dynamic configuration', () => {
|
|
|
291
291
|
const { state, compositionId } = gateway.__testing();
|
|
292
292
|
expect(state.phase).toEqual('loaded');
|
|
293
293
|
expect(compositionId).toEqual(
|
|
294
|
-
'
|
|
294
|
+
'1cb734eadae95dd1778f3fe3c9df6cbaecb95e80c2bcefed397c63ec72469032',
|
|
295
295
|
);
|
|
296
296
|
|
|
297
297
|
await expect(healthCheckCallback!(supergraphSdl)).rejects.toThrowError(
|
|
@@ -297,7 +297,7 @@ it('prunes unfilled type conditions', async () => {
|
|
|
297
297
|
|
|
298
298
|
it('fetches interfaces returned from other services', async () => {
|
|
299
299
|
const query = `#graphql
|
|
300
|
-
query
|
|
300
|
+
query GetUserAndProductsWithPriceAndTitle {
|
|
301
301
|
me {
|
|
302
302
|
reviews {
|
|
303
303
|
product {
|
|
@@ -412,7 +412,7 @@ it('fetches interfaces returned from other services', async () => {
|
|
|
412
412
|
|
|
413
413
|
it('fetches composite fields from a foreign type casted to an interface [@provides field]', async () => {
|
|
414
414
|
const query = `#graphql
|
|
415
|
-
query
|
|
415
|
+
query GetUserAndProductsWithPriceAndName {
|
|
416
416
|
me {
|
|
417
417
|
reviews {
|
|
418
418
|
product {
|
|
@@ -624,7 +624,7 @@ it('allows for extending an interface from another service with fields', async (
|
|
|
624
624
|
describe('unions', () => {
|
|
625
625
|
it('handles unions from the same service', async () => {
|
|
626
626
|
const query = `#graphql
|
|
627
|
-
query
|
|
627
|
+
query GetUserAndProductsWithBrandInfo {
|
|
628
628
|
me {
|
|
629
629
|
reviews {
|
|
630
630
|
product {
|
|
@@ -443,15 +443,4 @@ describe('deprecation warnings', () => {
|
|
|
443
443
|
'The `schemaConfigDeliveryEndpoint` option is deprecated and will be removed in a future version of `@apollo/gateway`. Please migrate to the equivalent (array form) `uplinkEndpoints` configuration option.',
|
|
444
444
|
);
|
|
445
445
|
});
|
|
446
|
-
|
|
447
|
-
it('warns with `experimental_pollInterval` option set', async () => {
|
|
448
|
-
new ApolloGateway({
|
|
449
|
-
experimental_pollInterval: 10000,
|
|
450
|
-
logger,
|
|
451
|
-
});
|
|
452
|
-
|
|
453
|
-
expect(logger.warn).toHaveBeenCalledWith(
|
|
454
|
-
'The `experimental_pollInterval` option is deprecated and will be removed in a future version of `@apollo/gateway`. Please migrate to the equivalent `pollIntervalInMs` configuration option.',
|
|
455
|
-
);
|
|
456
|
-
});
|
|
457
446
|
});
|
|
@@ -121,9 +121,10 @@ export function mockSupergraphSdlRequestSuccessIfAfter(
|
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
export function mockSupergraphSdlRequestIfAfterUnchanged(
|
|
124
|
-
|
|
124
|
+
ifAfter: string | null = null,
|
|
125
|
+
url: string = mockCloudConfigUrl1,
|
|
125
126
|
) {
|
|
126
|
-
return mockSupergraphSdlRequestIfAfter(ifAfter).reply(
|
|
127
|
+
return mockSupergraphSdlRequestIfAfter(ifAfter, url).reply(
|
|
127
128
|
200,
|
|
128
129
|
JSON.stringify({
|
|
129
130
|
data: {
|
|
@@ -6,7 +6,7 @@ expect.addSnapshotSerializer(astSerializer);
|
|
|
6
6
|
expect.addSnapshotSerializer(queryPlanSerializer);
|
|
7
7
|
it('supports passing additional fields defined by a requires', async () => {
|
|
8
8
|
const query = `#graphql
|
|
9
|
-
query
|
|
9
|
+
query GetReviewedBookNames {
|
|
10
10
|
me {
|
|
11
11
|
reviews {
|
|
12
12
|
product {
|
package/src/config.ts
CHANGED
|
@@ -81,6 +81,7 @@ export interface ServiceDefinitionUpdate {
|
|
|
81
81
|
export interface SupergraphSdlUpdate {
|
|
82
82
|
id: string;
|
|
83
83
|
supergraphSdl: string;
|
|
84
|
+
minDelaySeconds?: number;
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
export function isSupergraphSdlUpdate(
|
|
@@ -125,11 +126,6 @@ interface GatewayConfigBase {
|
|
|
125
126
|
// experimental observability callbacks
|
|
126
127
|
experimental_didResolveQueryPlan?: Experimental_DidResolveQueryPlanCallback;
|
|
127
128
|
experimental_didUpdateSupergraph?: Experimental_DidUpdateSupergraphCallback;
|
|
128
|
-
/**
|
|
129
|
-
* @deprecated use `pollIntervalInMs` instead
|
|
130
|
-
*/
|
|
131
|
-
experimental_pollInterval?: number;
|
|
132
|
-
pollIntervalInMs?: number;
|
|
133
129
|
experimental_approximateQueryPlanStoreMiB?: number;
|
|
134
130
|
experimental_autoFragmentization?: boolean;
|
|
135
131
|
fetcher?: typeof fetch;
|
|
@@ -150,6 +146,7 @@ export interface ServiceListGatewayConfig extends GatewayConfigBase {
|
|
|
150
146
|
| ((
|
|
151
147
|
service: ServiceEndpointDefinition,
|
|
152
148
|
) => Promise<HeadersInit> | HeadersInit);
|
|
149
|
+
pollIntervalInMs?: number;
|
|
153
150
|
}
|
|
154
151
|
|
|
155
152
|
export interface ManagedGatewayConfig extends GatewayConfigBase {
|
|
@@ -168,6 +165,11 @@ export interface ManagedGatewayConfig extends GatewayConfigBase {
|
|
|
168
165
|
*/
|
|
169
166
|
uplinkEndpoints?: string[];
|
|
170
167
|
uplinkMaxRetries?: number;
|
|
168
|
+
/**
|
|
169
|
+
* @deprecated use `fallbackPollIntervalInMs` instead
|
|
170
|
+
*/
|
|
171
|
+
pollIntervalInMs?: number;
|
|
172
|
+
fallbackPollIntervalInMs?: number;
|
|
171
173
|
}
|
|
172
174
|
|
|
173
175
|
// TODO(trevor:removeServiceList): migrate users to `supergraphSdl` function option
|
|
@@ -176,6 +178,7 @@ interface ManuallyManagedServiceDefsGatewayConfig extends GatewayConfigBase {
|
|
|
176
178
|
* @deprecated: use `supergraphSdl` instead (either as a `SupergraphSdlHook` or `SupergraphManager`)
|
|
177
179
|
*/
|
|
178
180
|
experimental_updateServiceDefinitions: Experimental_UpdateServiceDefinitions;
|
|
181
|
+
pollIntervalInMs?: number;
|
|
179
182
|
}
|
|
180
183
|
|
|
181
184
|
// TODO(trevor:removeServiceList): migrate users to `supergraphSdl` function option
|
|
@@ -185,6 +188,7 @@ interface ExperimentalManuallyManagedSupergraphSdlGatewayConfig
|
|
|
185
188
|
* @deprecated: use `supergraphSdl` instead (either as a `SupergraphSdlHook` or `SupergraphManager`)
|
|
186
189
|
*/
|
|
187
190
|
experimental_updateSupergraphSdl: Experimental_UpdateSupergraphSdl;
|
|
191
|
+
pollIntervalInMs?: number;
|
|
188
192
|
}
|
|
189
193
|
|
|
190
194
|
export function isManuallyManagedSupergraphSdlGatewayConfig(
|
|
@@ -238,7 +242,7 @@ type ManuallyManagedGatewayConfig =
|
|
|
238
242
|
| ManuallyManagedServiceDefsGatewayConfig
|
|
239
243
|
| ExperimentalManuallyManagedSupergraphSdlGatewayConfig
|
|
240
244
|
| ManuallyManagedSupergraphSdlGatewayConfig
|
|
241
|
-
// TODO(trevor:removeServiceList
|
|
245
|
+
// TODO(trevor:removeServiceList)
|
|
242
246
|
| ServiceListGatewayConfig;
|
|
243
247
|
|
|
244
248
|
// TODO(trevor:removeServiceList)
|
|
@@ -322,6 +326,7 @@ export function isManagedConfig(
|
|
|
322
326
|
return (
|
|
323
327
|
'schemaConfigDeliveryEndpoint' in config ||
|
|
324
328
|
'uplinkEndpoints' in config ||
|
|
329
|
+
'fallbackPollIntervalInMs' in config ||
|
|
325
330
|
(!isLocalConfig(config) &&
|
|
326
331
|
!isStaticSupergraphSdlConfig(config) &&
|
|
327
332
|
!isManuallyManagedConfig(config))
|
package/src/executeQueryPlan.ts
CHANGED
|
@@ -329,6 +329,7 @@ async function executeFetch<TContext>(
|
|
|
329
329
|
context,
|
|
330
330
|
fetch.operation,
|
|
331
331
|
variables,
|
|
332
|
+
fetch.operationName,
|
|
332
333
|
);
|
|
333
334
|
|
|
334
335
|
for (const entity of entities) {
|
|
@@ -364,6 +365,7 @@ async function executeFetch<TContext>(
|
|
|
364
365
|
context,
|
|
365
366
|
fetch.operation,
|
|
366
367
|
{...variables, representations},
|
|
368
|
+
fetch.operationName,
|
|
367
369
|
);
|
|
368
370
|
|
|
369
371
|
if (!dataReceivedFromService) {
|
|
@@ -405,6 +407,7 @@ async function executeFetch<TContext>(
|
|
|
405
407
|
context: ExecutionContext<TContext>,
|
|
406
408
|
source: string,
|
|
407
409
|
variables: Record<string, any>,
|
|
410
|
+
operationName: string | undefined,
|
|
408
411
|
): Promise<ResultMap | void | null> {
|
|
409
412
|
// We declare this as 'any' because it is missing url and method, which
|
|
410
413
|
// GraphQLRequest.http is supposed to have if it exists.
|
|
@@ -433,6 +436,7 @@ async function executeFetch<TContext>(
|
|
|
433
436
|
request: {
|
|
434
437
|
query: source,
|
|
435
438
|
variables,
|
|
439
|
+
operationName,
|
|
436
440
|
http,
|
|
437
441
|
},
|
|
438
442
|
incomingRequestContext: context.requestContext,
|
package/src/index.ts
CHANGED
|
@@ -202,8 +202,12 @@ export class ApolloGateway implements GraphQLService {
|
|
|
202
202
|
this.experimental_didUpdateSupergraph =
|
|
203
203
|
config?.experimental_didUpdateSupergraph;
|
|
204
204
|
|
|
205
|
-
this.
|
|
206
|
-
|
|
205
|
+
if (isManagedConfig(this.config)) {
|
|
206
|
+
this.pollIntervalInMs =
|
|
207
|
+
this.config.fallbackPollIntervalInMs ?? this.config.pollIntervalInMs;
|
|
208
|
+
} else if (isServiceListConfig(this.config)) {
|
|
209
|
+
this.pollIntervalInMs = this.config?.pollIntervalInMs;
|
|
210
|
+
}
|
|
207
211
|
|
|
208
212
|
this.issueConfigurationWarningsIfApplicable();
|
|
209
213
|
|
|
@@ -254,7 +258,7 @@ export class ApolloGateway implements GraphQLService {
|
|
|
254
258
|
'Polling Apollo services at a frequency of less than once per 10 ' +
|
|
255
259
|
'seconds (10000) is disallowed. Instead, the minimum allowed ' +
|
|
256
260
|
'pollInterval of 10000 will be used. Please reconfigure your ' +
|
|
257
|
-
'`
|
|
261
|
+
'`fallbackPollIntervalInMs` accordingly. If this is problematic for ' +
|
|
258
262
|
'your team, please contact support.',
|
|
259
263
|
);
|
|
260
264
|
}
|
|
@@ -288,9 +292,11 @@ export class ApolloGateway implements GraphQLService {
|
|
|
288
292
|
);
|
|
289
293
|
}
|
|
290
294
|
|
|
291
|
-
if ('
|
|
295
|
+
if (isManagedConfig(this.config) && 'pollIntervalInMs' in this.config) {
|
|
292
296
|
this.logger.warn(
|
|
293
|
-
'The `
|
|
297
|
+
'The `pollIntervalInMs` option is deprecated and will be removed in a future version of `@apollo/gateway`. ' +
|
|
298
|
+
'Please migrate to the equivalent `fallbackPollIntervalInMs` configuration option. ' +
|
|
299
|
+
'The poll interval is now defined by Uplink, this option will only be used if it is greater than the value defined by Uplink or as a fallback.',
|
|
294
300
|
);
|
|
295
301
|
}
|
|
296
302
|
}
|
|
@@ -400,11 +406,11 @@ export class ApolloGateway implements GraphQLService {
|
|
|
400
406
|
apiKey: this.apolloConfig!.key!,
|
|
401
407
|
uplinkEndpoints,
|
|
402
408
|
maxRetries:
|
|
403
|
-
this.config.uplinkMaxRetries ?? uplinkEndpoints.length * 3,
|
|
409
|
+
this.config.uplinkMaxRetries ?? uplinkEndpoints.length * 3 - 1, // -1 for the initial request
|
|
404
410
|
subgraphHealthCheck: this.config.serviceHealthCheck,
|
|
405
411
|
fetcher: this.fetcher,
|
|
406
412
|
logger: this.logger,
|
|
407
|
-
|
|
413
|
+
fallbackPollIntervalInMs: this.pollIntervalInMs ?? 10000,
|
|
408
414
|
}),
|
|
409
415
|
);
|
|
410
416
|
}
|
|
@@ -1144,3 +1150,6 @@ export {
|
|
|
1144
1150
|
SupergraphSdlHook,
|
|
1145
1151
|
SupergraphManager
|
|
1146
1152
|
} from './config';
|
|
1153
|
+
|
|
1154
|
+
export { UplinkFetcherError } from "./supergraphManagers"
|
|
1155
|
+
|
|
@@ -10,10 +10,10 @@ export interface GraphQLResolverMap<TContext = {}> {
|
|
|
10
10
|
[typeName: string]:
|
|
11
11
|
| {
|
|
12
12
|
[fieldName: string]:
|
|
13
|
-
| GraphQLFieldResolver<any, TContext>
|
|
13
|
+
| GraphQLFieldResolver<any, TContext, any>
|
|
14
14
|
| {
|
|
15
15
|
requires?: string;
|
|
16
|
-
resolve: GraphQLFieldResolver<any, TContext>;
|
|
16
|
+
resolve: GraphQLFieldResolver<any, TContext, any>;
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
19
|
| GraphQLScalarType
|
|
@@ -222,7 +222,7 @@ export class LegacyFetcher implements SupergraphManager {
|
|
|
222
222
|
|
|
223
223
|
private logUpdateFailure(e: any) {
|
|
224
224
|
this.config.logger?.error(
|
|
225
|
-
'
|
|
225
|
+
'LegacyFetcher failed to update supergraph with the following error: ' +
|
|
226
226
|
(e.message ?? e),
|
|
227
227
|
);
|
|
228
228
|
}
|
|
@@ -76,7 +76,7 @@ export class LocalCompose implements SupergraphManager {
|
|
|
76
76
|
|
|
77
77
|
private logUpdateFailure(e: any) {
|
|
78
78
|
this.config.logger?.error(
|
|
79
|
-
'
|
|
79
|
+
'LocalCompose failed to update supergraph with the following error: ' +
|
|
80
80
|
(e.message ?? e),
|
|
81
81
|
);
|
|
82
82
|
}
|