@apollo/gateway 0.50.2 → 0.52.1
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 +28 -28
- 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 +12 -33
- 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/package.json +10 -12
- 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/buildService.test.ts +81 -83
- package/src/__tests__/gateway/executor.test.ts +20 -17
- package/src/__tests__/gateway/opentelemetry.test.ts +3 -7
- package/src/__tests__/gateway/supergraphSdl.test.ts +10 -12
- package/src/config.ts +4 -6
- package/src/datasources/LocalGraphQLDataSource.ts +2 -4
- package/src/datasources/RemoteGraphQLDataSource.ts +72 -59
- package/src/datasources/__tests__/LocalGraphQLDataSource.test.ts +2 -2
- package/src/datasources/__tests__/RemoteGraphQLDataSource.test.ts +120 -159
- package/src/datasources/types.ts +5 -5
- package/src/executeQueryPlan.ts +18 -18
- package/src/index.ts +24 -70
- 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/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/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/index.ts
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import { deprecate } from 'util';
|
|
2
|
-
import { GraphQLService, Unsubscriber } from 'apollo-server-core';
|
|
3
|
-
import {
|
|
4
|
-
GraphQLExecutionResult,
|
|
5
|
-
GraphQLRequestContextExecutionDidStart,
|
|
6
|
-
} from 'apollo-server-types';
|
|
7
2
|
import type { Logger } from '@apollo/utils.logger';
|
|
8
|
-
import
|
|
3
|
+
import LRUCache from 'lru-cache';
|
|
9
4
|
import {
|
|
10
5
|
isObjectType,
|
|
11
6
|
isIntrospectionType,
|
|
@@ -31,8 +26,6 @@ import {
|
|
|
31
26
|
import { RemoteGraphQLDataSource } from './datasources/RemoteGraphQLDataSource';
|
|
32
27
|
import { getVariableValues } from 'graphql/execution/values';
|
|
33
28
|
import fetcher from 'make-fetch-happen';
|
|
34
|
-
import { HttpRequestCache } from './cache';
|
|
35
|
-
import { fetch } from 'apollo-server-env';
|
|
36
29
|
import {
|
|
37
30
|
QueryPlanner,
|
|
38
31
|
QueryPlan,
|
|
@@ -69,6 +62,8 @@ import {
|
|
|
69
62
|
LegacyFetcher,
|
|
70
63
|
LocalCompose,
|
|
71
64
|
} from './supergraphManagers';
|
|
65
|
+
import { Fetcher } from '@apollo/utils.fetcher';
|
|
66
|
+
import {GatewayInterface, GatewayUnsubscriber, GatewayGraphQLRequestContext, GatewayExecutionResult} from '@apollo/server-gateway-interface';
|
|
72
67
|
|
|
73
68
|
type DataSourceMap = {
|
|
74
69
|
[serviceName: string]: { url?: string; dataSource: GraphQLDataSource };
|
|
@@ -83,30 +78,6 @@ type WarnedStates = {
|
|
|
83
78
|
remoteWithLocalConfig?: boolean;
|
|
84
79
|
};
|
|
85
80
|
|
|
86
|
-
export function getDefaultFetcher() {
|
|
87
|
-
const { name, version } = require('../package.json');
|
|
88
|
-
return fetcher.defaults({
|
|
89
|
-
cacheManager: new HttpRequestCache(),
|
|
90
|
-
// All headers should be lower-cased here, as `make-fetch-happen`
|
|
91
|
-
// treats differently cased headers as unique (unlike the `Headers` object).
|
|
92
|
-
// @see: https://git.io/JvRUa
|
|
93
|
-
headers: {
|
|
94
|
-
'apollographql-client-name': name,
|
|
95
|
-
'apollographql-client-version': version,
|
|
96
|
-
'user-agent': `${name}/${version}`,
|
|
97
|
-
'content-type': 'application/json',
|
|
98
|
-
},
|
|
99
|
-
retry: {
|
|
100
|
-
retries: 5,
|
|
101
|
-
// The default factor: expected attempts at 0, 1, 3, 7, 15, and 31 seconds elapsed
|
|
102
|
-
factor: 2,
|
|
103
|
-
// 1 second
|
|
104
|
-
minTimeout: 1000,
|
|
105
|
-
randomize: true,
|
|
106
|
-
},
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
|
|
110
81
|
export const HEALTH_CHECK_QUERY =
|
|
111
82
|
'query __ApolloServiceHealthCheck__ { __typename }';
|
|
112
83
|
export const SERVICE_DEFINITION_QUERY =
|
|
@@ -148,12 +119,12 @@ interface GraphQLServiceEngineConfig {
|
|
|
148
119
|
graphVariant?: string;
|
|
149
120
|
}
|
|
150
121
|
|
|
151
|
-
export class ApolloGateway implements
|
|
122
|
+
export class ApolloGateway implements GatewayInterface {
|
|
152
123
|
public schema?: GraphQLSchema;
|
|
153
124
|
private serviceMap: DataSourceMap = Object.create(null);
|
|
154
125
|
private config: GatewayConfig;
|
|
155
126
|
private logger: Logger;
|
|
156
|
-
private queryPlanStore:
|
|
127
|
+
private queryPlanStore: LRUCache<string, QueryPlan>;
|
|
157
128
|
private apolloConfig?: ApolloConfigFromAS3;
|
|
158
129
|
private onSchemaChangeListeners = new Set<(schema: GraphQLSchema) => void>();
|
|
159
130
|
private onSchemaLoadOrUpdateListeners = new Set<
|
|
@@ -166,7 +137,7 @@ export class ApolloGateway implements GraphQLService {
|
|
|
166
137
|
private queryPlanner?: QueryPlanner;
|
|
167
138
|
private supergraphSdl?: string;
|
|
168
139
|
private parsedSupergraphSdl?: DocumentNode;
|
|
169
|
-
private fetcher:
|
|
140
|
+
private fetcher: Fetcher;
|
|
170
141
|
private compositionId?: string;
|
|
171
142
|
private state: GatewayState;
|
|
172
143
|
|
|
@@ -194,7 +165,7 @@ export class ApolloGateway implements GraphQLService {
|
|
|
194
165
|
this.queryPlanStore = this.initQueryPlanStore(
|
|
195
166
|
config?.experimental_approximateQueryPlanStoreMiB,
|
|
196
167
|
);
|
|
197
|
-
this.fetcher = config?.fetcher ||
|
|
168
|
+
this.fetcher = config?.fetcher || fetcher;
|
|
198
169
|
|
|
199
170
|
// set up experimental observability callbacks and config settings
|
|
200
171
|
this.experimental_didResolveQueryPlan =
|
|
@@ -235,14 +206,14 @@ export class ApolloGateway implements GraphQLService {
|
|
|
235
206
|
}
|
|
236
207
|
|
|
237
208
|
private initQueryPlanStore(approximateQueryPlanStoreMiB?: number) {
|
|
238
|
-
return new
|
|
209
|
+
return new LRUCache<string, QueryPlan>({
|
|
239
210
|
// Create ~about~ a 30MiB InMemoryLRUCache. This is less than precise
|
|
240
211
|
// since the technique to calculate the size of a DocumentNode is
|
|
241
212
|
// only using JSON.stringify on the DocumentNode (and thus doesn't account
|
|
242
213
|
// for unicode characters, etc.), but it should do a reasonable job at
|
|
243
214
|
// providing a caching document store for most operations.
|
|
244
215
|
maxSize: Math.pow(2, 20) * (approximateQueryPlanStoreMiB || 30),
|
|
245
|
-
|
|
216
|
+
sizeCalculation: approximateObjectSize,
|
|
246
217
|
});
|
|
247
218
|
}
|
|
248
219
|
|
|
@@ -632,7 +603,7 @@ export class ApolloGateway implements GraphQLService {
|
|
|
632
603
|
// Once we remove the deprecated onSchemaChange() method, we can remove this.
|
|
633
604
|
legacyDontNotifyOnSchemaChangeListeners: boolean = false,
|
|
634
605
|
): void {
|
|
635
|
-
|
|
606
|
+
this.queryPlanStore.clear();
|
|
636
607
|
this.schema = toAPISchema(coreSchema);
|
|
637
608
|
this.queryPlanner = new QueryPlanner(coreSchema);
|
|
638
609
|
|
|
@@ -743,7 +714,7 @@ export class ApolloGateway implements GraphQLService {
|
|
|
743
714
|
*/
|
|
744
715
|
public onSchemaChange(
|
|
745
716
|
callback: (schema: GraphQLSchema) => void,
|
|
746
|
-
):
|
|
717
|
+
): GatewayUnsubscriber {
|
|
747
718
|
this.onSchemaChangeListeners.add(callback);
|
|
748
719
|
|
|
749
720
|
return () => {
|
|
@@ -756,7 +727,7 @@ export class ApolloGateway implements GraphQLService {
|
|
|
756
727
|
apiSchema: GraphQLSchema;
|
|
757
728
|
coreSupergraphSdl: string;
|
|
758
729
|
}) => void,
|
|
759
|
-
):
|
|
730
|
+
): GatewayUnsubscriber {
|
|
760
731
|
this.onSchemaLoadOrUpdateListeners.add(callback);
|
|
761
732
|
|
|
762
733
|
return () => {
|
|
@@ -836,9 +807,9 @@ export class ApolloGateway implements GraphQLService {
|
|
|
836
807
|
// ApolloServerPluginUsageReporting) assumes that. In fact, errors talking to backends
|
|
837
808
|
// are unlikely to show up as GraphQLErrors. Do we need to use
|
|
838
809
|
// formatApolloErrors or something?
|
|
839
|
-
public executor = async
|
|
840
|
-
requestContext:
|
|
841
|
-
): Promise<
|
|
810
|
+
public executor = async (
|
|
811
|
+
requestContext: GatewayGraphQLRequestContext,
|
|
812
|
+
): Promise<GatewayExecutionResult> => {
|
|
842
813
|
const spanAttributes = requestContext.operationName
|
|
843
814
|
? { operationName: requestContext.operationName }
|
|
844
815
|
: {};
|
|
@@ -868,10 +839,7 @@ export class ApolloGateway implements GraphQLService {
|
|
|
868
839
|
return { errors: validationErrors };
|
|
869
840
|
}
|
|
870
841
|
|
|
871
|
-
let queryPlan
|
|
872
|
-
if (this.queryPlanStore) {
|
|
873
|
-
queryPlan = await this.queryPlanStore.get(queryPlanStoreKey);
|
|
874
|
-
}
|
|
842
|
+
let queryPlan = this.queryPlanStore.get(queryPlanStoreKey);
|
|
875
843
|
|
|
876
844
|
if (!queryPlan) {
|
|
877
845
|
queryPlan = tracer.startActiveSpan(
|
|
@@ -893,25 +861,11 @@ export class ApolloGateway implements GraphQLService {
|
|
|
893
861
|
},
|
|
894
862
|
);
|
|
895
863
|
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
// whether or not it's successful at this point. We'll instead proceed
|
|
902
|
-
// to serve the rest of the request and just hope that this works out.
|
|
903
|
-
// If it doesn't work, the next request will have another opportunity to
|
|
904
|
-
// try again. Errors will surface as warnings, as appropriate.
|
|
905
|
-
//
|
|
906
|
-
// While it shouldn't normally be necessary to wrap this `Promise` in a
|
|
907
|
-
// `Promise.resolve` invocation, it seems that the underlying cache store
|
|
908
|
-
// is returning a non-native `Promise` (e.g. Bluebird, etc.).
|
|
909
|
-
Promise.resolve(
|
|
910
|
-
this.queryPlanStore.set(queryPlanStoreKey, queryPlan),
|
|
911
|
-
).catch((err) =>
|
|
912
|
-
this.logger.warn(
|
|
913
|
-
'Could not store queryPlan' + ((err && err.message) || err),
|
|
914
|
-
),
|
|
864
|
+
try {
|
|
865
|
+
this.queryPlanStore.set(queryPlanStoreKey, queryPlan);
|
|
866
|
+
} catch (err) {
|
|
867
|
+
this.logger.warn(
|
|
868
|
+
'Could not store queryPlan' + ((err && err.message) || err),
|
|
915
869
|
);
|
|
916
870
|
}
|
|
917
871
|
}
|
|
@@ -933,7 +887,7 @@ export class ApolloGateway implements GraphQLService {
|
|
|
933
887
|
});
|
|
934
888
|
}
|
|
935
889
|
|
|
936
|
-
const response = await executeQueryPlan
|
|
890
|
+
const response = await executeQueryPlan(
|
|
937
891
|
queryPlan,
|
|
938
892
|
serviceMap,
|
|
939
893
|
requestContext,
|
|
@@ -990,8 +944,8 @@ export class ApolloGateway implements GraphQLService {
|
|
|
990
944
|
);
|
|
991
945
|
};
|
|
992
946
|
|
|
993
|
-
private validateIncomingRequest
|
|
994
|
-
requestContext:
|
|
947
|
+
private validateIncomingRequest(
|
|
948
|
+
requestContext: GatewayGraphQLRequestContext,
|
|
995
949
|
operationContext: OperationContext,
|
|
996
950
|
) {
|
|
997
951
|
return tracer.startActiveSpan(OpenTelemetrySpanNames.VALIDATE, (span) => {
|
|
@@ -119,11 +119,6 @@ describe('IntrospectAndCompose', () => {
|
|
|
119
119
|
|
|
120
120
|
// TODO: useFakeTimers (though I'm struggling to get this to work as expected)
|
|
121
121
|
it("doesn't call `update` when there's no change to the supergraph", async () => {
|
|
122
|
-
const fetcher =
|
|
123
|
-
jest.requireActual<typeof import('apollo-server-env')>(
|
|
124
|
-
'apollo-server-env',
|
|
125
|
-
).fetch;
|
|
126
|
-
|
|
127
122
|
// mock for initial load and a few polls against an unchanging schema
|
|
128
123
|
mockAllServicesSdlQuerySuccess();
|
|
129
124
|
mockAllServicesSdlQuerySuccess();
|
|
@@ -144,7 +139,6 @@ describe('IntrospectAndCompose', () => {
|
|
|
144
139
|
getDataSource({ url }) {
|
|
145
140
|
return new RemoteGraphQLDataSource({
|
|
146
141
|
url,
|
|
147
|
-
fetcher,
|
|
148
142
|
});
|
|
149
143
|
},
|
|
150
144
|
});
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { GraphQLRequest } from 'apollo-server-types';
|
|
2
1
|
import { parse } from 'graphql';
|
|
3
2
|
import { Headers, HeadersInit } from 'node-fetch';
|
|
4
3
|
import { GraphQLDataSource, GraphQLDataSourceRequestKind } from '../../datasources/types';
|
|
5
4
|
import { SERVICE_DEFINITION_QUERY } from '../..';
|
|
6
5
|
import { ServiceDefinitionUpdate, ServiceEndpointDefinition } from '../../config';
|
|
7
6
|
import { ServiceDefinition } from '@apollo/federation';
|
|
7
|
+
import { GatewayGraphQLRequest } from '@apollo/server-gateway-interface';
|
|
8
8
|
|
|
9
9
|
export type Service = ServiceEndpointDefinition & {
|
|
10
10
|
dataSource: GraphQLDataSource;
|
|
@@ -35,7 +35,7 @@ export async function loadServicesFromRemoteEndpoint({
|
|
|
35
35
|
`Tried to load schema for '${name}' but no 'url' was specified.`);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
const request:
|
|
38
|
+
const request: GatewayGraphQLRequest = {
|
|
39
39
|
query: SERVICE_DEFINITION_QUERY,
|
|
40
40
|
http: {
|
|
41
41
|
url,
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
loadSupergraphSdlFromUplinks,
|
|
4
4
|
UplinkFetcherError,
|
|
5
5
|
} from '../loadSupergraphSdlFromStorage';
|
|
6
|
-
import
|
|
6
|
+
import fetcher from 'make-fetch-happen';
|
|
7
7
|
import {
|
|
8
8
|
graphRef,
|
|
9
9
|
apiKey,
|
|
@@ -14,10 +14,13 @@ import {
|
|
|
14
14
|
mockOutOfBandReportRequestSuccess,
|
|
15
15
|
mockSupergraphSdlRequestSuccess,
|
|
16
16
|
mockSupergraphSdlRequestIfAfterUnchanged,
|
|
17
|
-
mockSupergraphSdlRequestIfAfter
|
|
17
|
+
mockSupergraphSdlRequestIfAfter,
|
|
18
18
|
} from '../../../__tests__/integration/nockMocks';
|
|
19
|
-
import { getTestingSupergraphSdl } from
|
|
20
|
-
import {
|
|
19
|
+
import { getTestingSupergraphSdl } from '../../../__tests__/execution-utils';
|
|
20
|
+
import {
|
|
21
|
+
nockAfterEach,
|
|
22
|
+
nockBeforeEach,
|
|
23
|
+
} from '../../../__tests__/nockAssertions';
|
|
21
24
|
|
|
22
25
|
describe('loadSupergraphSdlFromStorage', () => {
|
|
23
26
|
beforeEach(nockBeforeEach);
|
|
@@ -25,7 +28,6 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
25
28
|
|
|
26
29
|
it('fetches Supergraph SDL as expected', async () => {
|
|
27
30
|
mockSupergraphSdlRequestSuccess();
|
|
28
|
-
const fetcher = getDefaultFetcher();
|
|
29
31
|
const result = await loadSupergraphSdlFromStorage({
|
|
30
32
|
graphRef,
|
|
31
33
|
apiKey,
|
|
@@ -42,27 +44,29 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
42
44
|
|
|
43
45
|
it('Queries alternate Uplink URL if first one fails', async () => {
|
|
44
46
|
mockSupergraphSdlRequest('originalId-1234', mockCloudConfigUrl1).reply(500);
|
|
45
|
-
mockSupergraphSdlRequestIfAfter(
|
|
47
|
+
mockSupergraphSdlRequestIfAfter(
|
|
48
|
+
'originalId-1234',
|
|
49
|
+
mockCloudConfigUrl2,
|
|
50
|
+
).reply(
|
|
46
51
|
200,
|
|
47
52
|
JSON.stringify({
|
|
48
53
|
data: {
|
|
49
54
|
routerConfig: {
|
|
50
55
|
__typename: 'RouterConfigResult',
|
|
51
56
|
id: 'originalId-1234',
|
|
52
|
-
supergraphSdl: getTestingSupergraphSdl()
|
|
57
|
+
supergraphSdl: getTestingSupergraphSdl(),
|
|
53
58
|
},
|
|
54
59
|
},
|
|
55
60
|
}),
|
|
56
61
|
);
|
|
57
62
|
|
|
58
|
-
const fetcher = getDefaultFetcher();
|
|
59
63
|
const result = await loadSupergraphSdlFromUplinks({
|
|
60
64
|
graphRef,
|
|
61
65
|
apiKey,
|
|
62
66
|
endpoints: [mockCloudConfigUrl1, mockCloudConfigUrl2],
|
|
63
67
|
errorReportingEndpoint: undefined,
|
|
64
68
|
fetcher,
|
|
65
|
-
compositionId:
|
|
69
|
+
compositionId: 'originalId-1234',
|
|
66
70
|
maxRetries: 1,
|
|
67
71
|
roundRobinSeed: 0,
|
|
68
72
|
earliestFetchTime: null,
|
|
@@ -75,10 +79,12 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
75
79
|
});
|
|
76
80
|
|
|
77
81
|
it('Throws error if all Uplink URLs fail', async () => {
|
|
78
|
-
mockSupergraphSdlRequest(
|
|
79
|
-
mockSupergraphSdlRequestIfAfter(
|
|
82
|
+
mockSupergraphSdlRequest('originalId-1234', mockCloudConfigUrl1).reply(500);
|
|
83
|
+
mockSupergraphSdlRequestIfAfter(
|
|
84
|
+
'originalId-1234',
|
|
85
|
+
mockCloudConfigUrl2,
|
|
86
|
+
).reply(500);
|
|
80
87
|
|
|
81
|
-
const fetcher = getDefaultFetcher();
|
|
82
88
|
await expect(
|
|
83
89
|
loadSupergraphSdlFromUplinks({
|
|
84
90
|
graphRef,
|
|
@@ -86,23 +92,22 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
86
92
|
endpoints: [mockCloudConfigUrl1, mockCloudConfigUrl2],
|
|
87
93
|
errorReportingEndpoint: undefined,
|
|
88
94
|
fetcher,
|
|
89
|
-
compositionId:
|
|
95
|
+
compositionId: 'originalId-1234',
|
|
90
96
|
maxRetries: 1,
|
|
91
97
|
roundRobinSeed: 0,
|
|
92
98
|
earliestFetchTime: null,
|
|
93
99
|
}),
|
|
94
100
|
).rejects.toThrowError(
|
|
95
101
|
new UplinkFetcherError(
|
|
96
|
-
|
|
97
|
-
)
|
|
102
|
+
'An error occurred while fetching your schema from Apollo: 500 Internal Server Error',
|
|
103
|
+
),
|
|
98
104
|
);
|
|
99
|
-
})
|
|
105
|
+
});
|
|
100
106
|
|
|
101
107
|
describe('errors', () => {
|
|
102
108
|
it('throws on a malformed response', async () => {
|
|
103
109
|
mockSupergraphSdlRequest().reply(200, 'Invalid JSON');
|
|
104
110
|
|
|
105
|
-
const fetcher = getDefaultFetcher();
|
|
106
111
|
await expect(
|
|
107
112
|
loadSupergraphSdlFromStorage({
|
|
108
113
|
graphRef,
|
|
@@ -114,8 +119,8 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
114
119
|
}),
|
|
115
120
|
).rejects.toThrowError(
|
|
116
121
|
new UplinkFetcherError(
|
|
117
|
-
|
|
118
|
-
)
|
|
122
|
+
'An error occurred while fetching your schema from Apollo: 200 invalid json response body at https://example1.cloud-config-url.com/cloudconfig/ reason: Unexpected token I in JSON at position 0',
|
|
123
|
+
),
|
|
119
124
|
);
|
|
120
125
|
});
|
|
121
126
|
|
|
@@ -128,7 +133,6 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
128
133
|
}),
|
|
129
134
|
);
|
|
130
135
|
|
|
131
|
-
const fetcher = getDefaultFetcher();
|
|
132
136
|
await expect(
|
|
133
137
|
loadSupergraphSdlFromStorage({
|
|
134
138
|
graphRef,
|
|
@@ -139,7 +143,9 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
139
143
|
compositionId: null,
|
|
140
144
|
}),
|
|
141
145
|
).rejects.toThrowError(
|
|
142
|
-
new UplinkFetcherError(
|
|
146
|
+
new UplinkFetcherError(
|
|
147
|
+
`An error occurred while fetching your schema from Apollo: \n${message}`,
|
|
148
|
+
),
|
|
143
149
|
);
|
|
144
150
|
});
|
|
145
151
|
|
|
@@ -147,7 +153,6 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
147
153
|
mockSupergraphSdlRequest().reply(500);
|
|
148
154
|
mockOutOfBandReportRequestSuccess();
|
|
149
155
|
|
|
150
|
-
const fetcher = getDefaultFetcher();
|
|
151
156
|
await expect(
|
|
152
157
|
loadSupergraphSdlFromStorage({
|
|
153
158
|
graphRef,
|
|
@@ -159,8 +164,8 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
159
164
|
}),
|
|
160
165
|
).rejects.toThrowError(
|
|
161
166
|
new UplinkFetcherError(
|
|
162
|
-
|
|
163
|
-
)
|
|
167
|
+
'An error occurred while fetching your schema from Apollo: 500 Internal Server Error',
|
|
168
|
+
),
|
|
164
169
|
);
|
|
165
170
|
});
|
|
166
171
|
|
|
@@ -169,7 +174,6 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
169
174
|
it("Out of band reporting doesn't submit reports when endpoint is not configured", async () => {
|
|
170
175
|
mockSupergraphSdlRequest().reply(400);
|
|
171
176
|
|
|
172
|
-
const fetcher = getDefaultFetcher();
|
|
173
177
|
await expect(
|
|
174
178
|
loadSupergraphSdlFromStorage({
|
|
175
179
|
graphRef,
|
|
@@ -181,16 +185,14 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
181
185
|
}),
|
|
182
186
|
).rejects.toThrowError(
|
|
183
187
|
new UplinkFetcherError(
|
|
184
|
-
|
|
185
|
-
)
|
|
188
|
+
'An error occurred while fetching your schema from Apollo: 400 invalid json response body at https://example1.cloud-config-url.com/cloudconfig/ reason: Unexpected end of JSON input',
|
|
189
|
+
),
|
|
186
190
|
);
|
|
187
191
|
});
|
|
188
192
|
|
|
189
193
|
it('throws on 400 status response and does not submit an out of band error', async () => {
|
|
190
|
-
|
|
191
194
|
mockSupergraphSdlRequest().reply(400);
|
|
192
195
|
|
|
193
|
-
const fetcher = getDefaultFetcher();
|
|
194
196
|
await expect(
|
|
195
197
|
loadSupergraphSdlFromStorage({
|
|
196
198
|
graphRef,
|
|
@@ -202,8 +204,8 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
202
204
|
}),
|
|
203
205
|
).rejects.toThrowError(
|
|
204
206
|
new UplinkFetcherError(
|
|
205
|
-
|
|
206
|
-
)
|
|
207
|
+
'An error occurred while fetching your schema from Apollo: 400 invalid json response body at https://example1.cloud-config-url.com/cloudconfig/ reason: Unexpected end of JSON input',
|
|
208
|
+
),
|
|
207
209
|
);
|
|
208
210
|
});
|
|
209
211
|
|
|
@@ -211,7 +213,6 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
211
213
|
mockSupergraphSdlRequest().reply(413);
|
|
212
214
|
mockOutOfBandReportRequestSuccess();
|
|
213
215
|
|
|
214
|
-
const fetcher = getDefaultFetcher();
|
|
215
216
|
await expect(
|
|
216
217
|
loadSupergraphSdlFromStorage({
|
|
217
218
|
graphRef,
|
|
@@ -223,8 +224,8 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
223
224
|
}),
|
|
224
225
|
).rejects.toThrowError(
|
|
225
226
|
new UplinkFetcherError(
|
|
226
|
-
|
|
227
|
-
)
|
|
227
|
+
'An error occurred while fetching your schema from Apollo: 413 Payload Too Large',
|
|
228
|
+
),
|
|
228
229
|
);
|
|
229
230
|
});
|
|
230
231
|
|
|
@@ -232,7 +233,6 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
232
233
|
mockSupergraphSdlRequest().reply(422);
|
|
233
234
|
mockOutOfBandReportRequestSuccess();
|
|
234
235
|
|
|
235
|
-
const fetcher = getDefaultFetcher();
|
|
236
236
|
await expect(
|
|
237
237
|
loadSupergraphSdlFromStorage({
|
|
238
238
|
graphRef,
|
|
@@ -244,8 +244,8 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
244
244
|
}),
|
|
245
245
|
).rejects.toThrowError(
|
|
246
246
|
new UplinkFetcherError(
|
|
247
|
-
|
|
248
|
-
)
|
|
247
|
+
'An error occurred while fetching your schema from Apollo: 422 Unprocessable Entity',
|
|
248
|
+
),
|
|
249
249
|
);
|
|
250
250
|
});
|
|
251
251
|
|
|
@@ -253,7 +253,6 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
253
253
|
mockSupergraphSdlRequest().reply(408);
|
|
254
254
|
mockOutOfBandReportRequestSuccess();
|
|
255
255
|
|
|
256
|
-
const fetcher = getDefaultFetcher();
|
|
257
256
|
await expect(
|
|
258
257
|
loadSupergraphSdlFromStorage({
|
|
259
258
|
graphRef,
|
|
@@ -265,18 +264,16 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
265
264
|
}),
|
|
266
265
|
).rejects.toThrowError(
|
|
267
266
|
new UplinkFetcherError(
|
|
268
|
-
|
|
269
|
-
)
|
|
267
|
+
'An error occurred while fetching your schema from Apollo: 408 Request Timeout',
|
|
268
|
+
),
|
|
270
269
|
);
|
|
271
270
|
});
|
|
272
271
|
});
|
|
273
272
|
|
|
274
273
|
it('throws on 504 status response and successfully submits an out of band error', async () => {
|
|
275
|
-
|
|
276
274
|
mockSupergraphSdlRequest().reply(504);
|
|
277
275
|
mockOutOfBandReportRequestSuccess();
|
|
278
276
|
|
|
279
|
-
const fetcher = getDefaultFetcher();
|
|
280
277
|
await expect(
|
|
281
278
|
loadSupergraphSdlFromStorage({
|
|
282
279
|
graphRef,
|
|
@@ -288,8 +285,8 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
288
285
|
}),
|
|
289
286
|
).rejects.toThrowError(
|
|
290
287
|
new UplinkFetcherError(
|
|
291
|
-
|
|
292
|
-
)
|
|
288
|
+
'An error occurred while fetching your schema from Apollo: 504 Gateway Timeout',
|
|
289
|
+
),
|
|
293
290
|
);
|
|
294
291
|
});
|
|
295
292
|
|
|
@@ -297,7 +294,6 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
297
294
|
mockSupergraphSdlRequest().replyWithError('no response');
|
|
298
295
|
mockOutOfBandReportRequestSuccess();
|
|
299
296
|
|
|
300
|
-
const fetcher = getDefaultFetcher();
|
|
301
297
|
await expect(
|
|
302
298
|
loadSupergraphSdlFromStorage({
|
|
303
299
|
graphRef,
|
|
@@ -309,8 +305,8 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
309
305
|
}),
|
|
310
306
|
).rejects.toThrowError(
|
|
311
307
|
new UplinkFetcherError(
|
|
312
|
-
|
|
313
|
-
)
|
|
308
|
+
'An error occurred while fetching your schema from Apollo: request to https://example1.cloud-config-url.com/cloudconfig/ failed, reason: no response',
|
|
309
|
+
),
|
|
314
310
|
);
|
|
315
311
|
});
|
|
316
312
|
|
|
@@ -318,7 +314,6 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
318
314
|
mockSupergraphSdlRequest().reply(502);
|
|
319
315
|
mockOutOfBandReportRequestSuccess();
|
|
320
316
|
|
|
321
|
-
const fetcher = getDefaultFetcher();
|
|
322
317
|
await expect(
|
|
323
318
|
loadSupergraphSdlFromStorage({
|
|
324
319
|
graphRef,
|
|
@@ -330,8 +325,8 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
330
325
|
}),
|
|
331
326
|
).rejects.toThrowError(
|
|
332
327
|
new UplinkFetcherError(
|
|
333
|
-
|
|
334
|
-
)
|
|
328
|
+
'An error occurred while fetching your schema from Apollo: 502 Bad Gateway',
|
|
329
|
+
),
|
|
335
330
|
);
|
|
336
331
|
});
|
|
337
332
|
|
|
@@ -339,7 +334,6 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
339
334
|
mockSupergraphSdlRequest().reply(503);
|
|
340
335
|
mockOutOfBandReportRequestSuccess();
|
|
341
336
|
|
|
342
|
-
const fetcher = getDefaultFetcher();
|
|
343
337
|
await expect(
|
|
344
338
|
loadSupergraphSdlFromStorage({
|
|
345
339
|
graphRef,
|
|
@@ -351,69 +345,72 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
351
345
|
}),
|
|
352
346
|
).rejects.toThrowError(
|
|
353
347
|
new UplinkFetcherError(
|
|
354
|
-
|
|
355
|
-
)
|
|
348
|
+
'An error occurred while fetching your schema from Apollo: 503 Service Unavailable',
|
|
349
|
+
),
|
|
356
350
|
);
|
|
357
351
|
});
|
|
358
352
|
|
|
359
353
|
it('successfully responds to SDL unchanged by returning null', async () => {
|
|
360
|
-
mockSupergraphSdlRequestIfAfterUnchanged(
|
|
354
|
+
mockSupergraphSdlRequestIfAfterUnchanged('id-1234');
|
|
361
355
|
|
|
362
|
-
const fetcher = getDefaultFetcher();
|
|
363
356
|
const result = await loadSupergraphSdlFromStorage({
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
357
|
+
graphRef,
|
|
358
|
+
apiKey,
|
|
359
|
+
endpoint: mockCloudConfigUrl1,
|
|
360
|
+
errorReportingEndpoint: mockOutOfBandReporterUrl,
|
|
361
|
+
fetcher,
|
|
362
|
+
compositionId: 'id-1234',
|
|
370
363
|
});
|
|
371
364
|
expect(result).toBeNull();
|
|
372
365
|
});
|
|
373
366
|
});
|
|
374
367
|
|
|
375
|
-
|
|
376
|
-
describe("loadSupergraphSdlFromUplinks", () => {
|
|
368
|
+
describe('loadSupergraphSdlFromUplinks', () => {
|
|
377
369
|
beforeEach(nockBeforeEach);
|
|
378
370
|
afterEach(nockAfterEach);
|
|
379
371
|
|
|
380
372
|
it("doesn't retry in the unchanged / null case", async () => {
|
|
381
|
-
mockSupergraphSdlRequestIfAfterUnchanged(
|
|
373
|
+
mockSupergraphSdlRequestIfAfterUnchanged('id-1234', mockCloudConfigUrl1);
|
|
382
374
|
|
|
383
|
-
|
|
375
|
+
let calls = 0;
|
|
384
376
|
const result = await loadSupergraphSdlFromUplinks({
|
|
385
377
|
graphRef,
|
|
386
378
|
apiKey,
|
|
387
379
|
endpoints: [mockCloudConfigUrl1, mockCloudConfigUrl2],
|
|
388
380
|
errorReportingEndpoint: mockOutOfBandReporterUrl,
|
|
389
|
-
fetcher:
|
|
390
|
-
|
|
381
|
+
fetcher: (...args) => {
|
|
382
|
+
calls++;
|
|
383
|
+
return fetcher(...args);
|
|
384
|
+
},
|
|
385
|
+
compositionId: 'id-1234',
|
|
391
386
|
maxRetries: 5,
|
|
392
387
|
roundRobinSeed: 0,
|
|
393
388
|
earliestFetchTime: null,
|
|
394
389
|
});
|
|
395
390
|
|
|
396
391
|
expect(result).toBeNull();
|
|
397
|
-
expect(
|
|
392
|
+
expect(calls).toBe(1);
|
|
398
393
|
});
|
|
399
394
|
|
|
400
|
-
it(
|
|
395
|
+
it('Waits the correct time before retrying', async () => {
|
|
401
396
|
const timeoutSpy = jest.spyOn(global, 'setTimeout');
|
|
402
397
|
|
|
403
398
|
mockSupergraphSdlRequest('originalId-1234', mockCloudConfigUrl1).reply(500);
|
|
404
|
-
mockSupergraphSdlRequestIfAfter(
|
|
399
|
+
mockSupergraphSdlRequestIfAfter(
|
|
400
|
+
'originalId-1234',
|
|
401
|
+
mockCloudConfigUrl2,
|
|
402
|
+
).reply(
|
|
405
403
|
200,
|
|
406
404
|
JSON.stringify({
|
|
407
405
|
data: {
|
|
408
406
|
routerConfig: {
|
|
409
407
|
__typename: 'RouterConfigResult',
|
|
410
408
|
id: 'originalId-1234',
|
|
411
|
-
supergraphSdl: getTestingSupergraphSdl()
|
|
409
|
+
supergraphSdl: getTestingSupergraphSdl(),
|
|
412
410
|
},
|
|
413
411
|
},
|
|
414
412
|
}),
|
|
415
413
|
);
|
|
416
|
-
const fetcher = getDefaultFetcher();
|
|
417
414
|
|
|
418
415
|
await loadSupergraphSdlFromUplinks({
|
|
419
416
|
graphRef,
|
|
@@ -421,7 +418,7 @@ describe("loadSupergraphSdlFromUplinks", () => {
|
|
|
421
418
|
endpoints: [mockCloudConfigUrl1, mockCloudConfigUrl2],
|
|
422
419
|
errorReportingEndpoint: undefined,
|
|
423
420
|
fetcher: fetcher,
|
|
424
|
-
compositionId:
|
|
421
|
+
compositionId: 'originalId-1234',
|
|
425
422
|
maxRetries: 1,
|
|
426
423
|
roundRobinSeed: 0,
|
|
427
424
|
earliestFetchTime: new Date(Date.now() + 1000),
|
|
@@ -435,4 +432,3 @@ describe("loadSupergraphSdlFromUplinks", () => {
|
|
|
435
432
|
timeoutSpy.mockRestore();
|
|
436
433
|
});
|
|
437
434
|
});
|
|
438
|
-
|