@apollo/gateway 0.50.1 → 0.52.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 +4 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/datasources/LocalGraphQLDataSource.d.ts +2 -2
- package/dist/datasources/LocalGraphQLDataSource.d.ts.map +1 -1
- package/dist/datasources/LocalGraphQLDataSource.js +0 -2
- package/dist/datasources/LocalGraphQLDataSource.js.map +1 -1
- package/dist/datasources/RemoteGraphQLDataSource.d.ts +11 -10
- package/dist/datasources/RemoteGraphQLDataSource.d.ts.map +1 -1
- package/dist/datasources/RemoteGraphQLDataSource.js +30 -31
- package/dist/datasources/RemoteGraphQLDataSource.js.map +1 -1
- package/dist/datasources/types.d.ts +5 -5
- package/dist/datasources/types.d.ts.map +1 -1
- package/dist/executeQueryPlan.d.ts +3 -3
- 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 +7 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -35
- package/dist/index.js.map +1 -1
- package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.d.ts.map +1 -1
- package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.js.map +1 -1
- package/dist/supergraphManagers/UplinkFetcher/index.d.ts +2 -2
- package/dist/supergraphManagers/UplinkFetcher/index.d.ts.map +1 -1
- package/dist/supergraphManagers/UplinkFetcher/index.js.map +1 -1
- package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.d.ts +3 -3
- package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.d.ts.map +1 -1
- package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.js +14 -13
- package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.js.map +1 -1
- package/dist/supergraphManagers/UplinkFetcher/outOfBandReporter.d.ts +6 -5
- package/dist/supergraphManagers/UplinkFetcher/outOfBandReporter.d.ts.map +1 -1
- package/dist/supergraphManagers/UplinkFetcher/outOfBandReporter.js +3 -3
- package/dist/supergraphManagers/UplinkFetcher/outOfBandReporter.js.map +1 -1
- package/dist/typings/graphql.d.ts +24 -0
- package/dist/typings/graphql.d.ts.map +1 -0
- package/dist/{schema-helper/resolverMap.js → typings/graphql.js} +1 -1
- package/dist/typings/graphql.js.map +1 -0
- package/package.json +11 -13
- package/src/__generated__/graphqlTypes.ts +1 -1
- package/src/__tests__/executeQueryPlan.test.ts +15 -6
- package/src/__tests__/execution-utils.ts +4 -4
- package/src/__tests__/gateway/buildService.test.ts +81 -83
- package/src/__tests__/gateway/endToEnd.test.ts +1 -1
- package/src/__tests__/gateway/executor.test.ts +20 -17
- package/src/__tests__/gateway/opentelemetry.test.ts +3 -7
- package/src/__tests__/gateway/reporting.test.ts +1 -1
- package/src/__tests__/gateway/supergraphSdl.test.ts +11 -13
- package/src/__tests__/integration/complex-key.test.ts +2 -2
- package/src/config.ts +4 -6
- package/src/datasources/LocalGraphQLDataSource.ts +2 -4
- package/src/datasources/RemoteGraphQLDataSource.ts +72 -60
- package/src/datasources/__tests__/LocalGraphQLDataSource.test.ts +3 -3
- package/src/datasources/__tests__/RemoteGraphQLDataSource.test.ts +120 -159
- package/src/datasources/types.ts +12 -5
- package/src/executeQueryPlan.ts +18 -18
- package/src/index.ts +25 -71
- package/src/supergraphManagers/IntrospectAndCompose/__tests__/IntrospectAndCompose.test.ts +0 -6
- package/src/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.ts +2 -2
- package/src/supergraphManagers/UplinkFetcher/__tests__/loadSupergraphSdlFromStorage.test.ts +70 -74
- package/src/supergraphManagers/UplinkFetcher/index.ts +2 -2
- package/src/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.ts +23 -17
- package/src/supergraphManagers/UplinkFetcher/outOfBandReporter.ts +9 -7
- package/src/typings/graphql.ts +31 -0
- package/dist/cache.d.ts +0 -18
- package/dist/cache.d.ts.map +0 -1
- package/dist/cache.js +0 -46
- package/dist/cache.js.map +0 -1
- package/dist/schema-helper/addResolversToSchema.d.ts +0 -4
- package/dist/schema-helper/addResolversToSchema.d.ts.map +0 -1
- package/dist/schema-helper/addResolversToSchema.js +0 -62
- package/dist/schema-helper/addResolversToSchema.js.map +0 -1
- package/dist/schema-helper/index.d.ts +0 -3
- package/dist/schema-helper/index.d.ts.map +0 -1
- package/dist/schema-helper/index.js +0 -19
- package/dist/schema-helper/index.js.map +0 -1
- package/dist/schema-helper/resolverMap.d.ts +0 -16
- package/dist/schema-helper/resolverMap.d.ts.map +0 -1
- package/dist/schema-helper/resolverMap.js.map +0 -1
- package/dist/utilities/createHash.d.ts +0 -2
- package/dist/utilities/createHash.d.ts.map +0 -1
- package/dist/utilities/createHash.js +0 -15
- package/dist/utilities/createHash.js.map +0 -1
- package/src/__mocks__/apollo-server-env.ts +0 -56
- package/src/__mocks__/make-fetch-happen-fetcher.ts +0 -57
- package/src/cache.ts +0 -66
- package/src/make-fetch-happen.d.ts +0 -59
- package/src/schema-helper/addResolversToSchema.ts +0 -83
- package/src/schema-helper/index.ts +0 -2
- package/src/schema-helper/resolverMap.ts +0 -23
- package/src/utilities/createHash.ts +0 -10
|
@@ -1,28 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
GraphQLRequestContext,
|
|
3
|
-
GraphQLResponse,
|
|
4
|
-
ValueOrPromise,
|
|
5
|
-
GraphQLRequest,
|
|
6
|
-
CacheHint,
|
|
7
|
-
CacheScope,
|
|
8
|
-
CachePolicy,
|
|
9
|
-
} from 'apollo-server-types';
|
|
10
|
-
import {
|
|
11
|
-
ApolloError,
|
|
12
|
-
AuthenticationError,
|
|
13
|
-
ForbiddenError,
|
|
14
|
-
} from 'apollo-server-errors';
|
|
15
|
-
import { fetch, Request, Headers, Response } from 'apollo-server-env';
|
|
16
1
|
import { isObject } from '../utilities/predicates';
|
|
17
2
|
import { GraphQLDataSource, GraphQLDataSourceProcessOptions, GraphQLDataSourceRequestKind } from './types';
|
|
18
|
-
import
|
|
3
|
+
import { createHash } from '@apollo/utils.createhash';
|
|
19
4
|
import { parseCacheControlHeader } from './parseCacheControlHeader';
|
|
20
5
|
import fetcher from 'make-fetch-happen';
|
|
6
|
+
import { Headers as NodeFetchHeaders, Request as NodeFetchRequest } from 'node-fetch';
|
|
7
|
+
import { Fetcher, FetcherRequestInit, FetcherResponse } from '@apollo/utils.fetcher';
|
|
8
|
+
import { GraphQLError, GraphQLErrorExtensions } from 'graphql';
|
|
9
|
+
import { GatewayCacheHint, GatewayCachePolicy, GatewayGraphQLRequest, GatewayGraphQLRequestContext, GatewayGraphQLResponse } from '@apollo/server-gateway-interface';
|
|
10
|
+
|
|
21
11
|
export class RemoteGraphQLDataSource<
|
|
22
12
|
TContext extends Record<string, any> = Record<string, any>,
|
|
23
13
|
> implements GraphQLDataSource<TContext>
|
|
24
14
|
{
|
|
25
|
-
fetcher:
|
|
15
|
+
fetcher: Fetcher;
|
|
26
16
|
|
|
27
17
|
constructor(
|
|
28
18
|
config?: Partial<RemoteGraphQLDataSource<TContext>> &
|
|
@@ -30,6 +20,11 @@ export class RemoteGraphQLDataSource<
|
|
|
30
20
|
ThisType<RemoteGraphQLDataSource<TContext>>,
|
|
31
21
|
) {
|
|
32
22
|
this.fetcher = fetcher.defaults({
|
|
23
|
+
// Allow an arbitrary number of sockets per subgraph. This is the default
|
|
24
|
+
// behavior of Node's http.Agent as well as the npm package agentkeepalive
|
|
25
|
+
// which wraps it, but is not the default behavior of make-fetch-happen
|
|
26
|
+
// which wraps agentkeepalive (that package sets this to 15 by default).
|
|
27
|
+
maxSockets: Infinity,
|
|
33
28
|
// although this is the default, we want to take extra care and be very
|
|
34
29
|
// explicity to ensure that mutations cannot be retried. please leave this
|
|
35
30
|
// intact.
|
|
@@ -70,7 +65,7 @@ export class RemoteGraphQLDataSource<
|
|
|
70
65
|
|
|
71
66
|
async process(
|
|
72
67
|
options: GraphQLDataSourceProcessOptions<TContext>,
|
|
73
|
-
): Promise<
|
|
68
|
+
): Promise<GatewayGraphQLResponse> {
|
|
74
69
|
const { request, context: originalContext } = options;
|
|
75
70
|
// Deal with a bit of a hairy situation in typings: when doing health checks
|
|
76
71
|
// and schema checks we always pass in `{}` as the context even though it's
|
|
@@ -82,7 +77,12 @@ export class RemoteGraphQLDataSource<
|
|
|
82
77
|
const context = originalContext as TContext;
|
|
83
78
|
|
|
84
79
|
// Respect incoming http headers (eg, apollo-federation-include-trace).
|
|
85
|
-
const headers =
|
|
80
|
+
const headers = new NodeFetchHeaders();
|
|
81
|
+
if (request.http?.headers) {
|
|
82
|
+
for (const [name, value] of request.http.headers) {
|
|
83
|
+
headers.append(name, value);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
86
|
headers.set('Content-Type', 'application/json');
|
|
87
87
|
|
|
88
88
|
request.http = {
|
|
@@ -112,7 +112,7 @@ export class RemoteGraphQLDataSource<
|
|
|
112
112
|
: null;
|
|
113
113
|
|
|
114
114
|
if (this.apq) {
|
|
115
|
-
const apqHash =
|
|
115
|
+
const apqHash = createHash('sha256').update(request.query).digest('hex');
|
|
116
116
|
|
|
117
117
|
// Take the original extensions and extend them with
|
|
118
118
|
// the necessary "extensions" for APQ handshaking.
|
|
@@ -149,7 +149,7 @@ export class RemoteGraphQLDataSource<
|
|
|
149
149
|
// If APQ was enabled, we'll run the same request again, but add in the
|
|
150
150
|
// previously omitted `query`. If APQ was NOT enabled, this is the first
|
|
151
151
|
// request (non-APQ, all the way).
|
|
152
|
-
const requestWithQuery:
|
|
152
|
+
const requestWithQuery: GatewayGraphQLRequest = {
|
|
153
153
|
query,
|
|
154
154
|
...requestWithoutQuery,
|
|
155
155
|
};
|
|
@@ -163,9 +163,9 @@ export class RemoteGraphQLDataSource<
|
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
private async sendRequest(
|
|
166
|
-
request:
|
|
166
|
+
request: GatewayGraphQLRequest,
|
|
167
167
|
context: TContext,
|
|
168
|
-
): Promise<
|
|
168
|
+
): Promise<GatewayGraphQLResponse> {
|
|
169
169
|
// This would represent an internal programming error since this shouldn't
|
|
170
170
|
// be possible in the way that this method is invoked right now.
|
|
171
171
|
if (!request.http) {
|
|
@@ -177,20 +177,24 @@ export class RemoteGraphQLDataSource<
|
|
|
177
177
|
// we're accessing (e.g. url) and what we access it with (e.g. headers).
|
|
178
178
|
const { http, ...requestWithoutHttp } = request;
|
|
179
179
|
const stringifiedRequestWithoutHttp = JSON.stringify(requestWithoutHttp);
|
|
180
|
-
const
|
|
181
|
-
|
|
180
|
+
const requestInit: FetcherRequestInit = {
|
|
181
|
+
method: http.method,
|
|
182
|
+
headers: Object.fromEntries(http.headers),
|
|
182
183
|
body: stringifiedRequestWithoutHttp,
|
|
183
|
-
}
|
|
184
|
+
};
|
|
185
|
+
// Note that we don't actually send this Request object to the fetcher; it
|
|
186
|
+
// is merely sent to methods on this object that might be overridden by users.
|
|
187
|
+
// We are careful to only send data to the overridable fetcher function that uses
|
|
188
|
+
// plain JS objects --- some fetch implementations don't know how to handle
|
|
189
|
+
// Request or Headers objects created by other fetch implementations.
|
|
190
|
+
const fetchRequest = new NodeFetchRequest(http.url, requestInit);
|
|
184
191
|
|
|
185
|
-
let fetchResponse:
|
|
192
|
+
let fetchResponse: FetcherResponse | undefined;
|
|
186
193
|
|
|
187
194
|
try {
|
|
188
195
|
// Use our local `fetcher` to allow for fetch injection
|
|
189
196
|
// Use the fetcher's `Request` implementation for compatibility
|
|
190
|
-
fetchResponse = await this.fetcher(http.url,
|
|
191
|
-
...http,
|
|
192
|
-
body: stringifiedRequestWithoutHttp,
|
|
193
|
-
});
|
|
197
|
+
fetchResponse = await this.fetcher(http.url, requestInit);
|
|
194
198
|
|
|
195
199
|
if (!fetchResponse.ok) {
|
|
196
200
|
throw await this.errorFromResponse(fetchResponse);
|
|
@@ -214,7 +218,7 @@ export class RemoteGraphQLDataSource<
|
|
|
214
218
|
|
|
215
219
|
public willSendRequest?(
|
|
216
220
|
options: GraphQLDataSourceProcessOptions<TContext>,
|
|
217
|
-
):
|
|
221
|
+
): void | Promise<void>;
|
|
218
222
|
|
|
219
223
|
private async respond({
|
|
220
224
|
response,
|
|
@@ -222,11 +226,11 @@ export class RemoteGraphQLDataSource<
|
|
|
222
226
|
context,
|
|
223
227
|
overallCachePolicy,
|
|
224
228
|
}: {
|
|
225
|
-
response:
|
|
226
|
-
request:
|
|
229
|
+
response: GatewayGraphQLResponse;
|
|
230
|
+
request: GatewayGraphQLRequest;
|
|
227
231
|
context: TContext;
|
|
228
|
-
overallCachePolicy:
|
|
229
|
-
}): Promise<
|
|
232
|
+
overallCachePolicy: GatewayCachePolicy | null;
|
|
233
|
+
}): Promise<GatewayGraphQLResponse> {
|
|
230
234
|
const processedResponse =
|
|
231
235
|
typeof this.didReceiveResponse === 'function'
|
|
232
236
|
? await this.didReceiveResponse({ response, request, context })
|
|
@@ -241,16 +245,16 @@ export class RemoteGraphQLDataSource<
|
|
|
241
245
|
// thus the overall response) is uncacheable. (If you don't like this, you
|
|
242
246
|
// can tweak the `cache-control` header in your `didReceiveResponse`
|
|
243
247
|
// method.)
|
|
244
|
-
const hint:
|
|
248
|
+
const hint: GatewayCacheHint = { maxAge: 0 };
|
|
245
249
|
const maxAge = parsed['max-age'];
|
|
246
250
|
if (typeof maxAge === 'string' && maxAge.match(/^[0-9]+$/)) {
|
|
247
251
|
hint.maxAge = +maxAge;
|
|
248
252
|
}
|
|
249
253
|
if (parsed['private'] === true) {
|
|
250
|
-
hint.scope =
|
|
254
|
+
hint.scope = 'PRIVATE';
|
|
251
255
|
}
|
|
252
256
|
if (parsed['public'] === true) {
|
|
253
|
-
hint.scope =
|
|
257
|
+
hint.scope = 'PUBLIC';
|
|
254
258
|
}
|
|
255
259
|
overallCachePolicy.restrict(hint);
|
|
256
260
|
}
|
|
@@ -260,22 +264,22 @@ export class RemoteGraphQLDataSource<
|
|
|
260
264
|
|
|
261
265
|
public didReceiveResponse?(
|
|
262
266
|
requestContext: Required<
|
|
263
|
-
Pick<
|
|
267
|
+
Pick<GatewayGraphQLRequestContext<TContext>, 'request' | 'response' | 'context'>
|
|
264
268
|
>,
|
|
265
|
-
):
|
|
269
|
+
): GatewayGraphQLResponse | Promise<GatewayGraphQLResponse>;
|
|
266
270
|
|
|
267
271
|
public didEncounterError(
|
|
268
272
|
error: Error,
|
|
269
|
-
_fetchRequest:
|
|
270
|
-
_fetchResponse?:
|
|
273
|
+
_fetchRequest: NodeFetchRequest,
|
|
274
|
+
_fetchResponse?: FetcherResponse,
|
|
271
275
|
_context?: TContext,
|
|
272
276
|
) {
|
|
273
277
|
throw error;
|
|
274
278
|
}
|
|
275
279
|
|
|
276
280
|
public parseBody(
|
|
277
|
-
fetchResponse:
|
|
278
|
-
_fetchRequest?:
|
|
281
|
+
fetchResponse: FetcherResponse,
|
|
282
|
+
_fetchRequest?: NodeFetchRequest,
|
|
279
283
|
_context?: TContext,
|
|
280
284
|
): Promise<object | string> {
|
|
281
285
|
const contentType = fetchResponse.headers.get('Content-Type');
|
|
@@ -286,29 +290,37 @@ export class RemoteGraphQLDataSource<
|
|
|
286
290
|
}
|
|
287
291
|
}
|
|
288
292
|
|
|
289
|
-
public async errorFromResponse(response:
|
|
290
|
-
const message = `${response.status}: ${response.statusText}`;
|
|
291
|
-
|
|
292
|
-
let error: ApolloError;
|
|
293
|
-
if (response.status === 401) {
|
|
294
|
-
error = new AuthenticationError(message);
|
|
295
|
-
} else if (response.status === 403) {
|
|
296
|
-
error = new ForbiddenError(message);
|
|
297
|
-
} else {
|
|
298
|
-
error = new ApolloError(message);
|
|
299
|
-
}
|
|
300
|
-
|
|
293
|
+
public async errorFromResponse(response: FetcherResponse) {
|
|
301
294
|
const body = await this.parseBody(response);
|
|
302
295
|
|
|
303
|
-
|
|
296
|
+
const extensions: GraphQLErrorExtensions = {
|
|
304
297
|
response: {
|
|
305
298
|
url: response.url,
|
|
306
299
|
status: response.status,
|
|
307
300
|
statusText: response.statusText,
|
|
308
301
|
body,
|
|
309
302
|
},
|
|
310
|
-
}
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
if (response.status === 401) {
|
|
306
|
+
extensions.code = 'UNAUTHENTICATED';
|
|
307
|
+
} else if (response.status === 403) {
|
|
308
|
+
extensions.code = 'FORBIDDEN';
|
|
309
|
+
}
|
|
311
310
|
|
|
312
|
-
|
|
311
|
+
// Note: gateway 0.x still supports graphql-js v15.8, which does
|
|
312
|
+
// not have the options-based GraphQLError constructor. Note that
|
|
313
|
+
// the constructor used here is dropped in graphql-js v17, so this
|
|
314
|
+
// will have to be adjusted if we try to make gateway 0.x support
|
|
315
|
+
// graphql-js v17.
|
|
316
|
+
return new GraphQLError(
|
|
317
|
+
`${response.status}: ${response.statusText}`,
|
|
318
|
+
null,
|
|
319
|
+
null,
|
|
320
|
+
null,
|
|
321
|
+
null,
|
|
322
|
+
null,
|
|
323
|
+
extensions,
|
|
324
|
+
);
|
|
313
325
|
}
|
|
314
326
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { LocalGraphQLDataSource } from '../LocalGraphQLDataSource';
|
|
2
2
|
import { buildSubgraphSchema } from '@apollo/subgraph';
|
|
3
3
|
import gql from 'graphql-tag';
|
|
4
|
-
import { GraphQLResolverMap } from '
|
|
5
|
-
import { GraphQLRequestContext } from 'apollo-server-types';
|
|
4
|
+
import { GraphQLResolverMap } from '@apollo/subgraph/src/schema-helper';
|
|
6
5
|
import { GraphQLDataSourceRequestKind } from '../types';
|
|
6
|
+
import { GatewayGraphQLRequestContext } from '@apollo/server-gateway-interface';
|
|
7
7
|
|
|
8
8
|
describe('constructing requests', () => {
|
|
9
9
|
it('accepts context', async () => {
|
|
@@ -42,7 +42,7 @@ describe('constructing requests', () => {
|
|
|
42
42
|
},
|
|
43
43
|
incomingRequestContext: {
|
|
44
44
|
context: { userId: 2 },
|
|
45
|
-
} as
|
|
45
|
+
} as GatewayGraphQLRequestContext<{userId: number}>,
|
|
46
46
|
context: { userId: 2 },
|
|
47
47
|
});
|
|
48
48
|
|