@apollo/gateway 2.1.0-alpha.0 → 2.1.0-alpha.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 +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/datasources/RemoteGraphQLDataSource.d.ts.map +1 -1
- package/dist/datasources/RemoteGraphQLDataSource.js +2 -3
- package/dist/datasources/RemoteGraphQLDataSource.js.map +1 -1
- package/dist/datasources/types.d.ts +1 -0
- package/dist/datasources/types.d.ts.map +1 -1
- package/dist/executeQueryPlan.d.ts +2 -2
- package/dist/executeQueryPlan.d.ts.map +1 -1
- package/dist/executeQueryPlan.js +24 -16
- package/dist/executeQueryPlan.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -18
- package/dist/index.js.map +1 -1
- package/dist/operationContext.d.ts.map +1 -1
- package/dist/operationContext.js +3 -7
- package/dist/operationContext.js.map +1 -1
- package/dist/supergraphManagers/IntrospectAndCompose/index.d.ts +1 -1
- package/dist/supergraphManagers/IntrospectAndCompose/index.d.ts.map +1 -1
- package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.d.ts +1 -1
- package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.d.ts.map +1 -1
- package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.js.map +1 -1
- package/dist/supergraphManagers/UplinkSupergraphManager/index.d.ts +7 -7
- package/dist/supergraphManagers/UplinkSupergraphManager/index.d.ts.map +1 -1
- package/dist/supergraphManagers/UplinkSupergraphManager/index.js +38 -42
- package/dist/supergraphManagers/UplinkSupergraphManager/index.js.map +1 -1
- package/dist/supergraphManagers/UplinkSupergraphManager/loadSupergraphSdlFromStorage.d.ts +9 -8
- package/dist/supergraphManagers/UplinkSupergraphManager/loadSupergraphSdlFromStorage.d.ts.map +1 -1
- package/dist/supergraphManagers/UplinkSupergraphManager/loadSupergraphSdlFromStorage.js +19 -10
- package/dist/supergraphManagers/UplinkSupergraphManager/loadSupergraphSdlFromStorage.js.map +1 -1
- package/dist/supergraphManagers/UplinkSupergraphManager/outOfBandReporter.d.ts +2 -1
- package/dist/supergraphManagers/UplinkSupergraphManager/outOfBandReporter.d.ts.map +1 -1
- package/dist/supergraphManagers/UplinkSupergraphManager/outOfBandReporter.js.map +1 -1
- package/dist/supergraphManagers/UplinkSupergraphManager/types.d.ts +9 -0
- package/dist/supergraphManagers/UplinkSupergraphManager/types.d.ts.map +1 -0
- package/dist/supergraphManagers/UplinkSupergraphManager/types.js +5 -0
- package/dist/supergraphManagers/UplinkSupergraphManager/types.js.map +1 -0
- package/package.json +7 -7
- package/src/__tests__/executeQueryPlan.test.ts +199 -1
- package/src/__tests__/execution-utils.ts +5 -3
- package/src/__tests__/integration/abstract-types.test.ts +31 -65
- package/src/__tests__/integration/configuration.test.ts +2 -2
- package/src/__tests__/integration/managed.test.ts +47 -44
- package/src/__tests__/integration/networkRequests.test.ts +10 -23
- package/src/config.ts +1 -1
- package/src/datasources/RemoteGraphQLDataSource.ts +1 -2
- package/src/datasources/types.ts +4 -0
- package/src/executeQueryPlan.ts +41 -30
- package/src/index.ts +8 -22
- package/src/operationContext.ts +5 -7
- package/src/supergraphManagers/IntrospectAndCompose/index.ts +1 -1
- package/src/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.ts +1 -1
- package/src/supergraphManagers/UplinkSupergraphManager/__tests__/UplinkSupergraphManager.test.ts +1 -3
- package/src/supergraphManagers/UplinkSupergraphManager/__tests__/loadSupergraphSdlFromStorage.test.ts +51 -16
- package/src/supergraphManagers/UplinkSupergraphManager/index.ts +64 -58
- package/src/supergraphManagers/UplinkSupergraphManager/loadSupergraphSdlFromStorage.ts +31 -19
- package/src/supergraphManagers/UplinkSupergraphManager/outOfBandReporter.ts +2 -1
- package/src/supergraphManagers/UplinkSupergraphManager/types.ts +10 -0
|
@@ -118,8 +118,7 @@ export class RemoteGraphQLDataSource<
|
|
|
118
118
|
// there.
|
|
119
119
|
const overallCachePolicy =
|
|
120
120
|
this.honorSubgraphCacheControlHeader &&
|
|
121
|
-
options.kind === GraphQLDataSourceRequestKind.INCOMING_OPERATION
|
|
122
|
-
options.incomingRequestContext.overallCachePolicy?.restrict
|
|
121
|
+
options.kind === GraphQLDataSourceRequestKind.INCOMING_OPERATION
|
|
123
122
|
? options.incomingRequestContext.overallCachePolicy
|
|
124
123
|
: null;
|
|
125
124
|
|
package/src/datasources/types.ts
CHANGED
|
@@ -39,6 +39,10 @@ export type GraphQLDataSourceProcessOptions<
|
|
|
39
39
|
* checking `kind`).
|
|
40
40
|
*/
|
|
41
41
|
context: GraphQLRequestContext<TContext>['context'];
|
|
42
|
+
/**
|
|
43
|
+
* The document representation of the request's query being sent to the subgraph, if available.
|
|
44
|
+
*/
|
|
45
|
+
document?: GraphQLRequestContext<TContext>['document'];
|
|
42
46
|
}
|
|
43
47
|
| {
|
|
44
48
|
kind:
|
package/src/executeQueryPlan.ts
CHANGED
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
GraphQLSchema,
|
|
15
15
|
isObjectType,
|
|
16
16
|
isInterfaceType,
|
|
17
|
+
GraphQLErrorOptions,
|
|
18
|
+
DocumentNode,
|
|
17
19
|
} from 'graphql';
|
|
18
20
|
import { Trace, google } from 'apollo-reporting-protobuf';
|
|
19
21
|
import { GraphQLDataSource, GraphQLDataSourceRequestKind } from './datasources/types';
|
|
@@ -31,7 +33,7 @@ import { deepMerge } from './utilities/deepMerge';
|
|
|
31
33
|
import { isNotNullOrUndefined } from './utilities/array';
|
|
32
34
|
import { SpanStatusCode } from "@opentelemetry/api";
|
|
33
35
|
import { OpenTelemetrySpanNames, tracer } from "./utilities/opentelemetry";
|
|
34
|
-
import { defaultRootName } from '@apollo/federation-internals';
|
|
36
|
+
import { defaultRootName, errorCodeDef, ERRORS } from '@apollo/federation-internals';
|
|
35
37
|
|
|
36
38
|
export type ServiceMap = {
|
|
37
39
|
[serviceName: string]: GraphQLDataSource;
|
|
@@ -44,6 +46,7 @@ interface ExecutionContext<TContext> {
|
|
|
44
46
|
operationContext: OperationContext;
|
|
45
47
|
serviceMap: ServiceMap;
|
|
46
48
|
requestContext: GraphQLRequestContext<TContext>;
|
|
49
|
+
supergraphSchema: GraphQLSchema;
|
|
47
50
|
errors: GraphQLError[];
|
|
48
51
|
}
|
|
49
52
|
|
|
@@ -52,6 +55,7 @@ export async function executeQueryPlan<TContext>(
|
|
|
52
55
|
serviceMap: ServiceMap,
|
|
53
56
|
requestContext: GraphQLRequestContext<TContext>,
|
|
54
57
|
operationContext: OperationContext,
|
|
58
|
+
supergraphSchema: GraphQLSchema,
|
|
55
59
|
): Promise<GraphQLExecutionResult> {
|
|
56
60
|
|
|
57
61
|
const logger = requestContext.logger || console;
|
|
@@ -65,6 +69,7 @@ export async function executeQueryPlan<TContext>(
|
|
|
65
69
|
operationContext,
|
|
66
70
|
serviceMap,
|
|
67
71
|
requestContext,
|
|
72
|
+
supergraphSchema,
|
|
68
73
|
errors,
|
|
69
74
|
};
|
|
70
75
|
|
|
@@ -118,11 +123,7 @@ export async function executeQueryPlan<TContext>(
|
|
|
118
123
|
errors: [
|
|
119
124
|
new GraphQLError(
|
|
120
125
|
error.message,
|
|
121
|
-
|
|
122
|
-
undefined,
|
|
123
|
-
undefined,
|
|
124
|
-
undefined,
|
|
125
|
-
error as Error,
|
|
126
|
+
{ originalError: error },
|
|
126
127
|
)
|
|
127
128
|
]
|
|
128
129
|
};
|
|
@@ -298,7 +299,8 @@ async function executeFetch<TContext>(
|
|
|
298
299
|
context,
|
|
299
300
|
fetch.operation,
|
|
300
301
|
variables,
|
|
301
|
-
fetch.operationName
|
|
302
|
+
fetch.operationName,
|
|
303
|
+
fetch.operationDocumentNode
|
|
302
304
|
);
|
|
303
305
|
|
|
304
306
|
for (const entity of entities) {
|
|
@@ -312,7 +314,10 @@ async function executeFetch<TContext>(
|
|
|
312
314
|
|
|
313
315
|
entities.forEach((entity, index) => {
|
|
314
316
|
const representation = executeSelectionSet(
|
|
315
|
-
|
|
317
|
+
// Note that `requires` may include references to inacessible elements, so we should "execute" it using the supergrah
|
|
318
|
+
// schema, _not_ the API schema (the one in `context.operationContext.schema`). And this is not a security risk since
|
|
319
|
+
// what we're extracting here is what is sent to subgraphs, and subgraphs knows `@inacessible` elements.
|
|
320
|
+
context.supergraphSchema,
|
|
316
321
|
entity,
|
|
317
322
|
requires,
|
|
318
323
|
);
|
|
@@ -334,7 +339,8 @@ async function executeFetch<TContext>(
|
|
|
334
339
|
context,
|
|
335
340
|
fetch.operation,
|
|
336
341
|
{...variables, representations},
|
|
337
|
-
fetch.operationName
|
|
342
|
+
fetch.operationName,
|
|
343
|
+
fetch.operationDocumentNode
|
|
338
344
|
);
|
|
339
345
|
|
|
340
346
|
if (!dataReceivedFromService) {
|
|
@@ -376,7 +382,8 @@ async function executeFetch<TContext>(
|
|
|
376
382
|
context: ExecutionContext<TContext>,
|
|
377
383
|
source: string,
|
|
378
384
|
variables: Record<string, any>,
|
|
379
|
-
operationName: string | undefined
|
|
385
|
+
operationName: string | undefined,
|
|
386
|
+
operationDocumentNode?: DocumentNode
|
|
380
387
|
): Promise<ResultMap | void | null> {
|
|
381
388
|
// We declare this as 'any' because it is missing url and method, which
|
|
382
389
|
// GraphQLRequest.http is supposed to have if it exists.
|
|
@@ -414,6 +421,7 @@ async function executeFetch<TContext>(
|
|
|
414
421
|
},
|
|
415
422
|
incomingRequestContext: context.requestContext,
|
|
416
423
|
context: context.requestContext.context,
|
|
424
|
+
document: operationDocumentNode
|
|
417
425
|
});
|
|
418
426
|
|
|
419
427
|
if (response.errors) {
|
|
@@ -479,7 +487,7 @@ async function executeFetch<TContext>(
|
|
|
479
487
|
* @param selectionSet
|
|
480
488
|
*/
|
|
481
489
|
function executeSelectionSet(
|
|
482
|
-
|
|
490
|
+
schema: GraphQLSchema,
|
|
483
491
|
source: Record<string, any> | null,
|
|
484
492
|
selections: QueryPlanSelectionNode[],
|
|
485
493
|
): Record<string, any> | null {
|
|
@@ -514,12 +522,12 @@ function executeSelectionSet(
|
|
|
514
522
|
if (Array.isArray(source[responseName])) {
|
|
515
523
|
result[responseName] = source[responseName].map((value: any) =>
|
|
516
524
|
selections
|
|
517
|
-
? executeSelectionSet(
|
|
525
|
+
? executeSelectionSet(schema, value, selections)
|
|
518
526
|
: value,
|
|
519
527
|
);
|
|
520
528
|
} else if (selections) {
|
|
521
529
|
result[responseName] = executeSelectionSet(
|
|
522
|
-
|
|
530
|
+
schema,
|
|
523
531
|
source[responseName],
|
|
524
532
|
selections,
|
|
525
533
|
);
|
|
@@ -533,10 +541,10 @@ function executeSelectionSet(
|
|
|
533
541
|
const typename = source && source['__typename'];
|
|
534
542
|
if (!typename) continue;
|
|
535
543
|
|
|
536
|
-
if (doesTypeConditionMatch(
|
|
544
|
+
if (doesTypeConditionMatch(schema, selection.typeCondition, typename)) {
|
|
537
545
|
deepMerge(
|
|
538
546
|
result,
|
|
539
|
-
executeSelectionSet(
|
|
547
|
+
executeSelectionSet(schema, source, selection.selections),
|
|
540
548
|
);
|
|
541
549
|
}
|
|
542
550
|
break;
|
|
@@ -593,22 +601,25 @@ function downstreamServiceError(
|
|
|
593
601
|
if (!message) {
|
|
594
602
|
message = `Error while fetching subquery from service "${serviceName}"`;
|
|
595
603
|
}
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
604
|
+
|
|
605
|
+
const errorOptions: GraphQLErrorOptions = {
|
|
606
|
+
originalError: originalError as Error,
|
|
607
|
+
extensions: {
|
|
608
|
+
...extensions,
|
|
609
|
+
// XXX The presence of a serviceName in extensions is used to
|
|
610
|
+
// determine if this error should be captured for metrics reporting.
|
|
611
|
+
serviceName,
|
|
612
|
+
}
|
|
602
613
|
};
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
);
|
|
614
|
+
|
|
615
|
+
let codeDef = errorCodeDef(originalError);
|
|
616
|
+
// It's possible the orignal has a code, but not one we know about (one generated by the underlying `GraphQLDataSource`,
|
|
617
|
+
// which we don't control). In that case, we want to use that code (and have thus no `ErrorCodeDefinition` usable).
|
|
618
|
+
if (!codeDef && extensions?.code) {
|
|
619
|
+
return new GraphQLError(message, errorOptions);
|
|
620
|
+
}
|
|
621
|
+
// Otherwise, we either use the code we found and know, or default to a general downstream error code.
|
|
622
|
+
return (codeDef ?? ERRORS.DOWNSTREAM_SERVICE_ERROR).err(message, errorOptions);
|
|
612
623
|
}
|
|
613
624
|
|
|
614
625
|
export const defaultFieldResolverWithAliasSupport: GraphQLFieldResolver<
|
package/src/index.ts
CHANGED
|
@@ -142,6 +142,7 @@ export class ApolloGateway implements GraphQLService {
|
|
|
142
142
|
private warnedStates: WarnedStates = Object.create(null);
|
|
143
143
|
private queryPlanner?: QueryPlanner;
|
|
144
144
|
private supergraphSdl?: string;
|
|
145
|
+
private supergraphSchema?: GraphQLSchema;
|
|
145
146
|
private compositionId?: string;
|
|
146
147
|
private state: GatewayState;
|
|
147
148
|
private _supergraphManager?: SupergraphManager;
|
|
@@ -207,22 +208,6 @@ export class ApolloGateway implements GraphQLService {
|
|
|
207
208
|
}
|
|
208
209
|
|
|
209
210
|
private issueConfigurationWarningsIfApplicable() {
|
|
210
|
-
// Warn against a pollInterval of < 10s in managed mode and reset it to 10s
|
|
211
|
-
if (
|
|
212
|
-
isManagedConfig(this.config) &&
|
|
213
|
-
this.pollIntervalInMs &&
|
|
214
|
-
this.pollIntervalInMs < 10000
|
|
215
|
-
) {
|
|
216
|
-
this.pollIntervalInMs = 10000;
|
|
217
|
-
this.logger.warn(
|
|
218
|
-
'Polling Apollo services at a frequency of less than once per 10 ' +
|
|
219
|
-
'seconds (10000) is disallowed. Instead, the minimum allowed ' +
|
|
220
|
-
'pollInterval of 10000 will be used. Please reconfigure your ' +
|
|
221
|
-
'`fallbackPollIntervalInMs` accordingly. If this is problematic for ' +
|
|
222
|
-
'your team, please contact support.',
|
|
223
|
-
);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
211
|
// Warn against using the pollInterval and a serviceList simultaneously
|
|
227
212
|
// TODO(trevor:removeServiceList)
|
|
228
213
|
if (this.pollIntervalInMs && isServiceListConfig(this.config)) {
|
|
@@ -522,7 +507,7 @@ export class ApolloGateway implements GraphQLService {
|
|
|
522
507
|
// In the case that it throws, the gateway will:
|
|
523
508
|
// * on initial load, throw the error
|
|
524
509
|
// * on update, log the error and don't update
|
|
525
|
-
const {
|
|
510
|
+
const { supergraphSchema, supergraphSdl } = this.createSchemaFromSupergraphSdl(
|
|
526
511
|
result.supergraphSdl,
|
|
527
512
|
);
|
|
528
513
|
|
|
@@ -531,25 +516,26 @@ export class ApolloGateway implements GraphQLService {
|
|
|
531
516
|
const previousCompositionId = this.compositionId;
|
|
532
517
|
|
|
533
518
|
if (previousSchema) {
|
|
534
|
-
this.logger.info(
|
|
519
|
+
this.logger.info(`Updated Supergraph SDL was found [Composition ID ${this.compositionId} => ${result.id}]`);
|
|
535
520
|
}
|
|
536
521
|
|
|
537
522
|
this.compositionId = result.id;
|
|
538
523
|
this.supergraphSdl = supergraphSdl;
|
|
524
|
+
this.supergraphSchema = supergraphSchema.toGraphQLJSSchema();
|
|
539
525
|
|
|
540
526
|
if (!supergraphSdl) {
|
|
541
527
|
this.logger.error(
|
|
542
528
|
"A valid schema couldn't be composed. Falling back to previous schema.",
|
|
543
529
|
);
|
|
544
530
|
} else {
|
|
545
|
-
this.updateWithSchemaAndNotify(
|
|
531
|
+
this.updateWithSchemaAndNotify(supergraphSchema, supergraphSdl);
|
|
546
532
|
|
|
547
533
|
if (this.experimental_didUpdateSupergraph) {
|
|
548
534
|
this.experimental_didUpdateSupergraph(
|
|
549
535
|
{
|
|
550
536
|
compositionId: result.id,
|
|
551
537
|
supergraphSdl,
|
|
552
|
-
schema: schema
|
|
538
|
+
schema: this.schema!,
|
|
553
539
|
},
|
|
554
540
|
previousCompositionId && previousSupergraphSdl && previousSchema
|
|
555
541
|
? {
|
|
@@ -654,7 +640,7 @@ export class ApolloGateway implements GraphQLService {
|
|
|
654
640
|
this.createServices(serviceList);
|
|
655
641
|
|
|
656
642
|
return {
|
|
657
|
-
schema,
|
|
643
|
+
supergraphSchema: schema,
|
|
658
644
|
supergraphSdl,
|
|
659
645
|
};
|
|
660
646
|
}
|
|
@@ -859,6 +845,7 @@ export class ApolloGateway implements GraphQLService {
|
|
|
859
845
|
serviceMap,
|
|
860
846
|
requestContext,
|
|
861
847
|
operationContext,
|
|
848
|
+
this.supergraphSchema!,
|
|
862
849
|
);
|
|
863
850
|
|
|
864
851
|
const shouldShowQueryPlan =
|
|
@@ -1076,5 +1063,4 @@ export {
|
|
|
1076
1063
|
FailureToFetchSupergraphSdlAfterInit,
|
|
1077
1064
|
FailureToFetchSupergraphSdlDuringInit,
|
|
1078
1065
|
FailureToFetchSupergraphSdlFunctionParams,
|
|
1079
|
-
DEFAULT_UPLINK_ENDPOINTS,
|
|
1080
1066
|
} from './supergraphManagers';
|
package/src/operationContext.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { ERRORS } from '@apollo/federation-internals';
|
|
1
2
|
import {
|
|
2
3
|
DocumentNode,
|
|
3
4
|
FragmentDefinitionNode,
|
|
4
|
-
GraphQLError,
|
|
5
5
|
GraphQLSchema,
|
|
6
6
|
Kind,
|
|
7
7
|
OperationDefinitionNode,
|
|
@@ -37,7 +37,7 @@ export function buildOperationContext({
|
|
|
37
37
|
case Kind.OPERATION_DEFINITION:
|
|
38
38
|
operationCount++;
|
|
39
39
|
if (!operationName && operationCount > 1) {
|
|
40
|
-
throw
|
|
40
|
+
throw ERRORS.INVALID_GRAPHQL.err(
|
|
41
41
|
'Must provide operation name if query contains ' +
|
|
42
42
|
'multiple operations.',
|
|
43
43
|
);
|
|
@@ -55,11 +55,9 @@ export function buildOperationContext({
|
|
|
55
55
|
}
|
|
56
56
|
});
|
|
57
57
|
if (!operation) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
throw new GraphQLError('Must provide an operation.');
|
|
62
|
-
}
|
|
58
|
+
throw ERRORS.INVALID_GRAPHQL.err(
|
|
59
|
+
operationName ? `Unknown operation named "${operationName}".` : 'Must provide an operation.'
|
|
60
|
+
);
|
|
63
61
|
}
|
|
64
62
|
|
|
65
63
|
return {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { GraphQLRequest } from 'apollo-server-types';
|
|
2
2
|
import { parse } from 'graphql';
|
|
3
|
-
import { Headers, HeadersInit } from 'node-fetch';
|
|
3
|
+
import { Headers, type HeadersInit } from 'node-fetch';
|
|
4
4
|
import { ServiceDefinition } from '@apollo/federation-internals';
|
|
5
5
|
import { GraphQLDataSource, GraphQLDataSourceRequestKind } from '../../datasources/types';
|
|
6
6
|
import { SERVICE_DEFINITION_QUERY } from '../..';
|
package/src/supergraphManagers/UplinkSupergraphManager/__tests__/UplinkSupergraphManager.test.ts
CHANGED
|
@@ -2,8 +2,6 @@ import mockedEnv from 'mocked-env';
|
|
|
2
2
|
|
|
3
3
|
import { UplinkSupergraphManager } from '@apollo/gateway';
|
|
4
4
|
|
|
5
|
-
import { DEFAULT_UPLINK_ENDPOINTS } from '..';
|
|
6
|
-
|
|
7
5
|
let cleanUp: (() => void) | undefined;
|
|
8
6
|
|
|
9
7
|
const logger = {
|
|
@@ -31,7 +29,7 @@ describe('UplinkSupergraphManager', () => {
|
|
|
31
29
|
it('uses default uplink URLs', async () => {
|
|
32
30
|
const manager = new UplinkSupergraphManager({ apiKey, graphRef, logger });
|
|
33
31
|
|
|
34
|
-
expect(manager.uplinkEndpoints).toEqual(DEFAULT_UPLINK_ENDPOINTS);
|
|
32
|
+
expect(manager.uplinkEndpoints).toEqual(UplinkSupergraphManager.DEFAULT_UPLINK_ENDPOINTS);
|
|
35
33
|
});
|
|
36
34
|
|
|
37
35
|
it('can set uplink URLs via config', async () => {
|
|
@@ -22,6 +22,15 @@ import {
|
|
|
22
22
|
nockBeforeEach,
|
|
23
23
|
} from '../../../__tests__/nockAssertions';
|
|
24
24
|
|
|
25
|
+
const logger = {
|
|
26
|
+
warn: jest.fn(),
|
|
27
|
+
debug: jest.fn(),
|
|
28
|
+
error: jest.fn(),
|
|
29
|
+
info: jest.fn(),
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const requestTimeoutMs = 100;
|
|
33
|
+
|
|
25
34
|
describe('loadSupergraphSdlFromStorage', () => {
|
|
26
35
|
beforeEach(nockBeforeEach);
|
|
27
36
|
afterEach(nockAfterEach);
|
|
@@ -34,7 +43,9 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
34
43
|
endpoint: mockCloudConfigUrl1,
|
|
35
44
|
errorReportingEndpoint: undefined,
|
|
36
45
|
fetcher,
|
|
46
|
+
requestTimeoutMs,
|
|
37
47
|
compositionId: null,
|
|
48
|
+
logger,
|
|
38
49
|
});
|
|
39
50
|
expect(result).toMatchObject({
|
|
40
51
|
id: 'originalId-1234',
|
|
@@ -66,10 +77,11 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
66
77
|
endpoints: [mockCloudConfigUrl1, mockCloudConfigUrl2],
|
|
67
78
|
errorReportingEndpoint: undefined,
|
|
68
79
|
fetcher,
|
|
80
|
+
requestTimeoutMs,
|
|
69
81
|
compositionId: 'originalId-1234',
|
|
70
82
|
maxRetries: 1,
|
|
71
83
|
roundRobinSeed: 0,
|
|
72
|
-
|
|
84
|
+
logger,
|
|
73
85
|
});
|
|
74
86
|
|
|
75
87
|
expect(result).toMatchObject({
|
|
@@ -92,10 +104,11 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
92
104
|
endpoints: [mockCloudConfigUrl1, mockCloudConfigUrl2],
|
|
93
105
|
errorReportingEndpoint: undefined,
|
|
94
106
|
fetcher,
|
|
107
|
+
requestTimeoutMs,
|
|
95
108
|
compositionId: 'originalId-1234',
|
|
96
109
|
maxRetries: 1,
|
|
97
110
|
roundRobinSeed: 0,
|
|
98
|
-
|
|
111
|
+
logger,
|
|
99
112
|
}),
|
|
100
113
|
).rejects.toThrowError(
|
|
101
114
|
new UplinkFetcherError(
|
|
@@ -115,7 +128,9 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
115
128
|
endpoint: mockCloudConfigUrl1,
|
|
116
129
|
errorReportingEndpoint: mockOutOfBandReporterUrl,
|
|
117
130
|
fetcher,
|
|
131
|
+
requestTimeoutMs,
|
|
118
132
|
compositionId: null,
|
|
133
|
+
logger,
|
|
119
134
|
}),
|
|
120
135
|
).rejects.toThrowError(
|
|
121
136
|
new UplinkFetcherError(
|
|
@@ -140,7 +155,9 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
140
155
|
endpoint: mockCloudConfigUrl1,
|
|
141
156
|
errorReportingEndpoint: mockOutOfBandReporterUrl,
|
|
142
157
|
fetcher,
|
|
158
|
+
requestTimeoutMs,
|
|
143
159
|
compositionId: null,
|
|
160
|
+
logger,
|
|
144
161
|
}),
|
|
145
162
|
).rejects.toThrowError(
|
|
146
163
|
new UplinkFetcherError(
|
|
@@ -160,7 +177,9 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
160
177
|
endpoint: mockCloudConfigUrl1,
|
|
161
178
|
errorReportingEndpoint: mockOutOfBandReporterUrl,
|
|
162
179
|
fetcher,
|
|
180
|
+
requestTimeoutMs,
|
|
163
181
|
compositionId: null,
|
|
182
|
+
logger,
|
|
164
183
|
}),
|
|
165
184
|
).rejects.toThrowError(
|
|
166
185
|
new UplinkFetcherError(
|
|
@@ -181,7 +200,9 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
181
200
|
endpoint: mockCloudConfigUrl1,
|
|
182
201
|
errorReportingEndpoint: mockOutOfBandReporterUrl,
|
|
183
202
|
fetcher,
|
|
203
|
+
requestTimeoutMs,
|
|
184
204
|
compositionId: null,
|
|
205
|
+
logger,
|
|
185
206
|
}),
|
|
186
207
|
).rejects.toThrowError(
|
|
187
208
|
new UplinkFetcherError(
|
|
@@ -200,7 +221,9 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
200
221
|
endpoint: mockCloudConfigUrl1,
|
|
201
222
|
errorReportingEndpoint: mockOutOfBandReporterUrl,
|
|
202
223
|
fetcher,
|
|
224
|
+
requestTimeoutMs,
|
|
203
225
|
compositionId: null,
|
|
226
|
+
logger,
|
|
204
227
|
}),
|
|
205
228
|
).rejects.toThrowError(
|
|
206
229
|
new UplinkFetcherError(
|
|
@@ -220,7 +243,9 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
220
243
|
endpoint: mockCloudConfigUrl1,
|
|
221
244
|
errorReportingEndpoint: mockOutOfBandReporterUrl,
|
|
222
245
|
fetcher,
|
|
246
|
+
requestTimeoutMs,
|
|
223
247
|
compositionId: null,
|
|
248
|
+
logger,
|
|
224
249
|
}),
|
|
225
250
|
).rejects.toThrowError(
|
|
226
251
|
new UplinkFetcherError(
|
|
@@ -240,7 +265,9 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
240
265
|
endpoint: mockCloudConfigUrl1,
|
|
241
266
|
errorReportingEndpoint: mockOutOfBandReporterUrl,
|
|
242
267
|
fetcher,
|
|
268
|
+
requestTimeoutMs,
|
|
243
269
|
compositionId: null,
|
|
270
|
+
logger,
|
|
244
271
|
}),
|
|
245
272
|
).rejects.toThrowError(
|
|
246
273
|
new UplinkFetcherError(
|
|
@@ -260,7 +287,9 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
260
287
|
endpoint: mockCloudConfigUrl1,
|
|
261
288
|
errorReportingEndpoint: mockOutOfBandReporterUrl,
|
|
262
289
|
fetcher,
|
|
290
|
+
requestTimeoutMs,
|
|
263
291
|
compositionId: null,
|
|
292
|
+
logger,
|
|
264
293
|
}),
|
|
265
294
|
).rejects.toThrowError(
|
|
266
295
|
new UplinkFetcherError(
|
|
@@ -281,7 +310,9 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
281
310
|
endpoint: mockCloudConfigUrl1,
|
|
282
311
|
errorReportingEndpoint: mockOutOfBandReporterUrl,
|
|
283
312
|
fetcher,
|
|
313
|
+
requestTimeoutMs,
|
|
284
314
|
compositionId: null,
|
|
315
|
+
logger,
|
|
285
316
|
}),
|
|
286
317
|
).rejects.toThrowError(
|
|
287
318
|
new UplinkFetcherError(
|
|
@@ -301,7 +332,9 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
301
332
|
endpoint: mockCloudConfigUrl1,
|
|
302
333
|
errorReportingEndpoint: mockOutOfBandReporterUrl,
|
|
303
334
|
fetcher,
|
|
335
|
+
requestTimeoutMs,
|
|
304
336
|
compositionId: null,
|
|
337
|
+
logger,
|
|
305
338
|
}),
|
|
306
339
|
).rejects.toThrowError(
|
|
307
340
|
new UplinkFetcherError(
|
|
@@ -321,7 +354,9 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
321
354
|
endpoint: mockCloudConfigUrl1,
|
|
322
355
|
errorReportingEndpoint: mockOutOfBandReporterUrl,
|
|
323
356
|
fetcher,
|
|
357
|
+
requestTimeoutMs,
|
|
324
358
|
compositionId: null,
|
|
359
|
+
logger,
|
|
325
360
|
}),
|
|
326
361
|
).rejects.toThrowError(
|
|
327
362
|
new UplinkFetcherError(
|
|
@@ -341,7 +376,9 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
341
376
|
endpoint: mockCloudConfigUrl1,
|
|
342
377
|
errorReportingEndpoint: mockOutOfBandReporterUrl,
|
|
343
378
|
fetcher,
|
|
379
|
+
requestTimeoutMs,
|
|
344
380
|
compositionId: null,
|
|
381
|
+
logger,
|
|
345
382
|
}),
|
|
346
383
|
).rejects.toThrowError(
|
|
347
384
|
new UplinkFetcherError(
|
|
@@ -359,7 +396,9 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
359
396
|
endpoint: mockCloudConfigUrl1,
|
|
360
397
|
errorReportingEndpoint: mockOutOfBandReporterUrl,
|
|
361
398
|
fetcher,
|
|
399
|
+
requestTimeoutMs,
|
|
362
400
|
compositionId: 'id-1234',
|
|
401
|
+
logger,
|
|
363
402
|
});
|
|
364
403
|
expect(result).toBeNull();
|
|
365
404
|
});
|
|
@@ -382,20 +421,20 @@ describe('loadSupergraphSdlFromUplinks', () => {
|
|
|
382
421
|
calls++;
|
|
383
422
|
return fetcher(...args);
|
|
384
423
|
},
|
|
424
|
+
requestTimeoutMs,
|
|
385
425
|
compositionId: 'id-1234',
|
|
386
426
|
maxRetries: 5,
|
|
387
427
|
roundRobinSeed: 0,
|
|
388
|
-
|
|
428
|
+
logger,
|
|
389
429
|
});
|
|
390
430
|
|
|
391
431
|
expect(result).toBeNull();
|
|
392
432
|
expect(calls).toBe(1);
|
|
393
433
|
});
|
|
394
434
|
|
|
395
|
-
it('
|
|
396
|
-
const timeoutSpy = jest.spyOn(global, 'setTimeout');
|
|
397
|
-
|
|
435
|
+
it('Retries on error', async () => {
|
|
398
436
|
mockSupergraphSdlRequest('originalId-1234', mockCloudConfigUrl1).reply(500);
|
|
437
|
+
const supergraphSdl = getTestingSupergraphSdl();
|
|
399
438
|
mockSupergraphSdlRequestIfAfter(
|
|
400
439
|
'originalId-1234',
|
|
401
440
|
mockCloudConfigUrl2,
|
|
@@ -406,29 +445,25 @@ describe('loadSupergraphSdlFromUplinks', () => {
|
|
|
406
445
|
routerConfig: {
|
|
407
446
|
__typename: 'RouterConfigResult',
|
|
408
447
|
id: 'originalId-1234',
|
|
409
|
-
supergraphSdl
|
|
448
|
+
supergraphSdl,
|
|
410
449
|
},
|
|
411
450
|
},
|
|
412
451
|
}),
|
|
413
452
|
);
|
|
414
453
|
|
|
415
|
-
await loadSupergraphSdlFromUplinks({
|
|
454
|
+
const result = await loadSupergraphSdlFromUplinks({
|
|
416
455
|
graphRef,
|
|
417
456
|
apiKey,
|
|
418
457
|
endpoints: [mockCloudConfigUrl1, mockCloudConfigUrl2],
|
|
419
458
|
errorReportingEndpoint: undefined,
|
|
420
|
-
fetcher
|
|
459
|
+
fetcher,
|
|
460
|
+
requestTimeoutMs,
|
|
421
461
|
compositionId: 'originalId-1234',
|
|
422
462
|
maxRetries: 1,
|
|
423
463
|
roundRobinSeed: 0,
|
|
424
|
-
|
|
464
|
+
logger,
|
|
425
465
|
});
|
|
426
466
|
|
|
427
|
-
|
|
428
|
-
const setTimeoutCall = timeoutSpy.mock.calls[1][1];
|
|
429
|
-
expect(setTimeoutCall).toBeLessThanOrEqual(1000);
|
|
430
|
-
expect(setTimeoutCall).toBeGreaterThanOrEqual(900);
|
|
431
|
-
|
|
432
|
-
timeoutSpy.mockRestore();
|
|
467
|
+
expect(result?.supergraphSdl).toEqual(supergraphSdl);
|
|
433
468
|
});
|
|
434
469
|
});
|