@apollo/gateway 2.0.0-alpha.2 → 2.0.0-alpha.3
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 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.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 +1 -1
- package/dist/executeQueryPlan.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -6
- package/dist/index.js.map +1 -1
- package/dist/loadSupergraphSdlFromStorage.d.ts +13 -5
- package/dist/loadSupergraphSdlFromStorage.d.ts.map +1 -1
- package/dist/loadSupergraphSdlFromStorage.js +34 -7
- package/dist/loadSupergraphSdlFromStorage.js.map +1 -1
- package/dist/outOfBandReporter.d.ts +10 -12
- package/dist/outOfBandReporter.d.ts.map +1 -1
- package/dist/outOfBandReporter.js +70 -73
- package/dist/outOfBandReporter.js.map +1 -1
- package/package.json +4 -4
- package/src/__mocks__/make-fetch-happen-fetcher.ts +3 -1
- package/src/__tests__/executeQueryPlan.test.ts +598 -0
- package/src/__tests__/gateway/buildService.test.ts +1 -1
- package/src/__tests__/gateway/composedSdl.test.ts +1 -1
- package/src/__tests__/gateway/executor.test.ts +1 -1
- package/src/__tests__/gateway/reporting.test.ts +8 -5
- package/src/__tests__/integration/configuration.test.ts +44 -4
- package/src/__tests__/integration/networkRequests.test.ts +21 -19
- package/src/__tests__/integration/nockMocks.ts +12 -6
- package/src/__tests__/loadSupergraphSdlFromStorage.test.ts +101 -452
- package/src/__tests__/nockAssertions.ts +20 -0
- package/src/config.ts +3 -1
- package/src/datasources/RemoteGraphQLDataSource.ts +8 -2
- package/src/datasources/__tests__/RemoteGraphQLDataSource.test.ts +4 -4
- package/src/executeQueryPlan.ts +11 -1
- package/src/index.ts +26 -12
- package/src/loadSupergraphSdlFromStorage.ts +54 -8
- package/src/outOfBandReporter.ts +87 -89
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import nock from 'nock';
|
|
2
|
+
|
|
3
|
+
// Ensures an active and clean nock before every test
|
|
4
|
+
export function nockBeforeEach() {
|
|
5
|
+
if (!nock.isActive()) {
|
|
6
|
+
nock.activate();
|
|
7
|
+
}
|
|
8
|
+
// Cleaning _before_ each test ensures that any mocks from a previous test
|
|
9
|
+
// which failed don't affect the current test.
|
|
10
|
+
nock.cleanAll();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Ensures a test is complete (all expected requests were run) and a clean
|
|
14
|
+
// global state after each test.
|
|
15
|
+
export function nockAfterEach() {
|
|
16
|
+
// unmock HTTP interceptor
|
|
17
|
+
nock.restore();
|
|
18
|
+
// effectively nock.isDone() but with more helpful messages in test failures
|
|
19
|
+
expect(nock.activeMocks()).toEqual([]);
|
|
20
|
+
};
|
package/src/config.ts
CHANGED
|
@@ -140,7 +140,9 @@ export interface ManagedGatewayConfig extends GatewayConfigBase {
|
|
|
140
140
|
* This configuration option shouldn't be used unless by recommendation from
|
|
141
141
|
* Apollo staff.
|
|
142
142
|
*/
|
|
143
|
-
schemaConfigDeliveryEndpoint?: string;
|
|
143
|
+
schemaConfigDeliveryEndpoint?: string; // deprecated
|
|
144
|
+
uplinkEndpoints?: string[];
|
|
145
|
+
uplinkMaxRetries?: number;
|
|
144
146
|
}
|
|
145
147
|
|
|
146
148
|
interface ManuallyManagedServiceDefsGatewayConfig extends GatewayConfigBase {
|
|
@@ -17,18 +17,24 @@ import { isObject } from '../utilities/predicates';
|
|
|
17
17
|
import { GraphQLDataSource, GraphQLDataSourceProcessOptions, GraphQLDataSourceRequestKind } from './types';
|
|
18
18
|
import createSHA from 'apollo-server-core/dist/utils/createSHA';
|
|
19
19
|
import { parseCacheControlHeader } from './parseCacheControlHeader';
|
|
20
|
-
|
|
20
|
+
import fetcher from 'make-fetch-happen';
|
|
21
21
|
export class RemoteGraphQLDataSource<
|
|
22
22
|
TContext extends Record<string, any> = Record<string, any>,
|
|
23
23
|
> implements GraphQLDataSource<TContext>
|
|
24
24
|
{
|
|
25
|
-
fetcher: typeof fetch
|
|
25
|
+
fetcher: typeof fetch;
|
|
26
26
|
|
|
27
27
|
constructor(
|
|
28
28
|
config?: Partial<RemoteGraphQLDataSource<TContext>> &
|
|
29
29
|
object &
|
|
30
30
|
ThisType<RemoteGraphQLDataSource<TContext>>,
|
|
31
31
|
) {
|
|
32
|
+
this.fetcher = fetcher.defaults({
|
|
33
|
+
// although this is the default, we want to take extra care and be very
|
|
34
|
+
// explicity to ensure that mutations cannot be retried. please leave this
|
|
35
|
+
// intact.
|
|
36
|
+
retry: false,
|
|
37
|
+
});
|
|
32
38
|
if (config) {
|
|
33
39
|
return Object.assign(this, config);
|
|
34
40
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { fetch } from '../../__mocks__/apollo-server-env';
|
|
2
|
-
import {
|
|
1
|
+
import { fetch as customFetcher } from '../../__mocks__/apollo-server-env';
|
|
2
|
+
import { fetch } from '../../__mocks__/make-fetch-happen-fetcher';
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
ApolloError,
|
|
@@ -263,8 +263,8 @@ describe('fetcher', () => {
|
|
|
263
263
|
expect(data).toEqual({ injected: true });
|
|
264
264
|
});
|
|
265
265
|
|
|
266
|
-
it('supports a custom fetcher, like `
|
|
267
|
-
const injectedFetch =
|
|
266
|
+
it('supports a custom fetcher, like `node-fetch`', async () => {
|
|
267
|
+
const injectedFetch = customFetcher.mockJSONResponseOnce({
|
|
268
268
|
data: { me: 'james' },
|
|
269
269
|
});
|
|
270
270
|
const DataSource = new RemoteGraphQLDataSource({
|
package/src/executeQueryPlan.ts
CHANGED
|
@@ -489,7 +489,17 @@ function executeSelectionSet(
|
|
|
489
489
|
const selections = (selection as QueryPlanFieldNode).selections;
|
|
490
490
|
|
|
491
491
|
if (typeof source[responseName] === 'undefined') {
|
|
492
|
-
|
|
492
|
+
// This method is called to collect the inputs/requires of a fetch. So, assuming query plans are correct
|
|
493
|
+
// (and we have not reason to assume otherwise here), all inputs should be fetched beforehand and the
|
|
494
|
+
// only reason for not finding one of the inputs is that we had an error fetching it _and_ that field
|
|
495
|
+
// is non-nullable (it it was nullable, error fetching the input would have make that input `null`; not
|
|
496
|
+
// having the input means the field is non-nullable so the whole entity had to be nullified/ignored,
|
|
497
|
+
// leading use to not have the field at all).
|
|
498
|
+
// In any case, we don't have all the necessary inputs for this particular entity and should ignore it.
|
|
499
|
+
// Note that an error has already been logged for whichever issue happen while fetching the inputs we're
|
|
500
|
+
// missing here, and that error had much more context, so no reason to log a duplicate (less useful) error
|
|
501
|
+
// here.
|
|
502
|
+
return null;
|
|
493
503
|
}
|
|
494
504
|
if (Array.isArray(source[responseName])) {
|
|
495
505
|
result[responseName] = source[responseName].map((value: any) =>
|
package/src/index.ts
CHANGED
|
@@ -57,7 +57,7 @@ import {
|
|
|
57
57
|
SupergraphSdlUpdate,
|
|
58
58
|
CompositionUpdate,
|
|
59
59
|
} from './config';
|
|
60
|
-
import {
|
|
60
|
+
import {loadSupergraphSdlFromUplinks} from './loadSupergraphSdlFromStorage';
|
|
61
61
|
import { SpanStatusCode } from '@opentelemetry/api';
|
|
62
62
|
import { OpenTelemetrySpanNames, tracer } from './utilities/opentelemetry';
|
|
63
63
|
|
|
@@ -184,6 +184,8 @@ export class ApolloGateway implements GraphQLService {
|
|
|
184
184
|
private fetcher: typeof fetch;
|
|
185
185
|
private compositionId?: string;
|
|
186
186
|
private state: GatewayState;
|
|
187
|
+
private errorReportingEndpoint: string | undefined =
|
|
188
|
+
process.env.APOLLO_OUT_OF_BAND_REPORTER_ENDPOINT ?? undefined;
|
|
187
189
|
|
|
188
190
|
// Observe query plan, service info, and operation info prior to execution.
|
|
189
191
|
// The information made available here will give insight into the resulting
|
|
@@ -201,10 +203,11 @@ export class ApolloGateway implements GraphQLService {
|
|
|
201
203
|
private updateServiceDefinitions: Experimental_UpdateComposition;
|
|
202
204
|
// how often service defs should be loaded/updated (in ms)
|
|
203
205
|
private experimental_pollInterval?: number;
|
|
204
|
-
// Configure the
|
|
205
|
-
// *
|
|
206
|
+
// Configure the endpoints by which gateway will access its precomposed schema.
|
|
207
|
+
// * An array of URLs means use these endpoints to obtain schema, if one is unavailable then try the next.
|
|
206
208
|
// * `undefined` means the gateway is not using managed federation
|
|
207
|
-
private
|
|
209
|
+
private uplinkEndpoints?: string[];
|
|
210
|
+
private uplinkMaxRetries?: number;
|
|
208
211
|
|
|
209
212
|
constructor(config?: GatewayConfig) {
|
|
210
213
|
this.config = {
|
|
@@ -233,13 +236,22 @@ export class ApolloGateway implements GraphQLService {
|
|
|
233
236
|
|
|
234
237
|
// 1. If config is set to a `string`, use it
|
|
235
238
|
// 2. If the env var is set, use that
|
|
236
|
-
// 3. If config is `undefined`, use the default uplink
|
|
239
|
+
// 3. If config is `undefined`, use the default uplink URLs
|
|
237
240
|
if (isManagedConfig(this.config)) {
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
241
|
+
const rawEndpointsString = process.env.APOLLO_SCHEMA_CONFIG_DELIVERY_ENDPOINT;
|
|
242
|
+
const envEndpoints = rawEndpointsString?.split(",") ?? null;
|
|
243
|
+
|
|
244
|
+
if (this.config.schemaConfigDeliveryEndpoint && !this.config.uplinkEndpoints) {
|
|
245
|
+
this.uplinkEndpoints = [this.config.schemaConfigDeliveryEndpoint];
|
|
246
|
+
} else {
|
|
247
|
+
this.uplinkEndpoints = this.config.uplinkEndpoints ??
|
|
248
|
+
envEndpoints ?? [
|
|
249
|
+
'https://uplink.api.apollographql.com/',
|
|
250
|
+
'https://aws.uplink.api.apollographql.com/'
|
|
251
|
+
];
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
this.uplinkMaxRetries = this.config.uplinkMaxRetries ?? this.uplinkEndpoints.length * 3;
|
|
243
255
|
}
|
|
244
256
|
|
|
245
257
|
if (isManuallyManagedConfig(this.config)) {
|
|
@@ -916,12 +928,14 @@ export class ApolloGateway implements GraphQLService {
|
|
|
916
928
|
);
|
|
917
929
|
}
|
|
918
930
|
|
|
919
|
-
const result = await
|
|
931
|
+
const result = await loadSupergraphSdlFromUplinks({
|
|
920
932
|
graphRef: this.apolloConfig!.graphRef!,
|
|
921
933
|
apiKey: this.apolloConfig!.key!,
|
|
922
|
-
|
|
934
|
+
endpoints: this.uplinkEndpoints!,
|
|
935
|
+
errorReportingEndpoint: this.errorReportingEndpoint,
|
|
923
936
|
fetcher: this.fetcher,
|
|
924
937
|
compositionId: this.compositionId ?? null,
|
|
938
|
+
maxRetries: this.uplinkMaxRetries!,
|
|
925
939
|
});
|
|
926
940
|
|
|
927
941
|
return (
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { fetch, Response, Request } from 'apollo-server-env';
|
|
2
2
|
import { GraphQLError } from 'graphql';
|
|
3
|
-
import {
|
|
3
|
+
import { SupergraphSdlUpdate } from './config';
|
|
4
|
+
import { submitOutOfBandReportIfConfigured } from './outOfBandReporter';
|
|
4
5
|
import { SupergraphSdlQuery } from './__generated__/graphqlTypes';
|
|
5
6
|
|
|
6
7
|
// Magic /* GraphQL */ comment below is for codegen, do not remove
|
|
@@ -38,19 +39,63 @@ const { name, version } = require('../package.json');
|
|
|
38
39
|
|
|
39
40
|
const fetchErrorMsg = "An error occurred while fetching your schema from Apollo: ";
|
|
40
41
|
|
|
42
|
+
let fetchCounter = 0;
|
|
43
|
+
|
|
44
|
+
export async function loadSupergraphSdlFromUplinks({
|
|
45
|
+
graphRef,
|
|
46
|
+
apiKey,
|
|
47
|
+
endpoints,
|
|
48
|
+
errorReportingEndpoint,
|
|
49
|
+
fetcher,
|
|
50
|
+
compositionId,
|
|
51
|
+
maxRetries,
|
|
52
|
+
}: {
|
|
53
|
+
graphRef: string;
|
|
54
|
+
apiKey: string;
|
|
55
|
+
endpoints: string[];
|
|
56
|
+
errorReportingEndpoint: string | undefined,
|
|
57
|
+
fetcher: typeof fetch;
|
|
58
|
+
compositionId: string | null;
|
|
59
|
+
maxRetries: number
|
|
60
|
+
}) : Promise<SupergraphSdlUpdate | null> {
|
|
61
|
+
let retries = 0;
|
|
62
|
+
let lastException = null;
|
|
63
|
+
let result: SupergraphSdlUpdate | null = null;
|
|
64
|
+
while (retries++ <= maxRetries && result == null) {
|
|
65
|
+
try {
|
|
66
|
+
result = await loadSupergraphSdlFromStorage({
|
|
67
|
+
graphRef,
|
|
68
|
+
apiKey,
|
|
69
|
+
endpoint: endpoints[fetchCounter++ % endpoints.length],
|
|
70
|
+
errorReportingEndpoint,
|
|
71
|
+
fetcher,
|
|
72
|
+
compositionId
|
|
73
|
+
});
|
|
74
|
+
} catch (e) {
|
|
75
|
+
lastException = e;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (result === null && lastException !== null) {
|
|
79
|
+
throw lastException;
|
|
80
|
+
}
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
|
|
41
84
|
export async function loadSupergraphSdlFromStorage({
|
|
42
85
|
graphRef,
|
|
43
86
|
apiKey,
|
|
44
87
|
endpoint,
|
|
88
|
+
errorReportingEndpoint,
|
|
45
89
|
fetcher,
|
|
46
90
|
compositionId,
|
|
47
91
|
}: {
|
|
48
92
|
graphRef: string;
|
|
49
93
|
apiKey: string;
|
|
50
94
|
endpoint: string;
|
|
95
|
+
errorReportingEndpoint?: string;
|
|
51
96
|
fetcher: typeof fetch;
|
|
52
97
|
compositionId: string | null;
|
|
53
|
-
}) {
|
|
98
|
+
}) : Promise<SupergraphSdlUpdate | null> {
|
|
54
99
|
let result: Response;
|
|
55
100
|
const requestDetails = {
|
|
56
101
|
method: 'POST',
|
|
@@ -72,19 +117,19 @@ export async function loadSupergraphSdlFromStorage({
|
|
|
72
117
|
|
|
73
118
|
const request: Request = new Request(endpoint, requestDetails);
|
|
74
119
|
|
|
75
|
-
const
|
|
76
|
-
const startTime = new Date()
|
|
120
|
+
const startTime = new Date();
|
|
77
121
|
try {
|
|
78
122
|
result = await fetcher(endpoint, requestDetails);
|
|
79
123
|
} catch (e) {
|
|
80
124
|
const endTime = new Date();
|
|
81
125
|
|
|
82
|
-
await
|
|
126
|
+
await submitOutOfBandReportIfConfigured({
|
|
83
127
|
error: e,
|
|
84
128
|
request,
|
|
129
|
+
endpoint: errorReportingEndpoint,
|
|
85
130
|
startedAt: startTime,
|
|
86
131
|
endedAt: endTime,
|
|
87
|
-
fetcher
|
|
132
|
+
fetcher,
|
|
88
133
|
});
|
|
89
134
|
|
|
90
135
|
throw new Error(fetchErrorMsg + (e.message ?? e));
|
|
@@ -109,13 +154,14 @@ export async function loadSupergraphSdlFromStorage({
|
|
|
109
154
|
);
|
|
110
155
|
}
|
|
111
156
|
} else {
|
|
112
|
-
await
|
|
157
|
+
await submitOutOfBandReportIfConfigured({
|
|
113
158
|
error: new Error(fetchErrorMsg + result.status + ' ' + result.statusText),
|
|
114
159
|
request,
|
|
160
|
+
endpoint: errorReportingEndpoint,
|
|
115
161
|
response: result,
|
|
116
162
|
startedAt: startTime,
|
|
117
163
|
endedAt: endTime,
|
|
118
|
-
fetcher
|
|
164
|
+
fetcher,
|
|
119
165
|
});
|
|
120
166
|
throw new Error(fetchErrorMsg + result.status + ' ' + result.statusText);
|
|
121
167
|
}
|
package/src/outOfBandReporter.ts
CHANGED
|
@@ -27,102 +27,100 @@ interface OobReportMutationFailure {
|
|
|
27
27
|
data?: OobReportMutation;
|
|
28
28
|
errors: GraphQLError[];
|
|
29
29
|
}
|
|
30
|
-
export class OutOfBandReporter {
|
|
31
|
-
static endpoint: string | null = process.env.APOLLO_OUT_OF_BAND_REPORTER_ENDPOINT || null;
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
31
|
+
export async function submitOutOfBandReportIfConfigured({
|
|
32
|
+
error,
|
|
33
|
+
request,
|
|
34
|
+
endpoint,
|
|
35
|
+
response,
|
|
36
|
+
startedAt,
|
|
37
|
+
endedAt,
|
|
38
|
+
tags,
|
|
39
|
+
fetcher,
|
|
40
|
+
}: {
|
|
41
|
+
error: Error;
|
|
42
|
+
request: Request;
|
|
43
|
+
endpoint: string | undefined;
|
|
44
|
+
response?: Response;
|
|
45
|
+
startedAt: Date;
|
|
46
|
+
endedAt: Date;
|
|
47
|
+
tags?: string[];
|
|
48
|
+
fetcher: typeof fetch;
|
|
49
|
+
}) {
|
|
50
|
+
// don't send report if the endpoint url is not configured
|
|
51
|
+
if (!endpoint) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
55
54
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
55
|
+
let errorCode: ErrorCode;
|
|
56
|
+
if (!response) {
|
|
57
|
+
errorCode = ErrorCode.ConnectionFailed;
|
|
58
|
+
} else {
|
|
59
|
+
// possible error situations to check against
|
|
60
|
+
switch (response.status) {
|
|
61
|
+
case 400:
|
|
62
|
+
case 413:
|
|
63
|
+
case 422:
|
|
64
|
+
errorCode = ErrorCode.InvalidBody;
|
|
65
|
+
break;
|
|
66
|
+
case 408:
|
|
67
|
+
case 504:
|
|
68
|
+
errorCode = ErrorCode.Timeout;
|
|
69
|
+
break;
|
|
70
|
+
case 502:
|
|
71
|
+
case 503:
|
|
72
|
+
errorCode = ErrorCode.ConnectionFailed;
|
|
73
|
+
break;
|
|
74
|
+
default:
|
|
75
|
+
errorCode = ErrorCode.Other;
|
|
78
76
|
}
|
|
77
|
+
}
|
|
79
78
|
|
|
80
|
-
|
|
79
|
+
const responseBody: string | undefined = await response?.text();
|
|
81
80
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
},
|
|
92
|
-
response: response
|
|
93
|
-
? {
|
|
94
|
-
httpStatusCode: response.status,
|
|
95
|
-
body: responseBody,
|
|
96
|
-
}
|
|
97
|
-
: null,
|
|
98
|
-
startedAt: startedAt.toISOString(),
|
|
99
|
-
endedAt: endedAt.toISOString(),
|
|
100
|
-
tags: tags,
|
|
81
|
+
const variables: OobReportMutationVariables = {
|
|
82
|
+
input: {
|
|
83
|
+
error: {
|
|
84
|
+
code: errorCode,
|
|
85
|
+
message: error.message,
|
|
86
|
+
},
|
|
87
|
+
request: {
|
|
88
|
+
url: request.url,
|
|
89
|
+
body: await request.text(),
|
|
101
90
|
},
|
|
102
|
-
|
|
91
|
+
response: response
|
|
92
|
+
? {
|
|
93
|
+
httpStatusCode: response.status,
|
|
94
|
+
body: responseBody,
|
|
95
|
+
}
|
|
96
|
+
: null,
|
|
97
|
+
startedAt: startedAt.toISOString(),
|
|
98
|
+
endedAt: endedAt.toISOString(),
|
|
99
|
+
tags: tags,
|
|
100
|
+
},
|
|
101
|
+
};
|
|
103
102
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
} catch (e) {
|
|
125
|
-
throw new Error(`Out-of-band error reporting failed: ${e.message ?? e}`);
|
|
103
|
+
try {
|
|
104
|
+
const oobResponse = await fetcher(endpoint, {
|
|
105
|
+
method: 'POST',
|
|
106
|
+
body: JSON.stringify({
|
|
107
|
+
query: OUT_OF_BAND_REPORTER_QUERY,
|
|
108
|
+
variables,
|
|
109
|
+
}),
|
|
110
|
+
headers: {
|
|
111
|
+
'apollographql-client-name': name,
|
|
112
|
+
'apollographql-client-version': version,
|
|
113
|
+
'user-agent': `${name}/${version}`,
|
|
114
|
+
'content-type': 'application/json',
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
const parsedResponse: OobReportMutationResult = await oobResponse.json();
|
|
118
|
+
if (!parsedResponse?.data?.reportError) {
|
|
119
|
+
throw new Error(
|
|
120
|
+
`Out-of-band error reporting failed: ${oobResponse.status} ${oobResponse.statusText}`,
|
|
121
|
+
);
|
|
126
122
|
}
|
|
123
|
+
} catch (e) {
|
|
124
|
+
throw new Error(`Out-of-band error reporting failed: ${e.message ?? e}`);
|
|
127
125
|
}
|
|
128
126
|
}
|