@apollo/gateway 0.45.0 → 0.47.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/dist/config.d.ts +42 -16
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +28 -18
- package/dist/config.js.map +1 -1
- package/dist/datasources/LocalGraphQLDataSource.js.map +1 -1
- package/dist/datasources/RemoteGraphQLDataSource.d.ts.map +1 -1
- package/dist/datasources/RemoteGraphQLDataSource.js +4 -1
- package/dist/datasources/RemoteGraphQLDataSource.js.map +1 -1
- package/dist/executeQueryPlan.d.ts.map +1 -1
- package/dist/executeQueryPlan.js +2 -2
- package/dist/executeQueryPlan.js.map +1 -1
- package/dist/index.d.ts +35 -23
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +205 -308
- package/dist/index.js.map +1 -1
- package/dist/schema-helper/addResolversToSchema.d.ts +4 -0
- package/dist/schema-helper/addResolversToSchema.d.ts.map +1 -0
- package/dist/schema-helper/addResolversToSchema.js +62 -0
- package/dist/schema-helper/addResolversToSchema.js.map +1 -0
- package/dist/schema-helper/error.d.ts +6 -0
- package/dist/schema-helper/error.d.ts.map +1 -0
- package/dist/schema-helper/error.js +14 -0
- package/dist/schema-helper/error.js.map +1 -0
- package/dist/schema-helper/index.d.ts +4 -0
- package/dist/schema-helper/index.d.ts.map +1 -0
- package/dist/schema-helper/index.js +16 -0
- package/dist/schema-helper/index.js.map +1 -0
- package/dist/schema-helper/resolverMap.d.ts +16 -0
- package/dist/schema-helper/resolverMap.d.ts.map +1 -0
- package/dist/schema-helper/resolverMap.js +3 -0
- package/dist/schema-helper/resolverMap.js.map +1 -0
- package/dist/supergraphManagers/IntrospectAndCompose/index.d.ts +31 -0
- package/dist/supergraphManagers/IntrospectAndCompose/index.d.ts.map +1 -0
- package/dist/supergraphManagers/IntrospectAndCompose/index.js +112 -0
- package/dist/supergraphManagers/IntrospectAndCompose/index.js.map +1 -0
- package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.d.ts +12 -0
- package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.d.ts.map +1 -0
- package/dist/{loadServicesFromRemoteEndpoint.js → supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.js} +6 -6
- package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.js.map +1 -0
- package/dist/supergraphManagers/LegacyFetcher/index.d.ts +33 -0
- package/dist/supergraphManagers/LegacyFetcher/index.d.ts.map +1 -0
- package/dist/supergraphManagers/LegacyFetcher/index.js +149 -0
- package/dist/supergraphManagers/LegacyFetcher/index.js.map +1 -0
- package/dist/supergraphManagers/LocalCompose/index.d.ts +19 -0
- package/dist/supergraphManagers/LocalCompose/index.d.ts.map +1 -0
- package/dist/supergraphManagers/LocalCompose/index.js +55 -0
- package/dist/supergraphManagers/LocalCompose/index.js.map +1 -0
- package/dist/supergraphManagers/UplinkFetcher/index.d.ts +32 -0
- package/dist/supergraphManagers/UplinkFetcher/index.d.ts.map +1 -0
- package/dist/supergraphManagers/UplinkFetcher/index.js +96 -0
- package/dist/supergraphManagers/UplinkFetcher/index.js.map +1 -0
- package/dist/{loadSupergraphSdlFromStorage.d.ts → supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.d.ts} +1 -1
- package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.d.ts.map +1 -0
- package/dist/{loadSupergraphSdlFromStorage.js → supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.js} +1 -1
- package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.js.map +1 -0
- package/dist/{outOfBandReporter.d.ts → supergraphManagers/UplinkFetcher/outOfBandReporter.d.ts} +0 -0
- package/dist/supergraphManagers/UplinkFetcher/outOfBandReporter.d.ts.map +1 -0
- package/dist/{outOfBandReporter.js → supergraphManagers/UplinkFetcher/outOfBandReporter.js} +2 -2
- package/dist/supergraphManagers/UplinkFetcher/outOfBandReporter.js.map +1 -0
- package/dist/supergraphManagers/index.d.ts +5 -0
- package/dist/supergraphManagers/index.d.ts.map +1 -0
- package/dist/supergraphManagers/index.js +12 -0
- package/dist/supergraphManagers/index.js.map +1 -0
- package/dist/utilities/createHash.d.ts +2 -0
- package/dist/utilities/createHash.d.ts.map +1 -0
- package/dist/utilities/createHash.js +15 -0
- package/dist/utilities/createHash.js.map +1 -0
- package/dist/utilities/isNodeLike.d.ts +3 -0
- package/dist/utilities/isNodeLike.d.ts.map +1 -0
- package/dist/utilities/isNodeLike.js +8 -0
- package/dist/utilities/isNodeLike.js.map +1 -0
- package/package.json +12 -10
- package/src/__mocks__/make-fetch-happen-fetcher.ts +3 -1
- package/src/__tests__/executeQueryPlan.test.ts +637 -1
- package/src/__tests__/execution-utils.ts +3 -3
- package/src/__tests__/gateway/buildService.test.ts +3 -3
- package/src/__tests__/gateway/endToEnd.test.ts +1 -1
- package/src/__tests__/gateway/executor.test.ts +1 -1
- package/src/__tests__/gateway/lifecycle-hooks.test.ts +58 -99
- package/src/__tests__/gateway/opentelemetry.test.ts +8 -3
- package/src/__tests__/gateway/queryPlanCache.test.ts +25 -9
- package/src/__tests__/gateway/reporting.test.ts +44 -19
- package/src/__tests__/gateway/supergraphSdl.test.ts +394 -0
- package/src/__tests__/integration/aliases.test.ts +9 -3
- package/src/__tests__/integration/configuration.test.ts +109 -12
- package/src/__tests__/integration/logger.test.ts +1 -1
- package/src/__tests__/integration/networkRequests.test.ts +81 -118
- package/src/__tests__/integration/nockMocks.ts +15 -8
- package/src/config.ts +149 -40
- package/src/datasources/LocalGraphQLDataSource.ts +1 -1
- package/src/datasources/RemoteGraphQLDataSource.ts +8 -2
- package/src/datasources/__tests__/LocalGraphQLDataSource.test.ts +1 -1
- package/src/datasources/__tests__/RemoteGraphQLDataSource.test.ts +4 -4
- package/src/executeQueryPlan.ts +14 -2
- package/src/index.ts +314 -485
- package/src/schema-helper/addResolversToSchema.ts +83 -0
- package/src/schema-helper/error.ts +11 -0
- package/src/schema-helper/index.ts +3 -0
- package/src/schema-helper/resolverMap.ts +23 -0
- package/src/supergraphManagers/IntrospectAndCompose/__tests__/IntrospectAndCompose.test.ts +370 -0
- package/src/{__tests__ → supergraphManagers/IntrospectAndCompose/__tests__}/loadServicesFromRemoteEndpoint.test.ts +5 -5
- package/src/supergraphManagers/IntrospectAndCompose/__tests__/tsconfig.json +8 -0
- package/src/supergraphManagers/IntrospectAndCompose/index.ts +163 -0
- package/src/{loadServicesFromRemoteEndpoint.ts → supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.ts} +6 -6
- package/src/supergraphManagers/LegacyFetcher/index.ts +229 -0
- package/src/supergraphManagers/LocalCompose/index.ts +83 -0
- package/src/{__tests__ → supergraphManagers/UplinkFetcher/__tests__}/loadSupergraphSdlFromStorage.test.ts +4 -4
- package/src/supergraphManagers/UplinkFetcher/__tests__/tsconfig.json +8 -0
- package/src/supergraphManagers/UplinkFetcher/index.ts +128 -0
- package/src/{loadSupergraphSdlFromStorage.ts → supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.ts} +3 -3
- package/src/{outOfBandReporter.ts → supergraphManagers/UplinkFetcher/outOfBandReporter.ts} +2 -2
- package/src/supergraphManagers/index.ts +4 -0
- package/src/utilities/__tests__/cleanErrorOfInaccessibleElements.test.ts +3 -3
- package/src/utilities/createHash.ts +10 -0
- package/src/utilities/isNodeLike.ts +11 -0
- package/CHANGELOG.md +0 -470
- package/dist/loadServicesFromRemoteEndpoint.d.ts +0 -13
- package/dist/loadServicesFromRemoteEndpoint.d.ts.map +0 -1
- package/dist/loadServicesFromRemoteEndpoint.js.map +0 -1
- package/dist/loadSupergraphSdlFromStorage.d.ts.map +0 -1
- package/dist/loadSupergraphSdlFromStorage.js.map +0 -1
- package/dist/outOfBandReporter.d.ts.map +0 -1
- package/dist/outOfBandReporter.js.map +0 -1
- package/src/__tests__/gateway/composedSdl.test.ts +0 -44
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import gql from 'graphql-tag';
|
|
2
2
|
import { ApolloGateway } from '../..';
|
|
3
3
|
import {
|
|
4
|
+
DynamicGatewayConfig,
|
|
4
5
|
Experimental_DidResolveQueryPlanCallback,
|
|
5
6
|
Experimental_UpdateServiceDefinitions,
|
|
7
|
+
ServiceDefinitionUpdate,
|
|
6
8
|
} from '../../config';
|
|
7
9
|
import {
|
|
8
10
|
product,
|
|
@@ -11,8 +13,11 @@ import {
|
|
|
11
13
|
accounts,
|
|
12
14
|
books,
|
|
13
15
|
documents,
|
|
16
|
+
fixtures,
|
|
17
|
+
fixturesWithUpdate,
|
|
14
18
|
} from 'apollo-federation-integration-testsuite';
|
|
15
19
|
import { Logger } from 'apollo-server-types';
|
|
20
|
+
import resolvable from '@josephg/resolvable';
|
|
16
21
|
|
|
17
22
|
// The order of this was specified to preserve existing test coverage. Typically
|
|
18
23
|
// we would just import and use the `fixtures` array.
|
|
@@ -47,16 +52,14 @@ beforeEach(() => {
|
|
|
47
52
|
|
|
48
53
|
describe('lifecycle hooks', () => {
|
|
49
54
|
it('uses updateServiceDefinitions override', async () => {
|
|
50
|
-
const experimental_updateServiceDefinitions: Experimental_UpdateServiceDefinitions =
|
|
51
|
-
async () => {
|
|
55
|
+
const experimental_updateServiceDefinitions: Experimental_UpdateServiceDefinitions =
|
|
56
|
+
jest.fn(async () => {
|
|
52
57
|
return { serviceDefinitions, isNewSchema: true };
|
|
53
|
-
}
|
|
54
|
-
);
|
|
58
|
+
});
|
|
55
59
|
|
|
56
60
|
const gateway = new ApolloGateway({
|
|
57
61
|
serviceList: serviceDefinitions,
|
|
58
62
|
experimental_updateServiceDefinitions,
|
|
59
|
-
experimental_didUpdateComposition: jest.fn(),
|
|
60
63
|
logger,
|
|
61
64
|
});
|
|
62
65
|
|
|
@@ -67,39 +70,7 @@ describe('lifecycle hooks', () => {
|
|
|
67
70
|
await gateway.stop();
|
|
68
71
|
});
|
|
69
72
|
|
|
70
|
-
it('calls
|
|
71
|
-
const experimental_didFailComposition = jest.fn();
|
|
72
|
-
|
|
73
|
-
const gateway = new ApolloGateway({
|
|
74
|
-
async experimental_updateServiceDefinitions() {
|
|
75
|
-
return {
|
|
76
|
-
serviceDefinitions: [serviceDefinitions[0]],
|
|
77
|
-
compositionMetadata: {
|
|
78
|
-
formatVersion: 1,
|
|
79
|
-
id: 'abc',
|
|
80
|
-
implementingServiceLocations: [],
|
|
81
|
-
schemaHash: 'abc',
|
|
82
|
-
},
|
|
83
|
-
isNewSchema: true,
|
|
84
|
-
};
|
|
85
|
-
},
|
|
86
|
-
serviceList: [],
|
|
87
|
-
experimental_didFailComposition,
|
|
88
|
-
logger,
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
await expect(gateway.load()).rejects.toThrowError();
|
|
92
|
-
|
|
93
|
-
const callbackArgs = experimental_didFailComposition.mock.calls[0][0];
|
|
94
|
-
expect(callbackArgs.serviceList).toHaveLength(1);
|
|
95
|
-
expect(callbackArgs.errors[0]).toMatchInlineSnapshot(
|
|
96
|
-
`[GraphQLError: [product] Book -> \`Book\` is an extension type, but \`Book\` is not defined in any service]`,
|
|
97
|
-
);
|
|
98
|
-
expect(callbackArgs.compositionMetadata.id).toEqual('abc');
|
|
99
|
-
expect(experimental_didFailComposition).toBeCalled();
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it('calls experimental_didUpdateComposition on schema update', async () => {
|
|
73
|
+
it('calls experimental_didUpdateSupergraph on schema update', async () => {
|
|
103
74
|
const compositionMetadata = {
|
|
104
75
|
formatVersion: 1,
|
|
105
76
|
id: 'abc',
|
|
@@ -107,81 +78,73 @@ describe('lifecycle hooks', () => {
|
|
|
107
78
|
schemaHash: 'hash1',
|
|
108
79
|
};
|
|
109
80
|
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
// This is the simplest way I could find to achieve mocked functions that leverage our types
|
|
121
|
-
const mockUpdate = jest.fn(update);
|
|
122
|
-
|
|
123
|
-
// We want to return a different composition across two ticks, so we mock it
|
|
124
|
-
// slightly differenty
|
|
125
|
-
mockUpdate.mockImplementationOnce(async () => {
|
|
126
|
-
const services = serviceDefinitions.filter(s => s.name !== 'books');
|
|
127
|
-
return {
|
|
128
|
-
serviceDefinitions: [
|
|
129
|
-
...services,
|
|
130
|
-
{
|
|
131
|
-
name: 'book',
|
|
132
|
-
typeDefs: books.typeDefs,
|
|
133
|
-
url: 'http://localhost:32542',
|
|
81
|
+
const mockUpdate = jest
|
|
82
|
+
.fn<Promise<ServiceDefinitionUpdate>, [config: DynamicGatewayConfig]>()
|
|
83
|
+
.mockImplementationOnce(async () => {
|
|
84
|
+
return {
|
|
85
|
+
serviceDefinitions: fixtures,
|
|
86
|
+
isNewSchema: true,
|
|
87
|
+
compositionMetadata: {
|
|
88
|
+
...compositionMetadata,
|
|
89
|
+
id: '123',
|
|
90
|
+
schemaHash: 'hash2',
|
|
134
91
|
},
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
92
|
+
};
|
|
93
|
+
})
|
|
94
|
+
// We want to return a different composition across two ticks, so we mock it
|
|
95
|
+
// slightly differently
|
|
96
|
+
.mockImplementationOnce(async () => {
|
|
97
|
+
return {
|
|
98
|
+
serviceDefinitions: fixturesWithUpdate,
|
|
99
|
+
isNewSchema: true,
|
|
100
|
+
compositionMetadata,
|
|
101
|
+
};
|
|
102
|
+
});
|
|
140
103
|
|
|
141
104
|
const mockDidUpdate = jest.fn();
|
|
142
105
|
|
|
143
106
|
const gateway = new ApolloGateway({
|
|
144
107
|
experimental_updateServiceDefinitions: mockUpdate,
|
|
145
|
-
|
|
108
|
+
experimental_didUpdateSupergraph: mockDidUpdate,
|
|
146
109
|
logger,
|
|
147
110
|
});
|
|
148
|
-
//
|
|
149
|
-
gateway
|
|
111
|
+
// for testing purposes, a short pollInterval is ideal so we'll override here
|
|
112
|
+
gateway['pollIntervalInMs'] = 100;
|
|
150
113
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
const schemaChangeBlocker1 = new Promise(res => (resolve1 = res));
|
|
154
|
-
const schemaChangeBlocker2 = new Promise(res => (resolve2 = res));
|
|
114
|
+
const schemaChangeBlocker1 = resolvable();
|
|
115
|
+
const schemaChangeBlocker2 = resolvable();
|
|
155
116
|
|
|
156
|
-
gateway.
|
|
117
|
+
gateway.onSchemaLoadOrUpdate(
|
|
157
118
|
jest
|
|
158
119
|
.fn()
|
|
159
|
-
.mockImplementationOnce(() =>
|
|
160
|
-
.mockImplementationOnce(() =>
|
|
120
|
+
.mockImplementationOnce(() => schemaChangeBlocker1.resolve())
|
|
121
|
+
.mockImplementationOnce(() => schemaChangeBlocker2.resolve()),
|
|
161
122
|
);
|
|
162
123
|
|
|
163
124
|
await gateway.load();
|
|
164
125
|
|
|
165
126
|
await schemaChangeBlocker1;
|
|
127
|
+
|
|
166
128
|
expect(mockUpdate).toBeCalledTimes(1);
|
|
167
129
|
expect(mockDidUpdate).toBeCalledTimes(1);
|
|
168
130
|
|
|
169
131
|
await schemaChangeBlocker2;
|
|
132
|
+
|
|
170
133
|
expect(mockUpdate).toBeCalledTimes(2);
|
|
171
134
|
expect(mockDidUpdate).toBeCalledTimes(2);
|
|
172
135
|
|
|
173
136
|
const [firstCall, secondCall] = mockDidUpdate.mock.calls;
|
|
174
137
|
|
|
175
|
-
|
|
176
|
-
expect(firstCall[0]
|
|
138
|
+
const expectedFirstId = '562c22b3382b56b1651944a96e89a361fe847b9b32660eae5ecbd12adc20bf8b'
|
|
139
|
+
expect(firstCall[0]!.compositionId).toEqual(expectedFirstId);
|
|
177
140
|
// first call should have no second "previous" argument
|
|
178
141
|
expect(firstCall[1]).toBeUndefined();
|
|
179
142
|
|
|
180
|
-
expect(secondCall[0]
|
|
181
|
-
|
|
143
|
+
expect(secondCall[0]!.compositionId).toEqual(
|
|
144
|
+
'0ced02894592ade4376276d11735b46723eb84850c32765cb78502ba5c29a563',
|
|
145
|
+
);
|
|
182
146
|
// second call should have previous info in the second arg
|
|
183
|
-
expect(secondCall[1]!.
|
|
184
|
-
expect(secondCall[1]!.compositionMetadata!.schemaHash).toEqual('hash1');
|
|
147
|
+
expect(secondCall[1]!.compositionId).toEqual(expectedFirstId);
|
|
185
148
|
|
|
186
149
|
await gateway.stop();
|
|
187
150
|
});
|
|
@@ -206,34 +169,31 @@ describe('lifecycle hooks', () => {
|
|
|
206
169
|
it('warns when polling on the default fetcher', async () => {
|
|
207
170
|
new ApolloGateway({
|
|
208
171
|
serviceList: serviceDefinitions,
|
|
209
|
-
|
|
172
|
+
pollIntervalInMs: 10,
|
|
210
173
|
logger,
|
|
211
174
|
});
|
|
212
|
-
expect(logger.warn).toHaveBeenCalledTimes(1);
|
|
213
175
|
expect(logger.warn).toHaveBeenCalledWith(
|
|
214
176
|
'Polling running services is dangerous and not recommended in production. Polling should only be used against a registry. If you are polling running services, use with caution.',
|
|
215
177
|
);
|
|
216
178
|
});
|
|
217
179
|
|
|
218
180
|
it('registers schema change callbacks when experimental_pollInterval is set for unmanaged configs', async () => {
|
|
219
|
-
const experimental_updateServiceDefinitions: Experimental_UpdateServiceDefinitions =
|
|
220
|
-
async (_config) => {
|
|
181
|
+
const experimental_updateServiceDefinitions: Experimental_UpdateServiceDefinitions =
|
|
182
|
+
jest.fn(async (_config) => {
|
|
221
183
|
return { serviceDefinitions, isNewSchema: true };
|
|
222
|
-
}
|
|
223
|
-
);
|
|
184
|
+
});
|
|
224
185
|
|
|
225
186
|
const gateway = new ApolloGateway({
|
|
226
187
|
serviceList: [{ name: 'book', url: 'http://localhost:32542' }],
|
|
227
188
|
experimental_updateServiceDefinitions,
|
|
228
|
-
|
|
189
|
+
pollIntervalInMs: 100,
|
|
229
190
|
logger,
|
|
230
191
|
});
|
|
231
192
|
|
|
232
|
-
|
|
233
|
-
const
|
|
234
|
-
const schemaChangeCallback = jest.fn(() => resolve());
|
|
193
|
+
const schemaChangeBlocker = resolvable();
|
|
194
|
+
const schemaChangeCallback = jest.fn(() => schemaChangeBlocker.resolve());
|
|
235
195
|
|
|
236
|
-
gateway.
|
|
196
|
+
gateway.onSchemaLoadOrUpdate(schemaChangeCallback);
|
|
237
197
|
await gateway.load();
|
|
238
198
|
|
|
239
199
|
await schemaChangeBlocker;
|
|
@@ -243,12 +203,11 @@ describe('lifecycle hooks', () => {
|
|
|
243
203
|
});
|
|
244
204
|
|
|
245
205
|
it('calls experimental_didResolveQueryPlan when executor is called', async () => {
|
|
246
|
-
const experimental_didResolveQueryPlan: Experimental_DidResolveQueryPlanCallback =
|
|
206
|
+
const experimental_didResolveQueryPlan: Experimental_DidResolveQueryPlanCallback =
|
|
207
|
+
jest.fn();
|
|
247
208
|
|
|
248
209
|
const gateway = new ApolloGateway({
|
|
249
|
-
localServiceList: [
|
|
250
|
-
books
|
|
251
|
-
],
|
|
210
|
+
localServiceList: [books],
|
|
252
211
|
experimental_didResolveQueryPlan,
|
|
253
212
|
});
|
|
254
213
|
|
|
@@ -258,7 +217,7 @@ describe('lifecycle hooks', () => {
|
|
|
258
217
|
{ book(isbn: "0262510871") { year } }
|
|
259
218
|
`;
|
|
260
219
|
|
|
261
|
-
|
|
220
|
+
// @ts-ignore
|
|
262
221
|
await executor({
|
|
263
222
|
source,
|
|
264
223
|
document: gql(source),
|
|
@@ -36,11 +36,16 @@ describe('opentelemetry', () => {
|
|
|
36
36
|
describe('with local data', () =>
|
|
37
37
|
{
|
|
38
38
|
async function gateway() {
|
|
39
|
+
const localDataSources = Object.fromEntries(
|
|
40
|
+
fixtures.map((f) => [
|
|
41
|
+
f.name,
|
|
42
|
+
new LocalGraphQLDataSource(buildSubgraphSchema(f)),
|
|
43
|
+
]),
|
|
44
|
+
);
|
|
39
45
|
const gateway = new ApolloGateway({
|
|
40
46
|
localServiceList: fixtures,
|
|
41
|
-
buildService
|
|
42
|
-
|
|
43
|
-
return new LocalGraphQLDataSource(buildSubgraphSchema([service]));
|
|
47
|
+
buildService(service) {
|
|
48
|
+
return localDataSources[service.name];
|
|
44
49
|
},
|
|
45
50
|
});
|
|
46
51
|
|
|
@@ -6,14 +6,20 @@ import { LocalGraphQLDataSource } from '../../datasources/LocalGraphQLDataSource
|
|
|
6
6
|
import { ApolloGateway } from '../../';
|
|
7
7
|
import { fixtures } from 'apollo-federation-integration-testsuite';
|
|
8
8
|
import { QueryPlanner } from '@apollo/query-planner';
|
|
9
|
+
|
|
9
10
|
it('caches the query plan for a request', async () => {
|
|
10
11
|
const buildQueryPlanSpy = jest.spyOn(QueryPlanner.prototype, 'buildQueryPlan');
|
|
11
12
|
|
|
13
|
+
const localDataSources = Object.fromEntries(
|
|
14
|
+
fixtures.map((f) => [
|
|
15
|
+
f.name,
|
|
16
|
+
new LocalGraphQLDataSource(buildSubgraphSchema(f)),
|
|
17
|
+
]),
|
|
18
|
+
);
|
|
12
19
|
const gateway = new ApolloGateway({
|
|
13
20
|
localServiceList: fixtures,
|
|
14
|
-
buildService
|
|
15
|
-
|
|
16
|
-
return new LocalGraphQLDataSource(buildSubgraphSchema([service]));
|
|
21
|
+
buildService(service) {
|
|
22
|
+
return localDataSources[service.name];
|
|
17
23
|
},
|
|
18
24
|
});
|
|
19
25
|
|
|
@@ -65,11 +71,17 @@ it('supports multiple operations and operationName', async () => {
|
|
|
65
71
|
}
|
|
66
72
|
`;
|
|
67
73
|
|
|
74
|
+
const localDataSources = Object.fromEntries(
|
|
75
|
+
fixtures.map((f) => [
|
|
76
|
+
f.name,
|
|
77
|
+
new LocalGraphQLDataSource(buildSubgraphSchema(f)),
|
|
78
|
+
]),
|
|
79
|
+
);
|
|
80
|
+
|
|
68
81
|
const gateway = new ApolloGateway({
|
|
69
82
|
localServiceList: fixtures,
|
|
70
|
-
buildService
|
|
71
|
-
|
|
72
|
-
return new LocalGraphQLDataSource(buildSubgraphSchema([service]));
|
|
83
|
+
buildService(service) {
|
|
84
|
+
return localDataSources[service.name];
|
|
73
85
|
},
|
|
74
86
|
});
|
|
75
87
|
|
|
@@ -170,11 +182,15 @@ it('does not corrupt cached queryplan data across requests', async () => {
|
|
|
170
182
|
},
|
|
171
183
|
};
|
|
172
184
|
|
|
185
|
+
const dataSources: Record<string, LocalGraphQLDataSource> = {
|
|
186
|
+
a: new LocalGraphQLDataSource(buildSubgraphSchema(serviceA)),
|
|
187
|
+
b: new LocalGraphQLDataSource(buildSubgraphSchema(serviceB)),
|
|
188
|
+
};
|
|
189
|
+
|
|
173
190
|
const gateway = new ApolloGateway({
|
|
174
191
|
localServiceList: [serviceA, serviceB],
|
|
175
|
-
buildService
|
|
176
|
-
|
|
177
|
-
return new LocalGraphQLDataSource(buildSubgraphSchema([service]));
|
|
192
|
+
buildService(service) {
|
|
193
|
+
return dataSources[service.name];
|
|
178
194
|
},
|
|
179
195
|
});
|
|
180
196
|
|
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
import { gunzipSync } from 'zlib';
|
|
2
2
|
import nock from 'nock';
|
|
3
|
-
import { GraphQLSchemaModule } from 'apollo-graphql';
|
|
4
|
-
import gql from 'graphql-tag';
|
|
5
3
|
import { buildSubgraphSchema } from '@apollo/subgraph';
|
|
6
4
|
import { ApolloServer } from 'apollo-server';
|
|
7
5
|
import { ApolloServerPluginUsageReporting } from 'apollo-server-core';
|
|
8
|
-
import { execute, toPromise } from 'apollo-link';
|
|
9
|
-
import { createHttpLink } from 'apollo-link-http';
|
|
10
6
|
import fetch from 'node-fetch';
|
|
11
7
|
import { ApolloGateway } from '../..';
|
|
12
8
|
import { Plugin, Config, Refs } from 'pretty-format';
|
|
13
9
|
import { Report, Trace } from 'apollo-reporting-protobuf';
|
|
14
10
|
import { fixtures } from 'apollo-federation-integration-testsuite';
|
|
15
11
|
import { nockAfterEach, nockBeforeEach } from '../nockAssertions';
|
|
12
|
+
import resolvable, { Resolvable } from '@josephg/resolvable';
|
|
13
|
+
import { GraphQLSchemaModule } from '../../schema-helper';
|
|
16
14
|
|
|
17
15
|
// Normalize specific fields that change often (eg timestamps) to static values,
|
|
18
16
|
// to make snapshot testing viable. (If these helpers are more generally
|
|
@@ -89,19 +87,16 @@ describe('reporting', () => {
|
|
|
89
87
|
let backendServers: ApolloServer[];
|
|
90
88
|
let gatewayServer: ApolloServer;
|
|
91
89
|
let gatewayUrl: string;
|
|
92
|
-
let reportPromise:
|
|
90
|
+
let reportPromise: Resolvable<any>;
|
|
93
91
|
|
|
94
92
|
beforeEach(async () => {
|
|
95
|
-
|
|
96
|
-
reportPromise = new Promise<any>((resolve) => {
|
|
97
|
-
reportResolver = resolve;
|
|
98
|
-
});
|
|
93
|
+
reportPromise = resolvable();
|
|
99
94
|
|
|
100
95
|
nockBeforeEach();
|
|
101
96
|
nock('https://usage-reporting.api.apollographql.com')
|
|
102
97
|
.post('/api/ingress/traces')
|
|
103
98
|
.reply(200, (_: any, requestBody: string) => {
|
|
104
|
-
|
|
99
|
+
reportPromise.resolve(requestBody);
|
|
105
100
|
return 'ok';
|
|
106
101
|
});
|
|
107
102
|
|
|
@@ -143,7 +138,7 @@ describe('reporting', () => {
|
|
|
143
138
|
});
|
|
144
139
|
|
|
145
140
|
it(`queries three services`, async () => {
|
|
146
|
-
const query =
|
|
141
|
+
const query = `#graphql
|
|
147
142
|
query {
|
|
148
143
|
me {
|
|
149
144
|
name {
|
|
@@ -157,12 +152,15 @@ describe('reporting', () => {
|
|
|
157
152
|
}
|
|
158
153
|
`;
|
|
159
154
|
|
|
160
|
-
const result = await
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
155
|
+
const result = await fetch(gatewayUrl, {
|
|
156
|
+
method: 'POST',
|
|
157
|
+
headers: {
|
|
158
|
+
'Content-Type': 'application/json',
|
|
159
|
+
},
|
|
160
|
+
body: JSON.stringify({ query }),
|
|
161
|
+
});
|
|
162
|
+
const body = await result.json();
|
|
163
|
+
expect(body).toMatchInlineSnapshot(`
|
|
166
164
|
Object {
|
|
167
165
|
"data": Object {
|
|
168
166
|
"me": Object {
|
|
@@ -233,6 +231,34 @@ describe('reporting', () => {
|
|
|
233
231
|
"tracesPerQuery": Object {
|
|
234
232
|
"# -
|
|
235
233
|
{me{name{first last}}topProducts{name}}": Object {
|
|
234
|
+
"referencedFieldsByType": Object {
|
|
235
|
+
"Name": Object {
|
|
236
|
+
"fieldNames": Array [
|
|
237
|
+
"first",
|
|
238
|
+
"last",
|
|
239
|
+
],
|
|
240
|
+
"isInterface": false,
|
|
241
|
+
},
|
|
242
|
+
"Product": Object {
|
|
243
|
+
"fieldNames": Array [
|
|
244
|
+
"name",
|
|
245
|
+
],
|
|
246
|
+
"isInterface": true,
|
|
247
|
+
},
|
|
248
|
+
"Query": Object {
|
|
249
|
+
"fieldNames": Array [
|
|
250
|
+
"me",
|
|
251
|
+
"topProducts",
|
|
252
|
+
],
|
|
253
|
+
"isInterface": false,
|
|
254
|
+
},
|
|
255
|
+
"User": Object {
|
|
256
|
+
"fieldNames": Array [
|
|
257
|
+
"name",
|
|
258
|
+
],
|
|
259
|
+
"isInterface": false,
|
|
260
|
+
},
|
|
261
|
+
},
|
|
236
262
|
"trace": Array [
|
|
237
263
|
Object {
|
|
238
264
|
"cachePolicy": Object {
|
|
@@ -240,14 +266,13 @@ describe('reporting', () => {
|
|
|
240
266
|
"scope": "PRIVATE",
|
|
241
267
|
},
|
|
242
268
|
"clientName": "",
|
|
243
|
-
"clientReferenceId": "",
|
|
244
269
|
"clientVersion": "",
|
|
245
|
-
"details": Object {},
|
|
246
270
|
"durationNs": 12345,
|
|
247
271
|
"endTime": Object {
|
|
248
272
|
"nanos": 123000000,
|
|
249
273
|
"seconds": "1562203363",
|
|
250
274
|
},
|
|
275
|
+
"fieldExecutionWeight": 1,
|
|
251
276
|
"forbiddenOperation": false,
|
|
252
277
|
"fullQueryCacheHit": false,
|
|
253
278
|
"http": Object {
|