@apollo/gateway 0.51.0 → 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 +2 -2
- 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 +6 -6
- package/dist/datasources/RemoteGraphQLDataSource.d.ts.map +1 -1
- package/dist/datasources/RemoteGraphQLDataSource.js +12 -18
- 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.map +1 -1
- package/dist/index.d.ts +7 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -11
- 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/package.json +8 -10
- package/src/__generated__/graphqlTypes.ts +1 -1
- package/src/__tests__/executeQueryPlan.test.ts +14 -5
- package/src/__tests__/execution-utils.ts +3 -3
- package/src/__tests__/gateway/executor.test.ts +2 -2
- package/src/config.ts +2 -4
- package/src/datasources/LocalGraphQLDataSource.ts +2 -4
- package/src/datasources/RemoteGraphQLDataSource.ts +38 -42
- package/src/datasources/__tests__/LocalGraphQLDataSource.test.ts +2 -2
- package/src/datasources/__tests__/RemoteGraphQLDataSource.test.ts +14 -19
- package/src/datasources/types.ts +12 -5
- package/src/executeQueryPlan.ts +13 -17
- package/src/index.ts +21 -42
- package/src/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.ts +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loadServicesFromRemoteEndpoint.js","sourceRoot":"","sources":["../../../src/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"loadServicesFromRemoteEndpoint.js","sourceRoot":"","sources":["../../../src/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.ts"],"names":[],"mappings":";;;AAAA,qCAAgC;AAChC,2CAAkD;AAClD,mDAA0F;AAC1F,6BAAiD;AAS1C,KAAK,UAAU,8BAA8B,CAAC,EACnD,WAAW,EACX,8BAA8B,EAC9B,eAAe,GAOhB;IACC,IAAI,CAAC,WAAW,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE;QACvC,MAAM,IAAI,KAAK,CACb,gEAAgE,CACjE,CAAC;KACH;IAED,IAAI,WAAW,GAAG,KAAK,CAAC;IAExB,MAAM,oBAAoB,GAAG,WAAW,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,EAAE,EAAE;QAC/E,IAAI,CAAC,GAAG,EAAE;YACR,MAAM,IAAI,KAAK,CACb,6BAA6B,IAAI,+BAA+B,CAAC,CAAC;SACrE;QAED,MAAM,OAAO,GAA0B;YACrC,KAAK,EAAE,4BAAwB;YAC/B,IAAI,EAAE;gBACJ,GAAG;gBACH,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,IAAI,oBAAO,CAAC,MAAM,8BAA8B,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;aAC1E;SACF,CAAC;QAEF,OAAO,UAAU;aACd,OAAO,CAAC;YACP,IAAI,EAAE,oCAA4B,CAAC,cAAc;YACjD,OAAO;YACP,OAAO,EAAE,EAAE;SACZ,CAAC;aACD,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAqB,EAAE;YAC5C,IAAI,IAAI,IAAI,CAAC,MAAM,EAAE;gBACnB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAa,CAAC;gBAC7C,MAAM,kBAAkB,GAAG,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBAGrD,IAAI,kBAAkB,KAAK,QAAQ,EAAE;oBACnC,WAAW,GAAG,IAAI,CAAC;iBACpB;gBACD,eAAe,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;gBACpC,OAAO;oBACL,IAAI;oBACJ,GAAG;oBACH,QAAQ,EAAE,IAAA,eAAK,EAAC,QAAQ,CAAC;iBAC1B,CAAC;aACH;YAED,MAAM,IAAI,KAAK,CAAC,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC5D,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;YACb,MAAM,YAAY,GAChB,0CAA0C,IAAI,QAAQ,GAAG,EAAE;gBAC3D,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,GAAG,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAExD,MAAM,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC;QAChC,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,MAAM,kBAAkB,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;IACnE,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,CAAA;AAC5C,CAAC;AAtED,wEAsEC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apollo/gateway",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.52.0",
|
|
4
4
|
"description": "Apollo Gateway",
|
|
5
5
|
"author": "Apollo <packages@apollographql.com>",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -26,26 +26,24 @@
|
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@apollo/core-schema": "^0.2.0",
|
|
29
|
-
"@apollo/federation": "^0.
|
|
30
|
-
"@apollo/query-planner": "^0.
|
|
29
|
+
"@apollo/federation": "^0.37.0",
|
|
30
|
+
"@apollo/query-planner": "^0.11.0",
|
|
31
|
+
"@apollo/server-gateway-interface": "^1.0.1",
|
|
31
32
|
"@apollo/utils.createhash": "^1.0.0",
|
|
32
33
|
"@apollo/utils.fetcher": "^1.0.0",
|
|
33
34
|
"@apollo/utils.logger": "^1.0.0",
|
|
34
35
|
"@josephg/resolvable": "^1.0.1",
|
|
35
36
|
"@opentelemetry/api": "^1.0.1",
|
|
36
|
-
"@types/node-fetch": "2.6.
|
|
37
|
+
"@types/node-fetch": "2.6.2",
|
|
37
38
|
"apollo-reporting-protobuf": "^0.8.0 || ^3.0.0",
|
|
38
|
-
"apollo-server-caching": "^0.7.0 || ^3.0.0",
|
|
39
|
-
"apollo-server-core": "^2.23.0 || ^3.0.0",
|
|
40
|
-
"apollo-server-errors": "^2.5.0 || ^3.0.0",
|
|
41
|
-
"apollo-server-types": "^0.9.0 || ^3.0.0",
|
|
42
39
|
"async-retry": "^1.3.3",
|
|
43
40
|
"loglevel": "^1.6.1",
|
|
41
|
+
"lru-cache": "^7.13.1",
|
|
44
42
|
"make-fetch-happen": "^10.1.2",
|
|
45
|
-
"pretty-format": "^
|
|
43
|
+
"pretty-format": "^28.0.0"
|
|
46
44
|
},
|
|
47
45
|
"peerDependencies": {
|
|
48
46
|
"graphql": "^15.8.0 || ^16.0.0"
|
|
49
47
|
},
|
|
50
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "302ecaeea4e20a4d594b92b94499d9e156f4cea6"
|
|
51
49
|
}
|
|
@@ -10,7 +10,7 @@ export type Scalars = {
|
|
|
10
10
|
Boolean: boolean;
|
|
11
11
|
Int: number;
|
|
12
12
|
Float: number;
|
|
13
|
-
/** ISO 8601, extended format with nanoseconds, Zulu (or
|
|
13
|
+
/** ISO 8601, extended format with nanoseconds, Zulu (or "[+-]seconds" as a string or number relative to now) */
|
|
14
14
|
Timestamp: any;
|
|
15
15
|
};
|
|
16
16
|
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildClientSchema,
|
|
3
3
|
getIntrospectionQuery,
|
|
4
|
+
GraphQLError,
|
|
4
5
|
GraphQLObjectType,
|
|
5
6
|
GraphQLSchema,
|
|
6
7
|
print,
|
|
7
8
|
} from 'graphql';
|
|
8
9
|
import gql from 'graphql-tag';
|
|
9
|
-
import { GraphQLRequestContext, VariableValues } from 'apollo-server-types';
|
|
10
|
-
import { AuthenticationError } from 'apollo-server-core';
|
|
11
10
|
import { buildOperationContext } from '../operationContext';
|
|
12
11
|
import { executeQueryPlan } from '../executeQueryPlan';
|
|
13
12
|
import { LocalGraphQLDataSource } from '../datasources/LocalGraphQLDataSource';
|
|
@@ -21,6 +20,7 @@ import { ApolloGateway } from '..';
|
|
|
21
20
|
import { ApolloServerBase as ApolloServer } from 'apollo-server-core';
|
|
22
21
|
import { getFederatedTestingSchema } from './execution-utils';
|
|
23
22
|
import { addResolversToSchema, GraphQLResolverMap } from '@apollo/subgraph/src/schema-helper';
|
|
23
|
+
import {GatewayGraphQLRequestContext} from '@apollo/server-gateway-interface';
|
|
24
24
|
|
|
25
25
|
expect.addSnapshotSerializer(astSerializer);
|
|
26
26
|
expect.addSnapshotSerializer(queryPlanSerializer);
|
|
@@ -53,8 +53,8 @@ describe('executeQueryPlan', () => {
|
|
|
53
53
|
});
|
|
54
54
|
|
|
55
55
|
function buildRequestContext(
|
|
56
|
-
variables:
|
|
57
|
-
):
|
|
56
|
+
variables: Record<string, any> = {},
|
|
57
|
+
): GatewayGraphQLRequestContext {
|
|
58
58
|
// @ts-ignore
|
|
59
59
|
return {
|
|
60
60
|
cache: undefined as any,
|
|
@@ -101,7 +101,16 @@ describe('executeQueryPlan', () => {
|
|
|
101
101
|
overrideResolversInService('accounts', {
|
|
102
102
|
RootQuery: {
|
|
103
103
|
me() {
|
|
104
|
-
|
|
104
|
+
// Must use old constructor for graphql@15 compat on v0.x branch.
|
|
105
|
+
throw new GraphQLError(
|
|
106
|
+
'Something went wrong',
|
|
107
|
+
undefined,
|
|
108
|
+
undefined,
|
|
109
|
+
undefined,
|
|
110
|
+
undefined,
|
|
111
|
+
undefined,
|
|
112
|
+
{ code: 'UNAUTHENTICATED' },
|
|
113
|
+
);
|
|
105
114
|
},
|
|
106
115
|
},
|
|
107
116
|
});
|
|
@@ -2,7 +2,6 @@ import {
|
|
|
2
2
|
GraphQLSchemaModule,
|
|
3
3
|
GraphQLResolverMap,
|
|
4
4
|
} from '@apollo/subgraph/src/schema-helper';
|
|
5
|
-
import { GraphQLRequest, GraphQLExecutionResult } from 'apollo-server-types';
|
|
6
5
|
import type { Logger } from '@apollo/utils.logger';
|
|
7
6
|
import {
|
|
8
7
|
composeAndValidate,
|
|
@@ -22,6 +21,7 @@ import { queryPlanSerializer, astSerializer } from 'apollo-federation-integratio
|
|
|
22
21
|
import gql from 'graphql-tag';
|
|
23
22
|
import { fixtures } from 'apollo-federation-integration-testsuite';
|
|
24
23
|
import { parse } from 'graphql';
|
|
24
|
+
import { GatewayExecutionResult, GatewayGraphQLRequest } from '@apollo/server-gateway-interface';
|
|
25
25
|
|
|
26
26
|
const prettyFormat = require('pretty-format');
|
|
27
27
|
|
|
@@ -39,10 +39,10 @@ export function overrideResolversInService(
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
export async function execute(
|
|
42
|
-
request:
|
|
42
|
+
request: GatewayGraphQLRequest,
|
|
43
43
|
services: ServiceDefinitionModule[] = fixtures,
|
|
44
44
|
logger: Logger = console,
|
|
45
|
-
): Promise<
|
|
45
|
+
): Promise<GatewayExecutionResult & { queryPlan: QueryPlan }> {
|
|
46
46
|
const serviceMap = Object.fromEntries(
|
|
47
47
|
services.map(({ name, typeDefs, resolvers }) => {
|
|
48
48
|
return [
|
|
@@ -48,7 +48,7 @@ describe('ApolloGateway executor', () => {
|
|
|
48
48
|
variables: { first: '3' },
|
|
49
49
|
},
|
|
50
50
|
queryHash: 'hashed',
|
|
51
|
-
context:
|
|
51
|
+
context: {},
|
|
52
52
|
cache: {} as any,
|
|
53
53
|
logger,
|
|
54
54
|
});
|
|
@@ -83,7 +83,7 @@ describe('ApolloGateway executor', () => {
|
|
|
83
83
|
document: gql(source),
|
|
84
84
|
request: {},
|
|
85
85
|
queryHash: 'hashed',
|
|
86
|
-
context:
|
|
86
|
+
context: {},
|
|
87
87
|
cache: {} as any,
|
|
88
88
|
logger,
|
|
89
89
|
});
|
package/src/config.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { GraphQLError, GraphQLSchema } from 'graphql';
|
|
2
2
|
import { HeadersInit } from 'node-fetch';
|
|
3
|
-
import {
|
|
3
|
+
import { GatewayGraphQLRequestContext } from '@apollo/server-gateway-interface';
|
|
4
4
|
import type { Logger } from '@apollo/utils.logger';
|
|
5
5
|
import { ServiceDefinition } from '@apollo/federation';
|
|
6
6
|
import { GraphQLDataSource } from './datasources/types';
|
|
@@ -20,9 +20,7 @@ export type Experimental_DidResolveQueryPlanCallback = ({
|
|
|
20
20
|
readonly queryPlan: QueryPlan;
|
|
21
21
|
readonly serviceMap: ServiceMap;
|
|
22
22
|
readonly operationContext: OperationContext;
|
|
23
|
-
readonly requestContext:
|
|
24
|
-
Record<string, any>
|
|
25
|
-
>;
|
|
23
|
+
readonly requestContext: GatewayGraphQLRequestContext;
|
|
26
24
|
}) => void;
|
|
27
25
|
|
|
28
26
|
interface ImplementingServiceLocation {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GatewayGraphQLResponse } from '@apollo/server-gateway-interface';
|
|
2
2
|
import {
|
|
3
3
|
GraphQLSchema,
|
|
4
4
|
graphql,
|
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
DocumentNode,
|
|
7
7
|
parse,
|
|
8
8
|
} from 'graphql';
|
|
9
|
-
import { enablePluginsForSchemaResolvers } from 'apollo-server-core/dist/utils/schemaInstrumentation';
|
|
10
9
|
import { GraphQLDataSource, GraphQLDataSourceProcessOptions } from './types';
|
|
11
10
|
|
|
12
11
|
export class LocalGraphQLDataSource<
|
|
@@ -14,13 +13,12 @@ export class LocalGraphQLDataSource<
|
|
|
14
13
|
> implements GraphQLDataSource<TContext>
|
|
15
14
|
{
|
|
16
15
|
constructor(public readonly schema: GraphQLSchema) {
|
|
17
|
-
enablePluginsForSchemaResolvers(schema);
|
|
18
16
|
}
|
|
19
17
|
|
|
20
18
|
async process({
|
|
21
19
|
request,
|
|
22
20
|
context,
|
|
23
|
-
}: GraphQLDataSourceProcessOptions<TContext>): Promise<
|
|
21
|
+
}: GraphQLDataSourceProcessOptions<TContext>): Promise<GatewayGraphQLResponse> {
|
|
24
22
|
return graphql({
|
|
25
23
|
schema: this.schema,
|
|
26
24
|
source: request.query!,
|
|
@@ -1,17 +1,3 @@
|
|
|
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
1
|
import { isObject } from '../utilities/predicates';
|
|
16
2
|
import { GraphQLDataSource, GraphQLDataSourceProcessOptions, GraphQLDataSourceRequestKind } from './types';
|
|
17
3
|
import { createHash } from '@apollo/utils.createhash';
|
|
@@ -19,6 +5,8 @@ import { parseCacheControlHeader } from './parseCacheControlHeader';
|
|
|
19
5
|
import fetcher from 'make-fetch-happen';
|
|
20
6
|
import { Headers as NodeFetchHeaders, Request as NodeFetchRequest } from 'node-fetch';
|
|
21
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';
|
|
22
10
|
|
|
23
11
|
export class RemoteGraphQLDataSource<
|
|
24
12
|
TContext extends Record<string, any> = Record<string, any>,
|
|
@@ -77,7 +65,7 @@ export class RemoteGraphQLDataSource<
|
|
|
77
65
|
|
|
78
66
|
async process(
|
|
79
67
|
options: GraphQLDataSourceProcessOptions<TContext>,
|
|
80
|
-
): Promise<
|
|
68
|
+
): Promise<GatewayGraphQLResponse> {
|
|
81
69
|
const { request, context: originalContext } = options;
|
|
82
70
|
// Deal with a bit of a hairy situation in typings: when doing health checks
|
|
83
71
|
// and schema checks we always pass in `{}` as the context even though it's
|
|
@@ -161,7 +149,7 @@ export class RemoteGraphQLDataSource<
|
|
|
161
149
|
// If APQ was enabled, we'll run the same request again, but add in the
|
|
162
150
|
// previously omitted `query`. If APQ was NOT enabled, this is the first
|
|
163
151
|
// request (non-APQ, all the way).
|
|
164
|
-
const requestWithQuery:
|
|
152
|
+
const requestWithQuery: GatewayGraphQLRequest = {
|
|
165
153
|
query,
|
|
166
154
|
...requestWithoutQuery,
|
|
167
155
|
};
|
|
@@ -175,9 +163,9 @@ export class RemoteGraphQLDataSource<
|
|
|
175
163
|
}
|
|
176
164
|
|
|
177
165
|
private async sendRequest(
|
|
178
|
-
request:
|
|
166
|
+
request: GatewayGraphQLRequest,
|
|
179
167
|
context: TContext,
|
|
180
|
-
): Promise<
|
|
168
|
+
): Promise<GatewayGraphQLResponse> {
|
|
181
169
|
// This would represent an internal programming error since this shouldn't
|
|
182
170
|
// be possible in the way that this method is invoked right now.
|
|
183
171
|
if (!request.http) {
|
|
@@ -230,7 +218,7 @@ export class RemoteGraphQLDataSource<
|
|
|
230
218
|
|
|
231
219
|
public willSendRequest?(
|
|
232
220
|
options: GraphQLDataSourceProcessOptions<TContext>,
|
|
233
|
-
):
|
|
221
|
+
): void | Promise<void>;
|
|
234
222
|
|
|
235
223
|
private async respond({
|
|
236
224
|
response,
|
|
@@ -238,11 +226,11 @@ export class RemoteGraphQLDataSource<
|
|
|
238
226
|
context,
|
|
239
227
|
overallCachePolicy,
|
|
240
228
|
}: {
|
|
241
|
-
response:
|
|
242
|
-
request:
|
|
229
|
+
response: GatewayGraphQLResponse;
|
|
230
|
+
request: GatewayGraphQLRequest;
|
|
243
231
|
context: TContext;
|
|
244
|
-
overallCachePolicy:
|
|
245
|
-
}): Promise<
|
|
232
|
+
overallCachePolicy: GatewayCachePolicy | null;
|
|
233
|
+
}): Promise<GatewayGraphQLResponse> {
|
|
246
234
|
const processedResponse =
|
|
247
235
|
typeof this.didReceiveResponse === 'function'
|
|
248
236
|
? await this.didReceiveResponse({ response, request, context })
|
|
@@ -257,16 +245,16 @@ export class RemoteGraphQLDataSource<
|
|
|
257
245
|
// thus the overall response) is uncacheable. (If you don't like this, you
|
|
258
246
|
// can tweak the `cache-control` header in your `didReceiveResponse`
|
|
259
247
|
// method.)
|
|
260
|
-
const hint:
|
|
248
|
+
const hint: GatewayCacheHint = { maxAge: 0 };
|
|
261
249
|
const maxAge = parsed['max-age'];
|
|
262
250
|
if (typeof maxAge === 'string' && maxAge.match(/^[0-9]+$/)) {
|
|
263
251
|
hint.maxAge = +maxAge;
|
|
264
252
|
}
|
|
265
253
|
if (parsed['private'] === true) {
|
|
266
|
-
hint.scope =
|
|
254
|
+
hint.scope = 'PRIVATE';
|
|
267
255
|
}
|
|
268
256
|
if (parsed['public'] === true) {
|
|
269
|
-
hint.scope =
|
|
257
|
+
hint.scope = 'PUBLIC';
|
|
270
258
|
}
|
|
271
259
|
overallCachePolicy.restrict(hint);
|
|
272
260
|
}
|
|
@@ -276,9 +264,9 @@ export class RemoteGraphQLDataSource<
|
|
|
276
264
|
|
|
277
265
|
public didReceiveResponse?(
|
|
278
266
|
requestContext: Required<
|
|
279
|
-
Pick<
|
|
267
|
+
Pick<GatewayGraphQLRequestContext<TContext>, 'request' | 'response' | 'context'>
|
|
280
268
|
>,
|
|
281
|
-
):
|
|
269
|
+
): GatewayGraphQLResponse | Promise<GatewayGraphQLResponse>;
|
|
282
270
|
|
|
283
271
|
public didEncounterError(
|
|
284
272
|
error: Error,
|
|
@@ -303,28 +291,36 @@ export class RemoteGraphQLDataSource<
|
|
|
303
291
|
}
|
|
304
292
|
|
|
305
293
|
public async errorFromResponse(response: FetcherResponse) {
|
|
306
|
-
const message = `${response.status}: ${response.statusText}`;
|
|
307
|
-
|
|
308
|
-
let error: ApolloError;
|
|
309
|
-
if (response.status === 401) {
|
|
310
|
-
error = new AuthenticationError(message);
|
|
311
|
-
} else if (response.status === 403) {
|
|
312
|
-
error = new ForbiddenError(message);
|
|
313
|
-
} else {
|
|
314
|
-
error = new ApolloError(message);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
294
|
const body = await this.parseBody(response);
|
|
318
295
|
|
|
319
|
-
|
|
296
|
+
const extensions: GraphQLErrorExtensions = {
|
|
320
297
|
response: {
|
|
321
298
|
url: response.url,
|
|
322
299
|
status: response.status,
|
|
323
300
|
statusText: response.statusText,
|
|
324
301
|
body,
|
|
325
302
|
},
|
|
326
|
-
}
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
if (response.status === 401) {
|
|
306
|
+
extensions.code = 'UNAUTHENTICATED';
|
|
307
|
+
} else if (response.status === 403) {
|
|
308
|
+
extensions.code = 'FORBIDDEN';
|
|
309
|
+
}
|
|
327
310
|
|
|
328
|
-
|
|
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
|
+
);
|
|
329
325
|
}
|
|
330
326
|
}
|
|
@@ -2,8 +2,8 @@ import { LocalGraphQLDataSource } from '../LocalGraphQLDataSource';
|
|
|
2
2
|
import { buildSubgraphSchema } from '@apollo/subgraph';
|
|
3
3
|
import gql from 'graphql-tag';
|
|
4
4
|
import { GraphQLResolverMap } from '@apollo/subgraph/src/schema-helper';
|
|
5
|
-
import { GraphQLRequestContext } from 'apollo-server-types';
|
|
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
|
|
|
@@ -1,15 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ApolloError,
|
|
3
|
-
AuthenticationError,
|
|
4
|
-
ForbiddenError,
|
|
5
|
-
} from 'apollo-server-errors';
|
|
6
|
-
|
|
7
1
|
import { RemoteGraphQLDataSource } from '../RemoteGraphQLDataSource';
|
|
8
2
|
import { Response, Headers } from 'node-fetch';
|
|
9
|
-
import { GraphQLRequestContext } from 'apollo-server-types';
|
|
10
3
|
import { GraphQLDataSourceRequestKind } from '../types';
|
|
11
4
|
import { nockBeforeEach, nockAfterEach } from '../../__tests__/nockAssertions';
|
|
12
5
|
import nock from 'nock';
|
|
6
|
+
import { GraphQLError } from 'graphql';
|
|
7
|
+
import { GatewayGraphQLRequestContext } from '@apollo/server-gateway-interface';
|
|
13
8
|
|
|
14
9
|
beforeEach(nockBeforeEach);
|
|
15
10
|
afterEach(nockAfterEach);
|
|
@@ -322,7 +317,7 @@ describe('didReceiveResponse', () => {
|
|
|
322
317
|
response,
|
|
323
318
|
}: Required<
|
|
324
319
|
Pick<
|
|
325
|
-
|
|
320
|
+
GatewayGraphQLRequestContext<MyContext>,
|
|
326
321
|
'request' | 'response' | 'context'
|
|
327
322
|
>
|
|
328
323
|
>) {
|
|
@@ -367,7 +362,7 @@ describe('didReceiveResponse', () => {
|
|
|
367
362
|
response,
|
|
368
363
|
}: Required<
|
|
369
364
|
Pick<
|
|
370
|
-
|
|
365
|
+
GatewayGraphQLRequestContext<MyContext>,
|
|
371
366
|
'request' | 'response' | 'context'
|
|
372
367
|
>
|
|
373
368
|
>) {
|
|
@@ -403,7 +398,7 @@ describe('didReceiveResponse', () => {
|
|
|
403
398
|
response,
|
|
404
399
|
}: Required<
|
|
405
400
|
Pick<
|
|
406
|
-
|
|
401
|
+
GatewayGraphQLRequestContext<MyContext>,
|
|
407
402
|
'request' | 'response' | 'context'
|
|
408
403
|
>
|
|
409
404
|
>) {
|
|
@@ -457,11 +452,11 @@ describe('didEncounterError', () => {
|
|
|
457
452
|
},
|
|
458
453
|
incomingRequestContext: {
|
|
459
454
|
context,
|
|
460
|
-
} as
|
|
455
|
+
} as GatewayGraphQLRequestContext<MyContext>,
|
|
461
456
|
context,
|
|
462
457
|
});
|
|
463
458
|
|
|
464
|
-
await expect(result).rejects.toThrow(
|
|
459
|
+
await expect(result).rejects.toThrow(GraphQLError);
|
|
465
460
|
expect(context).toMatchObject({
|
|
466
461
|
timingData: [{ time: 1616446845234 }],
|
|
467
462
|
});
|
|
@@ -469,7 +464,7 @@ describe('didEncounterError', () => {
|
|
|
469
464
|
});
|
|
470
465
|
|
|
471
466
|
describe('error handling', () => {
|
|
472
|
-
it('throws
|
|
467
|
+
it('throws error with code UNAUTHENTICATED when the response status is 401', async () => {
|
|
473
468
|
const DataSource = new RemoteGraphQLDataSource({
|
|
474
469
|
url: 'https://api.example.com/foo',
|
|
475
470
|
});
|
|
@@ -480,7 +475,7 @@ describe('error handling', () => {
|
|
|
480
475
|
...defaultProcessOptions,
|
|
481
476
|
request: { query: '{ me { name } }' },
|
|
482
477
|
});
|
|
483
|
-
await expect(result).rejects.toThrow(
|
|
478
|
+
await expect(result).rejects.toThrow(GraphQLError);
|
|
484
479
|
await expect(result).rejects.toMatchObject({
|
|
485
480
|
extensions: {
|
|
486
481
|
code: 'UNAUTHENTICATED',
|
|
@@ -492,7 +487,7 @@ describe('error handling', () => {
|
|
|
492
487
|
});
|
|
493
488
|
});
|
|
494
489
|
|
|
495
|
-
it('throws
|
|
490
|
+
it('throws an error with code FORBIDDEN when the response status is 403', async () => {
|
|
496
491
|
const DataSource = new RemoteGraphQLDataSource({
|
|
497
492
|
url: 'https://api.example.com/foo',
|
|
498
493
|
});
|
|
@@ -503,7 +498,7 @@ describe('error handling', () => {
|
|
|
503
498
|
...defaultProcessOptions,
|
|
504
499
|
request: { query: '{ me { name } }' },
|
|
505
500
|
});
|
|
506
|
-
await expect(result).rejects.toThrow(
|
|
501
|
+
await expect(result).rejects.toThrow(GraphQLError);
|
|
507
502
|
await expect(result).rejects.toMatchObject({
|
|
508
503
|
extensions: {
|
|
509
504
|
code: 'FORBIDDEN',
|
|
@@ -515,7 +510,7 @@ describe('error handling', () => {
|
|
|
515
510
|
});
|
|
516
511
|
});
|
|
517
512
|
|
|
518
|
-
it('throws
|
|
513
|
+
it('throws a GraphQLError when the response status is 500', async () => {
|
|
519
514
|
const DataSource = new RemoteGraphQLDataSource({
|
|
520
515
|
url: 'https://api.example.com/foo',
|
|
521
516
|
});
|
|
@@ -526,7 +521,7 @@ describe('error handling', () => {
|
|
|
526
521
|
...defaultProcessOptions,
|
|
527
522
|
request: { query: '{ me { name } }' },
|
|
528
523
|
});
|
|
529
|
-
await expect(result).rejects.toThrow(
|
|
524
|
+
await expect(result).rejects.toThrow(GraphQLError);
|
|
530
525
|
await expect(result).rejects.toMatchObject({
|
|
531
526
|
extensions: {
|
|
532
527
|
response: {
|
|
@@ -560,7 +555,7 @@ describe('error handling', () => {
|
|
|
560
555
|
...defaultProcessOptions,
|
|
561
556
|
request: { query: '{ me { name } }' },
|
|
562
557
|
});
|
|
563
|
-
await expect(result).rejects.toThrow(
|
|
558
|
+
await expect(result).rejects.toThrow(GraphQLError);
|
|
564
559
|
await expect(result).rejects.toMatchObject({
|
|
565
560
|
extensions: {
|
|
566
561
|
response: {
|
package/src/datasources/types.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GatewayGraphQLResponse, GatewayGraphQLRequestContext } from '@apollo/server-gateway-interface';
|
|
2
2
|
|
|
3
3
|
export interface GraphQLDataSource<
|
|
4
4
|
TContext extends Record<string, any> = Record<string, any>,
|
|
5
5
|
> {
|
|
6
6
|
process(
|
|
7
7
|
options: GraphQLDataSourceProcessOptions<TContext>,
|
|
8
|
-
): Promise<
|
|
8
|
+
): Promise<GatewayGraphQLResponse>;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export enum GraphQLDataSourceRequestKind {
|
|
@@ -20,7 +20,7 @@ export type GraphQLDataSourceProcessOptions<
|
|
|
20
20
|
/**
|
|
21
21
|
* The request to send to the subgraph.
|
|
22
22
|
*/
|
|
23
|
-
request:
|
|
23
|
+
request: GatewayGraphQLRequestContext<TContext>['request'];
|
|
24
24
|
} & (
|
|
25
25
|
| {
|
|
26
26
|
kind: GraphQLDataSourceRequestKind.INCOMING_OPERATION;
|
|
@@ -28,8 +28,15 @@ export type GraphQLDataSourceProcessOptions<
|
|
|
28
28
|
* The GraphQLRequestContext for the operation received by the gateway, or
|
|
29
29
|
* one of the strings if this operation is generated by the gateway without an
|
|
30
30
|
* incoming request.
|
|
31
|
+
*
|
|
32
|
+
* For backwards compatibility with Apollo Server 2, `overallCachePolicy` needs
|
|
33
|
+
* to be treated as optional.
|
|
31
34
|
*/
|
|
32
|
-
incomingRequestContext:
|
|
35
|
+
incomingRequestContext: Omit<
|
|
36
|
+
GatewayGraphQLRequestContext<TContext>,
|
|
37
|
+
'overallCachePolicy'
|
|
38
|
+
> &
|
|
39
|
+
Pick<Partial<GatewayGraphQLRequestContext<TContext>>, 'overallCachePolicy'>;
|
|
33
40
|
/**
|
|
34
41
|
* Equivalent to incomingRequestContext.context (provided here for
|
|
35
42
|
* backwards compatibility): the object created by the Apollo Server
|
|
@@ -38,7 +45,7 @@ export type GraphQLDataSourceProcessOptions<
|
|
|
38
45
|
* @deprecated Use `incomingRequestContext.context` instead (after
|
|
39
46
|
* checking `kind`).
|
|
40
47
|
*/
|
|
41
|
-
context:
|
|
48
|
+
context: GatewayGraphQLRequestContext<TContext>['context'];
|
|
42
49
|
}
|
|
43
50
|
| {
|
|
44
51
|
kind:
|
package/src/executeQueryPlan.ts
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
GraphQLExecutionResult,
|
|
3
|
-
GraphQLRequestContext,
|
|
4
|
-
VariableValues,
|
|
5
|
-
} from 'apollo-server-types';
|
|
6
1
|
import { Headers } from 'node-fetch';
|
|
7
2
|
import {
|
|
8
3
|
execute,
|
|
@@ -34,6 +29,7 @@ import { deepMerge } from './utilities/deepMerge';
|
|
|
34
29
|
import { isNotNullOrUndefined } from './utilities/array';
|
|
35
30
|
import { SpanStatusCode } from "@opentelemetry/api";
|
|
36
31
|
import { OpenTelemetrySpanNames, tracer } from "./utilities/opentelemetry";
|
|
32
|
+
import { GatewayGraphQLRequestContext, GatewayExecutionResult } from '@apollo/server-gateway-interface';
|
|
37
33
|
|
|
38
34
|
export type ServiceMap = {
|
|
39
35
|
[serviceName: string]: GraphQLDataSource;
|
|
@@ -41,20 +37,20 @@ export type ServiceMap = {
|
|
|
41
37
|
|
|
42
38
|
type ResultMap = Record<string, any>;
|
|
43
39
|
|
|
44
|
-
interface ExecutionContext
|
|
40
|
+
interface ExecutionContext {
|
|
45
41
|
queryPlan: QueryPlan;
|
|
46
42
|
operationContext: OperationContext;
|
|
47
43
|
serviceMap: ServiceMap;
|
|
48
|
-
requestContext:
|
|
44
|
+
requestContext: GatewayGraphQLRequestContext;
|
|
49
45
|
errors: GraphQLError[];
|
|
50
46
|
}
|
|
51
47
|
|
|
52
|
-
export async function executeQueryPlan
|
|
48
|
+
export async function executeQueryPlan(
|
|
53
49
|
queryPlan: QueryPlan,
|
|
54
50
|
serviceMap: ServiceMap,
|
|
55
|
-
requestContext:
|
|
51
|
+
requestContext: GatewayGraphQLRequestContext,
|
|
56
52
|
operationContext: OperationContext,
|
|
57
|
-
): Promise<
|
|
53
|
+
): Promise<GatewayExecutionResult> {
|
|
58
54
|
|
|
59
55
|
const logger = requestContext.logger || console;
|
|
60
56
|
|
|
@@ -62,7 +58,7 @@ export async function executeQueryPlan<TContext>(
|
|
|
62
58
|
try {
|
|
63
59
|
const errors: GraphQLError[] = [];
|
|
64
60
|
|
|
65
|
-
const context: ExecutionContext
|
|
61
|
+
const context: ExecutionContext = {
|
|
66
62
|
queryPlan,
|
|
67
63
|
operationContext,
|
|
68
64
|
serviceMap,
|
|
@@ -171,8 +167,8 @@ export async function executeQueryPlan<TContext>(
|
|
|
171
167
|
// we're going to ignore it, because it makes the code much simpler and more
|
|
172
168
|
// typesafe. However, it doesn't actually ask for traces from the backend
|
|
173
169
|
// service unless we are capturing traces for Studio.
|
|
174
|
-
async function executeNode
|
|
175
|
-
context: ExecutionContext
|
|
170
|
+
async function executeNode(
|
|
171
|
+
context: ExecutionContext,
|
|
176
172
|
node: PlanNode,
|
|
177
173
|
results: ResultMap | ResultMap[],
|
|
178
174
|
path: ResponsePath,
|
|
@@ -261,7 +257,7 @@ async function executeNode<TContext>(
|
|
|
261
257
|
|
|
262
258
|
export function shouldSkipFetchNode(
|
|
263
259
|
node: FetchNode,
|
|
264
|
-
variables:
|
|
260
|
+
variables: Record<string, any> = {},
|
|
265
261
|
) {
|
|
266
262
|
if (!node.inclusionConditions) return false;
|
|
267
263
|
|
|
@@ -284,8 +280,8 @@ export function shouldSkipFetchNode(
|
|
|
284
280
|
});
|
|
285
281
|
}
|
|
286
282
|
|
|
287
|
-
async function executeFetch
|
|
288
|
-
context: ExecutionContext
|
|
283
|
+
async function executeFetch(
|
|
284
|
+
context: ExecutionContext,
|
|
289
285
|
fetch: FetchNode,
|
|
290
286
|
results: ResultMap | (ResultMap | null | undefined)[],
|
|
291
287
|
_path: ResponsePath,
|
|
@@ -404,7 +400,7 @@ async function executeFetch<TContext>(
|
|
|
404
400
|
}
|
|
405
401
|
});
|
|
406
402
|
async function sendOperation(
|
|
407
|
-
context: ExecutionContext
|
|
403
|
+
context: ExecutionContext,
|
|
408
404
|
source: string,
|
|
409
405
|
variables: Record<string, any>,
|
|
410
406
|
operationName: string | undefined,
|