@apollo/gateway 2.0.6-rc.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/config.js +2 -0
- package/dist/config.js.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 +8 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +27 -62
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +3 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +15 -0
- package/dist/logger.js.map +1 -0
- 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 +61 -0
- package/dist/supergraphManagers/UplinkSupergraphManager/index.d.ts.map +1 -0
- package/dist/supergraphManagers/UplinkSupergraphManager/index.js +209 -0
- package/dist/supergraphManagers/UplinkSupergraphManager/index.js.map +1 -0
- package/dist/supergraphManagers/{UplinkFetcher → UplinkSupergraphManager}/loadSupergraphSdlFromStorage.d.ts +8 -4
- package/dist/supergraphManagers/UplinkSupergraphManager/loadSupergraphSdlFromStorage.d.ts.map +1 -0
- package/dist/supergraphManagers/{UplinkFetcher → UplinkSupergraphManager}/loadSupergraphSdlFromStorage.js +20 -8
- package/dist/supergraphManagers/UplinkSupergraphManager/loadSupergraphSdlFromStorage.js.map +1 -0
- package/dist/supergraphManagers/{UplinkFetcher → UplinkSupergraphManager}/outOfBandReporter.d.ts +2 -1
- package/dist/supergraphManagers/UplinkSupergraphManager/outOfBandReporter.d.ts.map +1 -0
- package/dist/supergraphManagers/{UplinkFetcher → UplinkSupergraphManager}/outOfBandReporter.js +0 -0
- package/dist/supergraphManagers/UplinkSupergraphManager/outOfBandReporter.js.map +1 -0
- 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/dist/supergraphManagers/index.d.ts +2 -2
- package/dist/supergraphManagers/index.d.ts.map +1 -1
- package/dist/supergraphManagers/index.js +17 -4
- package/dist/supergraphManagers/index.js.map +1 -1
- package/package.json +10 -10
- 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 -45
- package/src/__tests__/integration/managed.test.ts +292 -0
- package/src/__tests__/integration/networkRequests.test.ts +14 -54
- package/src/__tests__/integration/nockMocks.ts +7 -6
- package/src/config.ts +3 -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 +33 -88
- package/src/logger.ts +11 -0
- 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 +65 -0
- package/src/supergraphManagers/{UplinkFetcher → UplinkSupergraphManager}/__tests__/loadSupergraphSdlFromStorage.test.ts +51 -16
- package/src/supergraphManagers/{UplinkFetcher → UplinkSupergraphManager}/__tests__/tsconfig.json +0 -0
- package/src/supergraphManagers/UplinkSupergraphManager/index.ts +312 -0
- package/src/supergraphManagers/{UplinkFetcher → UplinkSupergraphManager}/loadSupergraphSdlFromStorage.ts +32 -12
- package/src/supergraphManagers/{UplinkFetcher → UplinkSupergraphManager}/outOfBandReporter.ts +2 -1
- package/src/supergraphManagers/UplinkSupergraphManager/types.ts +10 -0
- package/src/supergraphManagers/index.ts +2 -2
- package/dist/supergraphManagers/UplinkFetcher/index.d.ts +0 -35
- package/dist/supergraphManagers/UplinkFetcher/index.d.ts.map +0 -1
- package/dist/supergraphManagers/UplinkFetcher/index.js +0 -114
- package/dist/supergraphManagers/UplinkFetcher/index.js.map +0 -1
- package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.d.ts.map +0 -1
- package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.js.map +0 -1
- package/dist/supergraphManagers/UplinkFetcher/outOfBandReporter.d.ts.map +0 -1
- package/dist/supergraphManagers/UplinkFetcher/outOfBandReporter.js.map +0 -1
- package/src/supergraphManagers/UplinkFetcher/index.ts +0 -149
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
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
GraphQLSchema,
|
|
14
14
|
VariableDefinitionNode,
|
|
15
15
|
} from 'graphql';
|
|
16
|
-
import loglevel from 'loglevel';
|
|
17
16
|
import { buildOperationContext, OperationContext } from './operationContext';
|
|
18
17
|
import {
|
|
19
18
|
executeQueryPlan,
|
|
@@ -26,7 +25,6 @@ import {
|
|
|
26
25
|
} from './datasources/types';
|
|
27
26
|
import { RemoteGraphQLDataSource } from './datasources/RemoteGraphQLDataSource';
|
|
28
27
|
import { getVariableValues } from 'graphql/execution/values';
|
|
29
|
-
import fetcher from 'make-fetch-happen';
|
|
30
28
|
import {
|
|
31
29
|
QueryPlanner,
|
|
32
30
|
QueryPlan,
|
|
@@ -46,7 +44,6 @@ import {
|
|
|
46
44
|
isManagedConfig,
|
|
47
45
|
SupergraphSdlUpdate,
|
|
48
46
|
isManuallyManagedSupergraphSdlGatewayConfig,
|
|
49
|
-
ManagedGatewayConfig,
|
|
50
47
|
isStaticSupergraphSdlConfig,
|
|
51
48
|
SupergraphManager,
|
|
52
49
|
} from './config';
|
|
@@ -55,7 +52,7 @@ import { OpenTelemetrySpanNames, tracer } from './utilities/opentelemetry';
|
|
|
55
52
|
import { addExtensions } from './schema-helper/addExtensions';
|
|
56
53
|
import {
|
|
57
54
|
IntrospectAndCompose,
|
|
58
|
-
|
|
55
|
+
UplinkSupergraphManager,
|
|
59
56
|
LegacyFetcher,
|
|
60
57
|
LocalCompose,
|
|
61
58
|
} from './supergraphManagers';
|
|
@@ -65,7 +62,7 @@ import {
|
|
|
65
62
|
Schema,
|
|
66
63
|
ServiceDefinition,
|
|
67
64
|
} from '@apollo/federation-internals';
|
|
68
|
-
import {
|
|
65
|
+
import { getDefaultLogger } from './logger';
|
|
69
66
|
|
|
70
67
|
type DataSourceMap = {
|
|
71
68
|
[serviceName: string]: { url?: string; dataSource: GraphQLDataSource };
|
|
@@ -145,9 +142,10 @@ export class ApolloGateway implements GraphQLService {
|
|
|
145
142
|
private warnedStates: WarnedStates = Object.create(null);
|
|
146
143
|
private queryPlanner?: QueryPlanner;
|
|
147
144
|
private supergraphSdl?: string;
|
|
148
|
-
private
|
|
145
|
+
private supergraphSchema?: GraphQLSchema;
|
|
149
146
|
private compositionId?: string;
|
|
150
147
|
private state: GatewayState;
|
|
148
|
+
private _supergraphManager?: SupergraphManager;
|
|
151
149
|
|
|
152
150
|
// Observe query plan, service info, and operation info prior to execution.
|
|
153
151
|
// The information made available here will give insight into the resulting
|
|
@@ -169,11 +167,10 @@ export class ApolloGateway implements GraphQLService {
|
|
|
169
167
|
...config,
|
|
170
168
|
};
|
|
171
169
|
|
|
172
|
-
this.logger = this.
|
|
170
|
+
this.logger = this.config.logger ?? getDefaultLogger(this.config.debug);
|
|
173
171
|
this.queryPlanStore = this.initQueryPlanStore(
|
|
174
172
|
config?.experimental_approximateQueryPlanStoreMiB,
|
|
175
173
|
);
|
|
176
|
-
this.fetcher = config?.fetcher || fetcher;
|
|
177
174
|
|
|
178
175
|
// set up experimental observability callbacks and config settings
|
|
179
176
|
this.experimental_didResolveQueryPlan =
|
|
@@ -194,23 +191,8 @@ export class ApolloGateway implements GraphQLService {
|
|
|
194
191
|
this.state = { phase: 'initialized' };
|
|
195
192
|
}
|
|
196
193
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if (this.config.logger) {
|
|
200
|
-
return this.config.logger;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// If the user didn't provide their own logger, we'll initialize one.
|
|
204
|
-
const loglevelLogger = loglevel.getLogger(`apollo-gateway`);
|
|
205
|
-
|
|
206
|
-
// And also support the `debug` option, if it's truthy.
|
|
207
|
-
if (this.config.debug === true) {
|
|
208
|
-
loglevelLogger.setLevel(loglevelLogger.levels.DEBUG);
|
|
209
|
-
} else {
|
|
210
|
-
loglevelLogger.setLevel(loglevelLogger.levels.WARN);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
return loglevelLogger;
|
|
194
|
+
public get supergraphManager(): SupergraphManager | undefined {
|
|
195
|
+
return this._supergraphManager;
|
|
214
196
|
}
|
|
215
197
|
|
|
216
198
|
private initQueryPlanStore(approximateQueryPlanStoreMiB?: number) {
|
|
@@ -226,22 +208,6 @@ export class ApolloGateway implements GraphQLService {
|
|
|
226
208
|
}
|
|
227
209
|
|
|
228
210
|
private issueConfigurationWarningsIfApplicable() {
|
|
229
|
-
// Warn against a pollInterval of < 10s in managed mode and reset it to 10s
|
|
230
|
-
if (
|
|
231
|
-
isManagedConfig(this.config) &&
|
|
232
|
-
this.pollIntervalInMs &&
|
|
233
|
-
this.pollIntervalInMs < 10000
|
|
234
|
-
) {
|
|
235
|
-
this.pollIntervalInMs = 10000;
|
|
236
|
-
this.logger.warn(
|
|
237
|
-
'Polling Apollo services at a frequency of less than once per 10 ' +
|
|
238
|
-
'seconds (10000) is disallowed. Instead, the minimum allowed ' +
|
|
239
|
-
'pollInterval of 10000 will be used. Please reconfigure your ' +
|
|
240
|
-
'`fallbackPollIntervalInMs` accordingly. If this is problematic for ' +
|
|
241
|
-
'your team, please contact support.',
|
|
242
|
-
);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
211
|
// Warn against using the pollInterval and a serviceList simultaneously
|
|
246
212
|
// TODO(trevor:removeServiceList)
|
|
247
213
|
if (this.pollIntervalInMs && isServiceListConfig(this.config)) {
|
|
@@ -381,19 +347,21 @@ export class ApolloGateway implements GraphQLService {
|
|
|
381
347
|
'`serviceList`, `supergraphSdl`, and `experimental_updateServiceDefinitions`.',
|
|
382
348
|
);
|
|
383
349
|
}
|
|
384
|
-
const uplinkEndpoints = this.getUplinkEndpoints(this.config);
|
|
385
350
|
|
|
351
|
+
const schemaDeliveryEndpoints: string[] | undefined = this.config
|
|
352
|
+
.schemaConfigDeliveryEndpoint
|
|
353
|
+
? [this.config.schemaConfigDeliveryEndpoint]
|
|
354
|
+
: undefined;
|
|
386
355
|
await this.initializeSupergraphManager(
|
|
387
|
-
new
|
|
356
|
+
new UplinkSupergraphManager({
|
|
388
357
|
graphRef: this.apolloConfig!.graphRef!,
|
|
389
358
|
apiKey: this.apolloConfig!.key!,
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
this.config.
|
|
393
|
-
|
|
394
|
-
fetcher: this.fetcher,
|
|
359
|
+
shouldRunSubgraphHealthcheck: this.config.serviceHealthCheck,
|
|
360
|
+
uplinkEndpoints:
|
|
361
|
+
this.config.uplinkEndpoints ?? schemaDeliveryEndpoints,
|
|
362
|
+
maxRetries: this.config.uplinkMaxRetries,
|
|
395
363
|
logger: this.logger,
|
|
396
|
-
fallbackPollIntervalInMs: this.pollIntervalInMs
|
|
364
|
+
fallbackPollIntervalInMs: this.pollIntervalInMs,
|
|
397
365
|
}),
|
|
398
366
|
);
|
|
399
367
|
}
|
|
@@ -415,29 +383,6 @@ export class ApolloGateway implements GraphQLService {
|
|
|
415
383
|
};
|
|
416
384
|
}
|
|
417
385
|
|
|
418
|
-
private getUplinkEndpoints(config: ManagedGatewayConfig) {
|
|
419
|
-
/**
|
|
420
|
-
* Configuration priority order:
|
|
421
|
-
* 1. `uplinkEndpoints` configuration option
|
|
422
|
-
* 2. (deprecated) `schemaConfigDeliveryEndpoint` configuration option
|
|
423
|
-
* 3. APOLLO_SCHEMA_CONFIG_DELIVERY_ENDPOINT environment variable
|
|
424
|
-
* 4. default (GCP and AWS)
|
|
425
|
-
*/
|
|
426
|
-
const rawEndpointsString =
|
|
427
|
-
process.env.APOLLO_SCHEMA_CONFIG_DELIVERY_ENDPOINT;
|
|
428
|
-
const envEndpoints = rawEndpointsString?.split(',') ?? null;
|
|
429
|
-
return (
|
|
430
|
-
config.uplinkEndpoints ??
|
|
431
|
-
(config.schemaConfigDeliveryEndpoint
|
|
432
|
-
? [config.schemaConfigDeliveryEndpoint]
|
|
433
|
-
: null) ??
|
|
434
|
-
envEndpoints ?? [
|
|
435
|
-
'https://uplink.api.apollographql.com/',
|
|
436
|
-
'https://aws.uplink.api.apollographql.com/',
|
|
437
|
-
]
|
|
438
|
-
);
|
|
439
|
-
}
|
|
440
|
-
|
|
441
386
|
private getIdForSupergraphSdl(supergraphSdl: string) {
|
|
442
387
|
return createHash('sha256').update(supergraphSdl).digest('hex');
|
|
443
388
|
}
|
|
@@ -451,11 +396,6 @@ export class ApolloGateway implements GraphQLService {
|
|
|
451
396
|
healthCheck: this.externalSubgraphHealthCheckCallback.bind(this),
|
|
452
397
|
getDataSource: this.externalGetDataSourceCallback.bind(this),
|
|
453
398
|
});
|
|
454
|
-
if (!result?.supergraphSdl) {
|
|
455
|
-
throw new Error(
|
|
456
|
-
'Provided `supergraphSdl` function did not return an object containing a `supergraphSdl` property',
|
|
457
|
-
);
|
|
458
|
-
}
|
|
459
399
|
if (result?.cleanup) {
|
|
460
400
|
if (typeof result.cleanup === 'function') {
|
|
461
401
|
this.toDispose.push(result.cleanup);
|
|
@@ -473,6 +413,7 @@ export class ApolloGateway implements GraphQLService {
|
|
|
473
413
|
throw e;
|
|
474
414
|
}
|
|
475
415
|
|
|
416
|
+
this._supergraphManager = supergraphManager;
|
|
476
417
|
this.state = { phase: 'loaded' };
|
|
477
418
|
}
|
|
478
419
|
|
|
@@ -566,7 +507,7 @@ export class ApolloGateway implements GraphQLService {
|
|
|
566
507
|
// In the case that it throws, the gateway will:
|
|
567
508
|
// * on initial load, throw the error
|
|
568
509
|
// * on update, log the error and don't update
|
|
569
|
-
const {
|
|
510
|
+
const { supergraphSchema, supergraphSdl } = this.createSchemaFromSupergraphSdl(
|
|
570
511
|
result.supergraphSdl,
|
|
571
512
|
);
|
|
572
513
|
|
|
@@ -575,25 +516,26 @@ export class ApolloGateway implements GraphQLService {
|
|
|
575
516
|
const previousCompositionId = this.compositionId;
|
|
576
517
|
|
|
577
518
|
if (previousSchema) {
|
|
578
|
-
this.logger.info(
|
|
519
|
+
this.logger.info(`Updated Supergraph SDL was found [Composition ID ${this.compositionId} => ${result.id}]`);
|
|
579
520
|
}
|
|
580
521
|
|
|
581
522
|
this.compositionId = result.id;
|
|
582
523
|
this.supergraphSdl = supergraphSdl;
|
|
524
|
+
this.supergraphSchema = supergraphSchema.toGraphQLJSSchema();
|
|
583
525
|
|
|
584
526
|
if (!supergraphSdl) {
|
|
585
527
|
this.logger.error(
|
|
586
528
|
"A valid schema couldn't be composed. Falling back to previous schema.",
|
|
587
529
|
);
|
|
588
530
|
} else {
|
|
589
|
-
this.updateWithSchemaAndNotify(
|
|
531
|
+
this.updateWithSchemaAndNotify(supergraphSchema, supergraphSdl);
|
|
590
532
|
|
|
591
533
|
if (this.experimental_didUpdateSupergraph) {
|
|
592
534
|
this.experimental_didUpdateSupergraph(
|
|
593
535
|
{
|
|
594
536
|
compositionId: result.id,
|
|
595
537
|
supergraphSdl,
|
|
596
|
-
schema: schema
|
|
538
|
+
schema: this.schema!,
|
|
597
539
|
},
|
|
598
540
|
previousCompositionId && previousSupergraphSdl && previousSchema
|
|
599
541
|
? {
|
|
@@ -698,7 +640,7 @@ export class ApolloGateway implements GraphQLService {
|
|
|
698
640
|
this.createServices(serviceList);
|
|
699
641
|
|
|
700
642
|
return {
|
|
701
|
-
schema,
|
|
643
|
+
supergraphSchema: schema,
|
|
702
644
|
supergraphSdl,
|
|
703
645
|
};
|
|
704
646
|
}
|
|
@@ -903,6 +845,7 @@ export class ApolloGateway implements GraphQLService {
|
|
|
903
845
|
serviceMap,
|
|
904
846
|
requestContext,
|
|
905
847
|
operationContext,
|
|
848
|
+
this.supergraphSchema!,
|
|
906
849
|
);
|
|
907
850
|
|
|
908
851
|
const shouldShowQueryPlan =
|
|
@@ -1008,9 +951,6 @@ export class ApolloGateway implements GraphQLService {
|
|
|
1008
951
|
switch (this.state.phase) {
|
|
1009
952
|
case 'initialized':
|
|
1010
953
|
case 'failed to load':
|
|
1011
|
-
throw Error(
|
|
1012
|
-
'ApolloGateway.stop does not need to be called before ApolloGateway.load is called successfully',
|
|
1013
|
-
);
|
|
1014
954
|
case 'stopped':
|
|
1015
955
|
// Calls to stop() are idempotent.
|
|
1016
956
|
return;
|
|
@@ -1105,6 +1045,7 @@ export {
|
|
|
1105
1045
|
CompositionInfo,
|
|
1106
1046
|
IntrospectAndCompose,
|
|
1107
1047
|
LocalCompose,
|
|
1048
|
+
UplinkSupergraphManager,
|
|
1108
1049
|
};
|
|
1109
1050
|
|
|
1110
1051
|
export * from './datasources';
|
|
@@ -1114,8 +1055,12 @@ export {
|
|
|
1114
1055
|
SubgraphHealthCheckFunction,
|
|
1115
1056
|
GetDataSourceFunction,
|
|
1116
1057
|
SupergraphSdlHook,
|
|
1117
|
-
SupergraphManager
|
|
1058
|
+
SupergraphManager,
|
|
1118
1059
|
} from './config';
|
|
1119
1060
|
|
|
1120
|
-
export {
|
|
1121
|
-
|
|
1061
|
+
export {
|
|
1062
|
+
UplinkFetcherError,
|
|
1063
|
+
FailureToFetchSupergraphSdlAfterInit,
|
|
1064
|
+
FailureToFetchSupergraphSdlDuringInit,
|
|
1065
|
+
FailureToFetchSupergraphSdlFunctionParams,
|
|
1066
|
+
} from './supergraphManagers';
|
package/src/logger.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import loglevel from 'loglevel';
|
|
2
|
+
import type { Logger } from '@apollo/utils.logger';
|
|
3
|
+
|
|
4
|
+
export function getDefaultLogger(debug: boolean = true): Logger {
|
|
5
|
+
const logger = loglevel.getLogger('apollo-gateway');
|
|
6
|
+
|
|
7
|
+
const level = debug === true ? loglevel.levels.DEBUG : loglevel.levels.WARN;
|
|
8
|
+
logger.setLevel(level);
|
|
9
|
+
|
|
10
|
+
return logger;
|
|
11
|
+
}
|
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
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import mockedEnv from 'mocked-env';
|
|
2
|
+
|
|
3
|
+
import { UplinkSupergraphManager } from '@apollo/gateway';
|
|
4
|
+
|
|
5
|
+
let cleanUp: (() => void) | undefined;
|
|
6
|
+
|
|
7
|
+
const logger = {
|
|
8
|
+
warn: jest.fn(),
|
|
9
|
+
debug: jest.fn(),
|
|
10
|
+
error: jest.fn(),
|
|
11
|
+
info: jest.fn(),
|
|
12
|
+
};
|
|
13
|
+
const apiKey = 'OU812';
|
|
14
|
+
const graphRef = 'graph@ref';
|
|
15
|
+
|
|
16
|
+
afterEach(async () => {
|
|
17
|
+
if (cleanUp) {
|
|
18
|
+
cleanUp();
|
|
19
|
+
cleanUp = undefined;
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe('UplinkSupergraphManager', () => {
|
|
24
|
+
it('can be minimally constructed', () => {
|
|
25
|
+
new UplinkSupergraphManager({ apiKey, graphRef });
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe('setting uplink URLs', () => {
|
|
29
|
+
it('uses default uplink URLs', async () => {
|
|
30
|
+
const manager = new UplinkSupergraphManager({ apiKey, graphRef, logger });
|
|
31
|
+
|
|
32
|
+
expect(manager.uplinkEndpoints).toEqual(UplinkSupergraphManager.DEFAULT_UPLINK_ENDPOINTS);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('can set uplink URLs via config', async () => {
|
|
36
|
+
cleanUp = mockedEnv({
|
|
37
|
+
APOLLO_SCHEMA_CONFIG_DELIVERY_ENDPOINT: 'https://env-delivery.com',
|
|
38
|
+
});
|
|
39
|
+
const uplinkEndpoints = [
|
|
40
|
+
'https://config-delivery1.com',
|
|
41
|
+
'https://config-delivery2.com',
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
const manager = new UplinkSupergraphManager({
|
|
45
|
+
apiKey,
|
|
46
|
+
graphRef,
|
|
47
|
+
uplinkEndpoints,
|
|
48
|
+
logger,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
expect(manager.uplinkEndpoints).toEqual(uplinkEndpoints);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('can set uplink URLs via environment variable', async () => {
|
|
55
|
+
const uplinkUrl = 'https://env-delivery.com';
|
|
56
|
+
cleanUp = mockedEnv({
|
|
57
|
+
APOLLO_SCHEMA_CONFIG_DELIVERY_ENDPOINT: uplinkUrl,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const manager = new UplinkSupergraphManager({ apiKey, graphRef, logger });
|
|
61
|
+
|
|
62
|
+
expect(manager.uplinkEndpoints).toEqual([uplinkUrl]);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
});
|