@apollo/gateway 2.0.0-alpha.1 → 2.0.0-alpha.2
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 +1 -1
- package/dist/__generated__/graphqlTypes.d.ts +13 -11
- package/dist/__generated__/graphqlTypes.d.ts.map +1 -1
- package/dist/__generated__/graphqlTypes.js.map +1 -1
- package/dist/config.d.ts +2 -9
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +6 -17
- package/dist/config.js.map +1 -1
- package/dist/datasources/types.d.ts +1 -1
- package/dist/datasources/types.d.ts.map +1 -1
- package/dist/executeQueryPlan.d.ts.map +1 -1
- package/dist/executeQueryPlan.js +4 -4
- package/dist/executeQueryPlan.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -28
- package/dist/index.js.map +1 -1
- package/dist/loadSupergraphSdlFromStorage.d.ts +4 -3
- package/dist/loadSupergraphSdlFromStorage.d.ts.map +1 -1
- package/dist/loadSupergraphSdlFromStorage.js +7 -3
- package/dist/loadSupergraphSdlFromStorage.js.map +1 -1
- package/dist/operationContext.js +0 -1
- package/dist/operationContext.js.map +1 -1
- package/dist/utilities/array.js +1 -1
- package/dist/utilities/array.js.map +1 -1
- package/package.json +8 -9
- package/src/__generated__/graphqlTypes.ts +13 -11
- package/src/__tests__/buildQueryPlan.test.ts +1 -1
- package/src/__tests__/executeQueryPlan.test.ts +572 -76
- package/src/__tests__/execution-utils.ts +2 -4
- package/src/__tests__/gateway/composedSdl.test.ts +1 -1
- package/src/__tests__/gateway/executor.test.ts +2 -0
- package/src/__tests__/gateway/lifecycle-hooks.test.ts +8 -4
- package/src/__tests__/gateway/opentelemetry.test.ts +1 -0
- package/src/__tests__/gateway/queryPlanCache.test.ts +3 -0
- package/src/__tests__/integration/aliases.test.ts +1 -0
- package/src/__tests__/integration/configuration.test.ts +3 -21
- package/src/__tests__/integration/logger.test.ts +1 -1
- package/src/__tests__/integration/networkRequests.test.ts +53 -28
- package/src/__tests__/integration/nockMocks.ts +33 -5
- package/src/__tests__/loadServicesFromRemoteEndpoint.test.ts +2 -2
- package/src/__tests__/loadSupergraphSdlFromStorage.test.ts +36 -6
- package/src/config.ts +8 -43
- package/src/core/__tests__/core.test.ts +6 -6
- package/src/datasources/types.ts +1 -1
- package/src/executeQueryPlan.ts +4 -7
- package/src/index.ts +22 -50
- package/src/loadServicesFromRemoteEndpoint.ts +1 -1
- package/src/loadSupergraphSdlFromStorage.ts +7 -4
- package/src/make-fetch-happen.d.ts +1 -1
- package/src/operationContext.ts +2 -2
- package/src/utilities/__tests__/cleanErrorOfInaccessibleElements.test.ts +1 -1
- package/src/utilities/array.ts +1 -1
- package/CHANGELOG.md +0 -452
- package/dist/legacyLoadServicesFromStorage.d.ts +0 -20
- package/dist/legacyLoadServicesFromStorage.d.ts.map +0 -1
- package/dist/legacyLoadServicesFromStorage.js +0 -62
- package/dist/legacyLoadServicesFromStorage.js.map +0 -1
- package/src/__tests__/integration/legacyNetworkRequests.test.ts +0 -279
- package/src/__tests__/integration/legacyNockMocks.ts +0 -113
- package/src/legacyLoadServicesFromStorage.ts +0 -170
|
@@ -4,9 +4,6 @@ import {
|
|
|
4
4
|
GraphQLResolverMap,
|
|
5
5
|
} from 'apollo-graphql';
|
|
6
6
|
import { GraphQLRequest, GraphQLExecutionResult, Logger } from 'apollo-server-types';
|
|
7
|
-
import {
|
|
8
|
-
ServiceDefinition,
|
|
9
|
-
} from '@apollo/federation';
|
|
10
7
|
import { buildSubgraphSchema } from '@apollo/subgraph';
|
|
11
8
|
import {
|
|
12
9
|
executeQueryPlan,
|
|
@@ -20,7 +17,7 @@ import { queryPlanSerializer, astSerializer } from 'apollo-federation-integratio
|
|
|
20
17
|
import gql from 'graphql-tag';
|
|
21
18
|
import { fixtures } from 'apollo-federation-integration-testsuite';
|
|
22
19
|
import { composeServices } from '@apollo/composition';
|
|
23
|
-
import { buildSchema, operationFromDocument } from '@apollo/federation-internals';
|
|
20
|
+
import { buildSchema, operationFromDocument, ServiceDefinition } from '@apollo/federation-internals';
|
|
24
21
|
|
|
25
22
|
const prettyFormat = require('pretty-format');
|
|
26
23
|
|
|
@@ -67,6 +64,7 @@ export async function execute(
|
|
|
67
64
|
const result = await executeQueryPlan(
|
|
68
65
|
queryPlan,
|
|
69
66
|
serviceMap,
|
|
67
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
70
68
|
// @ts-ignore
|
|
71
69
|
{
|
|
72
70
|
cache: undefined as any,
|
|
@@ -35,7 +35,7 @@ describe('Using supergraphSdl configuration', () => {
|
|
|
35
35
|
`);
|
|
36
36
|
|
|
37
37
|
const [url, request] = fetch.mock.calls[0];
|
|
38
|
-
expect(url).toEqual('https://accounts.api.com');
|
|
38
|
+
expect(url).toEqual('https://accounts.api.com.invalid');
|
|
39
39
|
expect(request?.body).toEqual(
|
|
40
40
|
JSON.stringify({ query: '{me{username}}', variables: {} }),
|
|
41
41
|
);
|
|
@@ -38,6 +38,7 @@ describe('ApolloGateway executor', () => {
|
|
|
38
38
|
}
|
|
39
39
|
`;
|
|
40
40
|
|
|
41
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
41
42
|
// @ts-ignore
|
|
42
43
|
const { errors } = await executor({
|
|
43
44
|
source,
|
|
@@ -73,6 +74,7 @@ describe('ApolloGateway executor', () => {
|
|
|
73
74
|
}
|
|
74
75
|
`;
|
|
75
76
|
|
|
77
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
76
78
|
// @ts-ignore
|
|
77
79
|
const { errors, data } = await executor({
|
|
78
80
|
source,
|
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
} from 'apollo-federation-integration-testsuite';
|
|
15
15
|
import { Logger } from 'apollo-server-types';
|
|
16
16
|
|
|
17
|
+
type GenericFunction = (...args: unknown[]) => unknown;
|
|
18
|
+
|
|
17
19
|
// The order of this was specified to preserve existing test coverage. Typically
|
|
18
20
|
// we would just import and use the `fixtures` array.
|
|
19
21
|
const serviceDefinitions = [
|
|
@@ -115,7 +117,7 @@ describe('lifecycle hooks', () => {
|
|
|
115
117
|
const callbackArgs = experimental_didFailComposition.mock.calls[0][0];
|
|
116
118
|
expect(callbackArgs.serviceList).toHaveLength(2);
|
|
117
119
|
expect(callbackArgs.errors[0]).toMatchInlineSnapshot(
|
|
118
|
-
`[GraphQLError: Field "T.a" has incompatible types
|
|
120
|
+
`[GraphQLError: Field "T.a" has incompatible types across subgraphs: it has type "Int" in subgraph "S1" but type "String" in subgraph "S2"]`,
|
|
119
121
|
);
|
|
120
122
|
expect(callbackArgs.compositionMetadata.id).toEqual('abc');
|
|
121
123
|
expect(experimental_didFailComposition).toBeCalled();
|
|
@@ -167,11 +169,12 @@ describe('lifecycle hooks', () => {
|
|
|
167
169
|
experimental_didUpdateComposition: mockDidUpdate,
|
|
168
170
|
logger,
|
|
169
171
|
});
|
|
172
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
170
173
|
// @ts-ignore for testing purposes, a short pollInterval is ideal so we'll override here
|
|
171
174
|
gateway.experimental_pollInterval = 100;
|
|
172
175
|
|
|
173
|
-
let resolve1:
|
|
174
|
-
let resolve2:
|
|
176
|
+
let resolve1: GenericFunction;
|
|
177
|
+
let resolve2: GenericFunction;
|
|
175
178
|
const schemaChangeBlocker1 = new Promise(res => (resolve1 = res));
|
|
176
179
|
const schemaChangeBlocker2 = new Promise(res => (resolve2 = res));
|
|
177
180
|
|
|
@@ -251,7 +254,7 @@ describe('lifecycle hooks', () => {
|
|
|
251
254
|
logger,
|
|
252
255
|
});
|
|
253
256
|
|
|
254
|
-
let resolve:
|
|
257
|
+
let resolve: GenericFunction;
|
|
255
258
|
const schemaChangeBlocker = new Promise(res => (resolve = res));
|
|
256
259
|
const schemaChangeCallback = jest.fn(() => resolve());
|
|
257
260
|
|
|
@@ -280,6 +283,7 @@ describe('lifecycle hooks', () => {
|
|
|
280
283
|
{ book(isbn: "0262510871") { year } }
|
|
281
284
|
`;
|
|
282
285
|
|
|
286
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
283
287
|
// @ts-ignore
|
|
284
288
|
await executor({
|
|
285
289
|
source,
|
|
@@ -39,6 +39,7 @@ describe('opentelemetry', () => {
|
|
|
39
39
|
const gateway = new ApolloGateway({
|
|
40
40
|
localServiceList: fixtures,
|
|
41
41
|
buildService: service => {
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
42
43
|
// @ts-ignore
|
|
43
44
|
return new LocalGraphQLDataSource(buildSubgraphSchema([service]));
|
|
44
45
|
},
|
|
@@ -12,6 +12,7 @@ it('caches the query plan for a request', async () => {
|
|
|
12
12
|
const gateway = new ApolloGateway({
|
|
13
13
|
localServiceList: fixtures,
|
|
14
14
|
buildService: service => {
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
15
16
|
// @ts-ignore
|
|
16
17
|
return new LocalGraphQLDataSource(buildSubgraphSchema([service]));
|
|
17
18
|
},
|
|
@@ -68,6 +69,7 @@ it('supports multiple operations and operationName', async () => {
|
|
|
68
69
|
const gateway = new ApolloGateway({
|
|
69
70
|
localServiceList: fixtures,
|
|
70
71
|
buildService: service => {
|
|
72
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
71
73
|
// @ts-ignore
|
|
72
74
|
return new LocalGraphQLDataSource(buildSubgraphSchema([service]));
|
|
73
75
|
},
|
|
@@ -173,6 +175,7 @@ it('does not corrupt cached queryplan data across requests', async () => {
|
|
|
173
175
|
const gateway = new ApolloGateway({
|
|
174
176
|
localServiceList: [serviceA, serviceB],
|
|
175
177
|
buildService: service => {
|
|
178
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
176
179
|
// @ts-ignore
|
|
177
180
|
return new LocalGraphQLDataSource(buildSubgraphSchema([service]));
|
|
178
181
|
},
|
|
@@ -144,6 +144,7 @@ it('supports aliases when using ApolloServer', async () => {
|
|
|
144
144
|
const gateway = new ApolloGateway({
|
|
145
145
|
localServiceList: fixtures,
|
|
146
146
|
buildService: service => {
|
|
147
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
147
148
|
// @ts-ignore
|
|
148
149
|
return new LocalGraphQLDataSource(buildSubgraphSchema([service]));
|
|
149
150
|
},
|
|
@@ -70,9 +70,10 @@ describe('gateway configuration warnings', () => {
|
|
|
70
70
|
|
|
71
71
|
it('warns when both manual update configurations are provided', async () => {
|
|
72
72
|
gateway = new ApolloGateway({
|
|
73
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
73
74
|
// @ts-ignore
|
|
74
|
-
async
|
|
75
|
-
async
|
|
75
|
+
experimental_updateSupergraphSdl: async () => undefined,
|
|
76
|
+
experimental_updateServiceDefinitions: async () => undefined,
|
|
76
77
|
logger,
|
|
77
78
|
});
|
|
78
79
|
|
|
@@ -110,7 +111,6 @@ describe('gateway configuration warnings', () => {
|
|
|
110
111
|
|
|
111
112
|
gateway = new ApolloGateway({
|
|
112
113
|
logger,
|
|
113
|
-
// TODO(trevor:cloudconfig): remove
|
|
114
114
|
schemaConfigDeliveryEndpoint: mockCloudConfigUrl,
|
|
115
115
|
});
|
|
116
116
|
|
|
@@ -300,7 +300,6 @@ describe('gateway config / env behavior', () => {
|
|
|
300
300
|
});
|
|
301
301
|
});
|
|
302
302
|
|
|
303
|
-
// TODO(trevor:cloudconfig): this behavior will be updated
|
|
304
303
|
describe('schema config delivery endpoint configuration', () => {
|
|
305
304
|
it('A code config overrides the env variable', async () => {
|
|
306
305
|
cleanUp = mockedEnv({
|
|
@@ -318,22 +317,5 @@ describe('gateway config / env behavior', () => {
|
|
|
318
317
|
|
|
319
318
|
gateway = null;
|
|
320
319
|
});
|
|
321
|
-
|
|
322
|
-
it('A code config set to `null` takes precedence over an existing env variable', async () => {
|
|
323
|
-
cleanUp = mockedEnv({
|
|
324
|
-
APOLLO_SCHEMA_CONFIG_DELIVERY_ENDPOINT: 'env-config',
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
gateway = new ApolloGateway({
|
|
328
|
-
logger,
|
|
329
|
-
schemaConfigDeliveryEndpoint: null,
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
expect(gateway['schemaConfigDeliveryEndpoint']).toEqual(
|
|
333
|
-
null,
|
|
334
|
-
);
|
|
335
|
-
|
|
336
|
-
gateway = null;
|
|
337
|
-
});
|
|
338
320
|
});
|
|
339
321
|
});
|
|
@@ -18,7 +18,7 @@ async function triggerKnownDebugMessage(logger: Logger) {
|
|
|
18
18
|
// message outside of the constructor, but it seemed worth testing
|
|
19
19
|
// the compatibility with `ApolloGateway` itself rather than generically.
|
|
20
20
|
// The error does not matter, so it is caught and ignored.
|
|
21
|
-
await new ApolloGateway({ logger }).load().catch(_e =>
|
|
21
|
+
await new ApolloGateway({ logger }).load().catch(_e => undefined);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
describe("logger", () => {
|
|
@@ -13,6 +13,8 @@ import {
|
|
|
13
13
|
mockSupergraphSdlRequest,
|
|
14
14
|
mockApolloConfig,
|
|
15
15
|
mockCloudConfigUrl,
|
|
16
|
+
mockSupergraphSdlRequestIfAfter,
|
|
17
|
+
mockSupergraphSdlRequestSuccessIfAfter,
|
|
16
18
|
} from './nockMocks';
|
|
17
19
|
import {
|
|
18
20
|
accounts,
|
|
@@ -25,6 +27,7 @@ import {
|
|
|
25
27
|
} from 'apollo-federation-integration-testsuite';
|
|
26
28
|
import { getTestingSupergraphSdl } from '../execution-utils';
|
|
27
29
|
|
|
30
|
+
type GenericFunction = (...args: unknown[]) => unknown;
|
|
28
31
|
export interface MockService {
|
|
29
32
|
name: string;
|
|
30
33
|
url: string;
|
|
@@ -98,7 +101,6 @@ it('Queries remote endpoints for their SDLs', async () => {
|
|
|
98
101
|
expect(gateway.schema!.getType('User')!.description).toBe('This is my User');
|
|
99
102
|
});
|
|
100
103
|
|
|
101
|
-
// TODO(trevor:cloudconfig): Remove all usages of the experimental config option
|
|
102
104
|
it('Fetches Supergraph SDL from remote storage', async () => {
|
|
103
105
|
mockSupergraphSdlRequestSuccess();
|
|
104
106
|
|
|
@@ -112,7 +114,6 @@ it('Fetches Supergraph SDL from remote storage', async () => {
|
|
|
112
114
|
expect(gateway.schema?.getType('User')).toBeTruthy();
|
|
113
115
|
});
|
|
114
116
|
|
|
115
|
-
// TODO(trevor:cloudconfig): This test should evolve to demonstrate overriding the default in the future
|
|
116
117
|
it('Fetches Supergraph SDL from remote storage using a configured env variable', async () => {
|
|
117
118
|
cleanUp = mockedEnv({
|
|
118
119
|
APOLLO_SCHEMA_CONFIG_DELIVERY_ENDPOINT: mockCloudConfigUrl,
|
|
@@ -130,15 +131,19 @@ it('Fetches Supergraph SDL from remote storage using a configured env variable',
|
|
|
130
131
|
|
|
131
132
|
it('Updates Supergraph SDL from remote storage', async () => {
|
|
132
133
|
mockSupergraphSdlRequestSuccess();
|
|
133
|
-
|
|
134
|
+
mockSupergraphSdlRequestSuccessIfAfter(
|
|
135
|
+
'originalId-1234',
|
|
136
|
+
'updatedId-5678',
|
|
137
|
+
getTestingSupergraphSdl(fixturesWithUpdate),
|
|
138
|
+
);
|
|
134
139
|
|
|
135
140
|
// This test is only interested in the second time the gateway notifies of an
|
|
136
141
|
// update, since the first happens on load.
|
|
137
|
-
let secondUpdateResolve:
|
|
142
|
+
let secondUpdateResolve: GenericFunction;
|
|
138
143
|
const secondUpdate = new Promise((res) => (secondUpdateResolve = res));
|
|
139
144
|
const schemaChangeCallback = jest
|
|
140
145
|
.fn()
|
|
141
|
-
.mockImplementationOnce(() =>
|
|
146
|
+
.mockImplementationOnce(() => undefined)
|
|
142
147
|
.mockImplementationOnce(() => {
|
|
143
148
|
secondUpdateResolve();
|
|
144
149
|
});
|
|
@@ -147,9 +152,10 @@ it('Updates Supergraph SDL from remote storage', async () => {
|
|
|
147
152
|
logger,
|
|
148
153
|
schemaConfigDeliveryEndpoint: mockCloudConfigUrl,
|
|
149
154
|
});
|
|
155
|
+
// eslint-disable-next-line
|
|
150
156
|
// @ts-ignore for testing purposes, a short pollInterval is ideal so we'll override here
|
|
151
157
|
gateway.experimental_pollInterval = 100;
|
|
152
|
-
gateway.
|
|
158
|
+
gateway.onSchemaLoadOrUpdate(schemaChangeCallback);
|
|
153
159
|
|
|
154
160
|
await gateway.load(mockApolloConfig);
|
|
155
161
|
expect(gateway['compositionId']).toMatchInlineSnapshot(`"originalId-1234"`);
|
|
@@ -183,10 +189,10 @@ describe('Supergraph SDL update failures', () => {
|
|
|
183
189
|
|
|
184
190
|
it('Handles arbitrary fetch failures (non 200 response)', async () => {
|
|
185
191
|
mockSupergraphSdlRequestSuccess();
|
|
186
|
-
|
|
192
|
+
mockSupergraphSdlRequestIfAfter('originalId-1234').reply(500);
|
|
187
193
|
|
|
188
194
|
// Spy on logger.error so we can just await once it's been called
|
|
189
|
-
let errorLogged:
|
|
195
|
+
let errorLogged: GenericFunction;
|
|
190
196
|
const errorLoggedPromise = new Promise((r) => (errorLogged = r));
|
|
191
197
|
logger.error = jest.fn(() => errorLogged());
|
|
192
198
|
|
|
@@ -195,6 +201,7 @@ describe('Supergraph SDL update failures', () => {
|
|
|
195
201
|
schemaConfigDeliveryEndpoint: mockCloudConfigUrl,
|
|
196
202
|
});
|
|
197
203
|
|
|
204
|
+
// eslint-disable-next-line
|
|
198
205
|
// @ts-ignore for testing purposes, a short pollInterval is ideal so we'll override here
|
|
199
206
|
gateway.experimental_pollInterval = 100;
|
|
200
207
|
|
|
@@ -208,7 +215,7 @@ describe('Supergraph SDL update failures', () => {
|
|
|
208
215
|
|
|
209
216
|
it('Handles GraphQL errors', async () => {
|
|
210
217
|
mockSupergraphSdlRequestSuccess();
|
|
211
|
-
mockSupergraphSdlRequest().reply(200, {
|
|
218
|
+
mockSupergraphSdlRequest('originalId-1234').reply(200, {
|
|
212
219
|
errors: [
|
|
213
220
|
{
|
|
214
221
|
message: 'Cannot query field "fail" on type "Query".',
|
|
@@ -219,7 +226,7 @@ describe('Supergraph SDL update failures', () => {
|
|
|
219
226
|
});
|
|
220
227
|
|
|
221
228
|
// Spy on logger.error so we can just await once it's been called
|
|
222
|
-
let errorLogged:
|
|
229
|
+
let errorLogged: GenericFunction;
|
|
223
230
|
const errorLoggedPromise = new Promise((r) => (errorLogged = r));
|
|
224
231
|
logger.error = jest.fn(() => errorLogged());
|
|
225
232
|
|
|
@@ -227,6 +234,7 @@ describe('Supergraph SDL update failures', () => {
|
|
|
227
234
|
logger,
|
|
228
235
|
schemaConfigDeliveryEndpoint: mockCloudConfigUrl,
|
|
229
236
|
});
|
|
237
|
+
// eslint-disable-next-line
|
|
230
238
|
// @ts-ignore for testing purposes, a short pollInterval is ideal so we'll override here
|
|
231
239
|
gateway.experimental_pollInterval = 100;
|
|
232
240
|
|
|
@@ -242,7 +250,7 @@ describe('Supergraph SDL update failures', () => {
|
|
|
242
250
|
|
|
243
251
|
it("Doesn't update and logs on receiving unparseable Supergraph SDL", async () => {
|
|
244
252
|
mockSupergraphSdlRequestSuccess();
|
|
245
|
-
|
|
253
|
+
mockSupergraphSdlRequestIfAfter('originalId-1234').reply(
|
|
246
254
|
200,
|
|
247
255
|
JSON.stringify({
|
|
248
256
|
data: {
|
|
@@ -256,7 +264,7 @@ describe('Supergraph SDL update failures', () => {
|
|
|
256
264
|
);
|
|
257
265
|
|
|
258
266
|
// Spy on logger.error so we can just await once it's been called
|
|
259
|
-
let errorLogged:
|
|
267
|
+
let errorLogged: GenericFunction;
|
|
260
268
|
const errorLoggedPromise = new Promise((r) => (errorLogged = r));
|
|
261
269
|
logger.error = jest.fn(() => errorLogged());
|
|
262
270
|
|
|
@@ -264,6 +272,7 @@ describe('Supergraph SDL update failures', () => {
|
|
|
264
272
|
logger,
|
|
265
273
|
schemaConfigDeliveryEndpoint: mockCloudConfigUrl,
|
|
266
274
|
});
|
|
275
|
+
// eslint-disable-next-line
|
|
267
276
|
// @ts-ignore for testing purposes, a short pollInterval is ideal so we'll override here
|
|
268
277
|
gateway.experimental_pollInterval = 100;
|
|
269
278
|
|
|
@@ -294,6 +303,7 @@ describe('Supergraph SDL update failures', () => {
|
|
|
294
303
|
logger,
|
|
295
304
|
schemaConfigDeliveryEndpoint: mockCloudConfigUrl,
|
|
296
305
|
});
|
|
306
|
+
// eslint-disable-next-line
|
|
297
307
|
// @ts-ignore for testing purposes, a short pollInterval is ideal so we'll override here
|
|
298
308
|
gateway.experimental_pollInterval = 100;
|
|
299
309
|
|
|
@@ -314,12 +324,16 @@ describe('Supergraph SDL update failures', () => {
|
|
|
314
324
|
it('Rollsback to a previous schema when triggered', async () => {
|
|
315
325
|
// Init
|
|
316
326
|
mockSupergraphSdlRequestSuccess();
|
|
317
|
-
|
|
318
|
-
|
|
327
|
+
mockSupergraphSdlRequestSuccessIfAfter(
|
|
328
|
+
'originalId-1234',
|
|
329
|
+
'updatedId-5678',
|
|
330
|
+
getTestingSupergraphSdl(fixturesWithUpdate),
|
|
331
|
+
);
|
|
332
|
+
mockSupergraphSdlRequestSuccessIfAfter('updatedId-5678');
|
|
319
333
|
|
|
320
|
-
let firstResolve:
|
|
321
|
-
let secondResolve:
|
|
322
|
-
let thirdResolve:
|
|
334
|
+
let firstResolve: GenericFunction;
|
|
335
|
+
let secondResolve: GenericFunction;
|
|
336
|
+
let thirdResolve: GenericFunction;
|
|
323
337
|
const firstSchemaChangeBlocker = new Promise((res) => (firstResolve = res));
|
|
324
338
|
const secondSchemaChangeBlocker = new Promise((res) => (secondResolve = res));
|
|
325
339
|
const thirdSchemaChangeBlocker = new Promise((res) => (thirdResolve = res));
|
|
@@ -334,6 +348,7 @@ it('Rollsback to a previous schema when triggered', async () => {
|
|
|
334
348
|
logger,
|
|
335
349
|
schemaConfigDeliveryEndpoint: mockCloudConfigUrl,
|
|
336
350
|
});
|
|
351
|
+
// eslint-disable-next-line
|
|
337
352
|
// @ts-ignore for testing purposes, a short pollInterval is ideal so we'll override here
|
|
338
353
|
gateway.experimental_pollInterval = 100;
|
|
339
354
|
|
|
@@ -386,10 +401,11 @@ describe('Downstream service health checks', () => {
|
|
|
386
401
|
// [accounts] Account -> A @key selects id, but Account.id could not be found"
|
|
387
402
|
// `);
|
|
388
403
|
// Instead we'll just use the regular snapshot matcher...
|
|
404
|
+
let err;
|
|
389
405
|
try {
|
|
390
406
|
await gateway.load(mockApolloConfig);
|
|
391
407
|
} catch (e) {
|
|
392
|
-
|
|
408
|
+
err = e;
|
|
393
409
|
}
|
|
394
410
|
|
|
395
411
|
// TODO: smell that we should be awaiting something else
|
|
@@ -418,6 +434,7 @@ describe('Downstream service health checks', () => {
|
|
|
418
434
|
logger,
|
|
419
435
|
schemaConfigDeliveryEndpoint: mockCloudConfigUrl,
|
|
420
436
|
});
|
|
437
|
+
// eslint-disable-next-line
|
|
421
438
|
// @ts-ignore for testing purposes, a short pollInterval is ideal so we'll override here
|
|
422
439
|
gateway.experimental_pollInterval = 100;
|
|
423
440
|
|
|
@@ -450,10 +467,11 @@ describe('Downstream service health checks', () => {
|
|
|
450
467
|
// [accounts] Account -> A @key selects id, but Account.id could not be found"
|
|
451
468
|
// `);
|
|
452
469
|
// Instead we'll just use the regular snapshot matcher...
|
|
470
|
+
let err;
|
|
453
471
|
try {
|
|
454
472
|
await gateway.load(mockApolloConfig);
|
|
455
473
|
} catch (e) {
|
|
456
|
-
|
|
474
|
+
err = e;
|
|
457
475
|
}
|
|
458
476
|
|
|
459
477
|
// TODO: smell that we should be awaiting something else
|
|
@@ -480,14 +498,15 @@ describe('Downstream service health checks', () => {
|
|
|
480
498
|
mockAllServicesHealthCheckSuccess();
|
|
481
499
|
|
|
482
500
|
// Update
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
501
|
+
mockSupergraphSdlRequestSuccessIfAfter(
|
|
502
|
+
'originalId-1234',
|
|
503
|
+
'updatedId-5678',
|
|
504
|
+
getTestingSupergraphSdl(fixturesWithUpdate),
|
|
486
505
|
);
|
|
487
506
|
mockAllServicesHealthCheckSuccess();
|
|
488
507
|
|
|
489
|
-
let resolve1:
|
|
490
|
-
let resolve2:
|
|
508
|
+
let resolve1: GenericFunction;
|
|
509
|
+
let resolve2: GenericFunction;
|
|
491
510
|
const schemaChangeBlocker1 = new Promise((res) => (resolve1 = res));
|
|
492
511
|
const schemaChangeBlocker2 = new Promise((res) => (resolve2 = res));
|
|
493
512
|
const onChange = jest
|
|
@@ -500,6 +519,7 @@ describe('Downstream service health checks', () => {
|
|
|
500
519
|
logger,
|
|
501
520
|
schemaConfigDeliveryEndpoint: mockCloudConfigUrl,
|
|
502
521
|
});
|
|
522
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
503
523
|
// @ts-ignore for testing purposes, a short pollInterval is ideal so we'll override here
|
|
504
524
|
gateway.experimental_pollInterval = 100;
|
|
505
525
|
|
|
@@ -523,9 +543,10 @@ describe('Downstream service health checks', () => {
|
|
|
523
543
|
mockAllServicesHealthCheckSuccess();
|
|
524
544
|
|
|
525
545
|
// Update (with one health check failure)
|
|
526
|
-
|
|
546
|
+
mockSupergraphSdlRequestSuccessIfAfter(
|
|
547
|
+
'originalId-1234',
|
|
548
|
+
'updatedId-5678',
|
|
527
549
|
getTestingSupergraphSdl(fixturesWithUpdate),
|
|
528
|
-
'updatedId-5678',
|
|
529
550
|
);
|
|
530
551
|
mockServiceHealthCheck(accounts).reply(500);
|
|
531
552
|
mockServiceHealthCheckSuccess(books);
|
|
@@ -534,7 +555,7 @@ describe('Downstream service health checks', () => {
|
|
|
534
555
|
mockServiceHealthCheckSuccess(reviews);
|
|
535
556
|
mockServiceHealthCheckSuccess(documents);
|
|
536
557
|
|
|
537
|
-
let resolve:
|
|
558
|
+
let resolve: GenericFunction;
|
|
538
559
|
const schemaChangeBlocker = new Promise((res) => (resolve = res));
|
|
539
560
|
|
|
540
561
|
gateway = new ApolloGateway({
|
|
@@ -542,9 +563,11 @@ describe('Downstream service health checks', () => {
|
|
|
542
563
|
logger,
|
|
543
564
|
schemaConfigDeliveryEndpoint: mockCloudConfigUrl,
|
|
544
565
|
});
|
|
566
|
+
// eslint-disable-next-line
|
|
545
567
|
// @ts-ignore for testing purposes, a short pollInterval is ideal so we'll override here
|
|
546
568
|
gateway.experimental_pollInterval = 100;
|
|
547
569
|
|
|
570
|
+
// eslint-disable-next-line
|
|
548
571
|
// @ts-ignore for testing purposes, we'll call the original `updateSchema`
|
|
549
572
|
// function from our mock. The first call should mimic original behavior,
|
|
550
573
|
// but the second call needs to handle the PromiseRejection. Typically for tests
|
|
@@ -568,10 +591,11 @@ describe('Downstream service health checks', () => {
|
|
|
568
591
|
// [accounts]: 500: Internal Server Error"
|
|
569
592
|
// `);
|
|
570
593
|
// Instead we'll just use the regular snapshot matcher...
|
|
594
|
+
let err;
|
|
571
595
|
try {
|
|
572
596
|
await original.apply(gateway);
|
|
573
597
|
} catch (e) {
|
|
574
|
-
|
|
598
|
+
err = e;
|
|
575
599
|
}
|
|
576
600
|
|
|
577
601
|
expect(err.message).toMatchInlineSnapshot(`
|
|
@@ -582,6 +606,7 @@ describe('Downstream service health checks', () => {
|
|
|
582
606
|
resolve();
|
|
583
607
|
});
|
|
584
608
|
|
|
609
|
+
// eslint-disable-next-line
|
|
585
610
|
// @ts-ignore for testing purposes, replace the `updateSchema`
|
|
586
611
|
// function on the gateway with our mock
|
|
587
612
|
gateway.updateSchema = mockUpdateSchema;
|
|
@@ -70,21 +70,30 @@ export const mockCloudConfigUrl =
|
|
|
70
70
|
export const mockOutOfBandReporterUrl =
|
|
71
71
|
'https://example.outofbandreporter.com/monitoring/';
|
|
72
72
|
|
|
73
|
-
export function
|
|
73
|
+
export function mockSupergraphSdlRequestIfAfter(ifAfter: string | null) {
|
|
74
74
|
return gatewayNock(mockCloudConfigUrl).post('/', {
|
|
75
75
|
query: SUPERGRAPH_SDL_QUERY,
|
|
76
76
|
variables: {
|
|
77
77
|
ref: graphRef,
|
|
78
78
|
apiKey: apiKey,
|
|
79
|
+
ifAfterId: ifAfter,
|
|
79
80
|
},
|
|
80
81
|
});
|
|
81
82
|
}
|
|
82
83
|
|
|
83
|
-
export function
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
export function mockSupergraphSdlRequest(ifAfter: string | null = null) {
|
|
85
|
+
return mockSupergraphSdlRequestIfAfter(ifAfter);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function mockSupergraphSdlRequestSuccessIfAfter(
|
|
89
|
+
ifAfter: string | null = null,
|
|
90
|
+
id: string = 'originalId-1234',
|
|
91
|
+
supergraphSdl: string = getTestingSupergraphSdl(),
|
|
86
92
|
) {
|
|
87
|
-
|
|
93
|
+
if (supergraphSdl == null) {
|
|
94
|
+
supergraphSdl = getTestingSupergraphSdl();
|
|
95
|
+
}
|
|
96
|
+
return mockSupergraphSdlRequestIfAfter(ifAfter).reply(
|
|
88
97
|
200,
|
|
89
98
|
JSON.stringify({
|
|
90
99
|
data: {
|
|
@@ -98,6 +107,25 @@ export function mockSupergraphSdlRequestSuccess(
|
|
|
98
107
|
);
|
|
99
108
|
}
|
|
100
109
|
|
|
110
|
+
export function mockSupergraphSdlRequestIfAfterUnchanged(
|
|
111
|
+
ifAfter: string | null = null,
|
|
112
|
+
) {
|
|
113
|
+
return mockSupergraphSdlRequestIfAfter(ifAfter).reply(
|
|
114
|
+
200,
|
|
115
|
+
JSON.stringify({
|
|
116
|
+
data: {
|
|
117
|
+
routerConfig: {
|
|
118
|
+
__typename: 'Unchanged',
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
}),
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function mockSupergraphSdlRequestSuccess() {
|
|
126
|
+
return mockSupergraphSdlRequestSuccessIfAfter(null);
|
|
127
|
+
}
|
|
128
|
+
|
|
101
129
|
export function mockOutOfBandReportRequest() {
|
|
102
130
|
return gatewayNock(mockOutOfBandReporterUrl).post('/', () => true);
|
|
103
131
|
}
|
|
@@ -19,7 +19,7 @@ describe('getServiceDefinitionsFromRemoteEndpoint', () => {
|
|
|
19
19
|
|
|
20
20
|
it('throws when the downstream service returns errors', async () => {
|
|
21
21
|
const serviceSdlCache = new Map<string, string>();
|
|
22
|
-
const host = 'http://host-which-better-not-resolve';
|
|
22
|
+
const host = 'http://host-which-better-not-resolve.invalid';
|
|
23
23
|
const url = host + '/graphql';
|
|
24
24
|
|
|
25
25
|
const dataSource = new RemoteGraphQLDataSource({ url });
|
|
@@ -34,7 +34,7 @@ describe('getServiceDefinitionsFromRemoteEndpoint', () => {
|
|
|
34
34
|
getServiceIntrospectionHeaders: async () => ({}),
|
|
35
35
|
}),
|
|
36
36
|
).rejects.toThrowError(
|
|
37
|
-
/^Couldn't load service definitions for "test" at http:\/\/host-which-better-not-resolve\/graphql: request to http:\/\/host-which-better-not-resolve\/graphql failed, reason: getaddrinfo (ENOTFOUND|EAI_AGAIN)/,
|
|
37
|
+
/^Couldn't load service definitions for "test" at http:\/\/host-which-better-not-resolve.invalid\/graphql: request to http:\/\/host-which-better-not-resolve.invalid\/graphql failed, reason: getaddrinfo (ENOTFOUND|EAI_AGAIN)/,
|
|
38
38
|
);
|
|
39
39
|
});
|
|
40
40
|
});
|