@apollo/gateway 2.0.0-alpha.0 → 2.0.0-alpha.4
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/README.md +1 -1
- package/dist/__generated__/graphqlTypes.d.ts +13 -11
- package/dist/__generated__/graphqlTypes.d.ts.map +1 -1
- package/dist/__generated__/graphqlTypes.js.map +1 -1
- package/dist/config.d.ts +45 -24
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +30 -31
- package/dist/config.js.map +1 -1
- package/dist/datasources/LocalGraphQLDataSource.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/datasources/types.d.ts +1 -1
- package/dist/datasources/types.d.ts.map +1 -1
- package/dist/executeQueryPlan.d.ts.map +1 -1
- package/dist/executeQueryPlan.js +6 -6
- package/dist/executeQueryPlan.js.map +1 -1
- package/dist/index.d.ts +36 -23
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +197 -297
- package/dist/index.js.map +1 -1
- package/dist/operationContext.js +0 -1
- package/dist/operationContext.js.map +1 -1
- package/dist/schema-helper/addResolversToSchema.d.ts +4 -0
- package/dist/schema-helper/addResolversToSchema.d.ts.map +1 -0
- package/dist/schema-helper/addResolversToSchema.js +62 -0
- package/dist/schema-helper/addResolversToSchema.js.map +1 -0
- package/dist/schema-helper/error.d.ts +6 -0
- package/dist/schema-helper/error.d.ts.map +1 -0
- package/dist/schema-helper/error.js +14 -0
- package/dist/schema-helper/error.js.map +1 -0
- package/dist/schema-helper/index.d.ts +4 -0
- package/dist/schema-helper/index.d.ts.map +1 -0
- package/dist/schema-helper/index.js +16 -0
- package/dist/schema-helper/index.js.map +1 -0
- package/dist/schema-helper/resolverMap.d.ts +16 -0
- package/dist/schema-helper/resolverMap.d.ts.map +1 -0
- package/dist/schema-helper/resolverMap.js +3 -0
- package/dist/schema-helper/resolverMap.js.map +1 -0
- package/dist/supergraphManagers/IntrospectAndCompose/index.d.ts +31 -0
- package/dist/supergraphManagers/IntrospectAndCompose/index.d.ts.map +1 -0
- package/dist/supergraphManagers/IntrospectAndCompose/index.js +112 -0
- package/dist/supergraphManagers/IntrospectAndCompose/index.js.map +1 -0
- package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.d.ts +12 -0
- package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.d.ts.map +1 -0
- package/dist/{loadServicesFromRemoteEndpoint.js → supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.js} +6 -6
- package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.js.map +1 -0
- package/dist/supergraphManagers/LegacyFetcher/index.d.ts +33 -0
- package/dist/supergraphManagers/LegacyFetcher/index.d.ts.map +1 -0
- package/dist/supergraphManagers/LegacyFetcher/index.js +149 -0
- package/dist/supergraphManagers/LegacyFetcher/index.js.map +1 -0
- package/dist/supergraphManagers/LocalCompose/index.d.ts +19 -0
- package/dist/supergraphManagers/LocalCompose/index.d.ts.map +1 -0
- package/dist/supergraphManagers/LocalCompose/index.js +55 -0
- package/dist/supergraphManagers/LocalCompose/index.js.map +1 -0
- package/dist/supergraphManagers/UplinkFetcher/index.d.ts +32 -0
- package/dist/supergraphManagers/UplinkFetcher/index.d.ts.map +1 -0
- package/dist/supergraphManagers/UplinkFetcher/index.js +96 -0
- package/dist/supergraphManagers/UplinkFetcher/index.js.map +1 -0
- package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.d.ts +21 -0
- package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.d.ts.map +1 -0
- package/dist/{loadSupergraphSdlFromStorage.js → supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.js} +41 -10
- package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.js.map +1 -0
- package/dist/supergraphManagers/UplinkFetcher/outOfBandReporter.d.ts +13 -0
- package/dist/supergraphManagers/UplinkFetcher/outOfBandReporter.d.ts.map +1 -0
- package/dist/supergraphManagers/UplinkFetcher/outOfBandReporter.js +85 -0
- package/dist/supergraphManagers/UplinkFetcher/outOfBandReporter.js.map +1 -0
- package/dist/supergraphManagers/index.d.ts +5 -0
- package/dist/supergraphManagers/index.d.ts.map +1 -0
- package/dist/supergraphManagers/index.js +12 -0
- package/dist/supergraphManagers/index.js.map +1 -0
- package/dist/utilities/array.js +1 -1
- package/dist/utilities/array.js.map +1 -1
- package/dist/utilities/createHash.d.ts +2 -0
- package/dist/utilities/createHash.d.ts.map +1 -0
- package/dist/utilities/createHash.js +15 -0
- package/dist/utilities/createHash.js.map +1 -0
- package/dist/utilities/isNodeLike.d.ts +3 -0
- package/dist/utilities/isNodeLike.d.ts.map +1 -0
- package/dist/utilities/isNodeLike.js +8 -0
- package/dist/utilities/isNodeLike.js.map +1 -0
- package/package.json +9 -9
- package/src/__generated__/graphqlTypes.ts +13 -11
- package/src/__mocks__/make-fetch-happen-fetcher.ts +3 -1
- package/src/__tests__/buildQueryPlan.test.ts +1 -1
- package/src/__tests__/executeQueryPlan.test.ts +1171 -77
- package/src/__tests__/execution-utils.ts +5 -7
- package/src/__tests__/gateway/buildService.test.ts +3 -3
- package/src/__tests__/gateway/endToEnd.test.ts +1 -1
- package/src/__tests__/gateway/executor.test.ts +3 -1
- package/src/__tests__/gateway/lifecycle-hooks.test.ts +59 -121
- package/src/__tests__/gateway/opentelemetry.test.ts +8 -3
- package/src/__tests__/gateway/queryPlanCache.test.ts +25 -9
- package/src/__tests__/gateway/reporting.test.ts +42 -13
- package/src/__tests__/gateway/supergraphSdl.test.ts +397 -0
- package/src/__tests__/integration/aliases.test.ts +9 -3
- package/src/__tests__/integration/configuration.test.ts +140 -21
- package/src/__tests__/integration/logger.test.ts +2 -2
- package/src/__tests__/integration/networkRequests.test.ts +126 -149
- package/src/__tests__/integration/nockMocks.ts +57 -16
- package/src/__tests__/nockAssertions.ts +20 -0
- package/src/config.ts +153 -77
- package/src/core/__tests__/core.test.ts +6 -6
- package/src/datasources/LocalGraphQLDataSource.ts +1 -1
- package/src/datasources/RemoteGraphQLDataSource.ts +8 -2
- package/src/datasources/__tests__/LocalGraphQLDataSource.test.ts +1 -1
- package/src/datasources/__tests__/RemoteGraphQLDataSource.test.ts +4 -4
- package/src/datasources/types.ts +1 -1
- package/src/executeQueryPlan.ts +18 -9
- package/src/index.ts +323 -481
- package/src/make-fetch-happen.d.ts +1 -1
- package/src/operationContext.ts +2 -2
- package/src/schema-helper/addResolversToSchema.ts +83 -0
- package/src/schema-helper/error.ts +11 -0
- package/src/schema-helper/index.ts +3 -0
- package/src/schema-helper/resolverMap.ts +23 -0
- package/src/supergraphManagers/IntrospectAndCompose/__tests__/IntrospectAndCompose.test.ts +370 -0
- package/src/{__tests__ → supergraphManagers/IntrospectAndCompose/__tests__}/loadServicesFromRemoteEndpoint.test.ts +7 -7
- package/src/supergraphManagers/IntrospectAndCompose/__tests__/tsconfig.json +8 -0
- package/src/supergraphManagers/IntrospectAndCompose/index.ts +160 -0
- package/src/{loadServicesFromRemoteEndpoint.ts → supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.ts} +7 -7
- package/src/supergraphManagers/LegacyFetcher/index.ts +226 -0
- package/src/supergraphManagers/LocalCompose/index.ts +79 -0
- package/src/supergraphManagers/UplinkFetcher/__tests__/loadSupergraphSdlFromStorage.test.ts +343 -0
- package/src/supergraphManagers/UplinkFetcher/__tests__/tsconfig.json +8 -0
- package/src/supergraphManagers/UplinkFetcher/index.ts +128 -0
- package/src/{loadSupergraphSdlFromStorage.ts → supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.ts} +63 -14
- package/src/supergraphManagers/UplinkFetcher/outOfBandReporter.ts +126 -0
- package/src/supergraphManagers/index.ts +4 -0
- package/src/utilities/__tests__/cleanErrorOfInaccessibleElements.test.ts +13 -10
- package/src/utilities/array.ts +1 -1
- package/src/utilities/createHash.ts +10 -0
- package/src/utilities/isNodeLike.ts +11 -0
- package/CHANGELOG.md +0 -452
- package/dist/legacyLoadServicesFromStorage.d.ts +0 -20
- package/dist/legacyLoadServicesFromStorage.d.ts.map +0 -1
- package/dist/legacyLoadServicesFromStorage.js +0 -62
- package/dist/legacyLoadServicesFromStorage.js.map +0 -1
- package/dist/loadServicesFromRemoteEndpoint.d.ts +0 -13
- package/dist/loadServicesFromRemoteEndpoint.d.ts.map +0 -1
- package/dist/loadServicesFromRemoteEndpoint.js.map +0 -1
- package/dist/loadSupergraphSdlFromStorage.d.ts +0 -12
- package/dist/loadSupergraphSdlFromStorage.d.ts.map +0 -1
- package/dist/loadSupergraphSdlFromStorage.js.map +0 -1
- package/dist/outOfBandReporter.d.ts +0 -15
- package/dist/outOfBandReporter.d.ts.map +0 -1
- package/dist/outOfBandReporter.js +0 -88
- package/dist/outOfBandReporter.js.map +0 -1
- package/src/__tests__/gateway/composedSdl.test.ts +0 -44
- package/src/__tests__/integration/legacyNetworkRequests.test.ts +0 -279
- package/src/__tests__/integration/legacyNockMocks.ts +0 -113
- package/src/__tests__/loadSupergraphSdlFromStorage.test.ts +0 -664
- package/src/legacyLoadServicesFromStorage.ts +0 -170
- package/src/outOfBandReporter.ts +0 -128
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { Logger } from 'apollo-server-types';
|
|
2
|
+
import { HeadersInit } from 'node-fetch';
|
|
3
|
+
import resolvable from '@josephg/resolvable';
|
|
4
|
+
import {
|
|
5
|
+
ServiceEndpointDefinition,
|
|
6
|
+
SupergraphSdlUpdateFunction,
|
|
7
|
+
SubgraphHealthCheckFunction,
|
|
8
|
+
} from '../..';
|
|
9
|
+
import {
|
|
10
|
+
loadServicesFromRemoteEndpoint,
|
|
11
|
+
Service,
|
|
12
|
+
} from './loadServicesFromRemoteEndpoint';
|
|
13
|
+
import { SupergraphManager, SupergraphSdlHookOptions } from '../../config';
|
|
14
|
+
import { composeServices } from '@apollo/composition';
|
|
15
|
+
import { ServiceDefinition } from '@apollo/federation-internals';
|
|
16
|
+
|
|
17
|
+
export interface IntrospectAndComposeOptions {
|
|
18
|
+
subgraphs: ServiceEndpointDefinition[];
|
|
19
|
+
introspectionHeaders?:
|
|
20
|
+
| HeadersInit
|
|
21
|
+
| ((
|
|
22
|
+
service: ServiceEndpointDefinition,
|
|
23
|
+
) => Promise<HeadersInit> | HeadersInit);
|
|
24
|
+
pollIntervalInMs?: number;
|
|
25
|
+
logger?: Logger;
|
|
26
|
+
subgraphHealthCheck?: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type State =
|
|
30
|
+
| { phase: 'initialized' }
|
|
31
|
+
| { phase: 'polling'; pollingPromise?: Promise<void> }
|
|
32
|
+
| { phase: 'stopped' };
|
|
33
|
+
|
|
34
|
+
export class IntrospectAndCompose implements SupergraphManager {
|
|
35
|
+
private config: IntrospectAndComposeOptions;
|
|
36
|
+
private update?: SupergraphSdlUpdateFunction;
|
|
37
|
+
private healthCheck?: SubgraphHealthCheckFunction;
|
|
38
|
+
private subgraphs?: Service[];
|
|
39
|
+
private serviceSdlCache: Map<string, string> = new Map();
|
|
40
|
+
private timerRef: NodeJS.Timeout | null = null;
|
|
41
|
+
private state: State;
|
|
42
|
+
|
|
43
|
+
constructor(options: IntrospectAndComposeOptions) {
|
|
44
|
+
this.config = options;
|
|
45
|
+
this.state = { phase: 'initialized' };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public async initialize({ update, getDataSource, healthCheck }: SupergraphSdlHookOptions) {
|
|
49
|
+
this.update = update;
|
|
50
|
+
|
|
51
|
+
if (this.config.subgraphHealthCheck) {
|
|
52
|
+
this.healthCheck = healthCheck;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
this.subgraphs = this.config.subgraphs.map((subgraph) => ({
|
|
56
|
+
...subgraph,
|
|
57
|
+
dataSource: getDataSource(subgraph),
|
|
58
|
+
}));
|
|
59
|
+
|
|
60
|
+
let initialSupergraphSdl: string | null = null;
|
|
61
|
+
try {
|
|
62
|
+
initialSupergraphSdl = await this.updateSupergraphSdl();
|
|
63
|
+
} catch (e) {
|
|
64
|
+
this.logUpdateFailure(e);
|
|
65
|
+
throw e;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Start polling after we resolve the first supergraph
|
|
69
|
+
if (this.config.pollIntervalInMs) {
|
|
70
|
+
this.beginPolling();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
// on init, this supergraphSdl should never actually be `null`.
|
|
75
|
+
// `this.updateSupergraphSdl()` will only return null if the schema hasn't
|
|
76
|
+
// changed over the course of an _update_.
|
|
77
|
+
supergraphSdl: initialSupergraphSdl!,
|
|
78
|
+
cleanup: async () => {
|
|
79
|
+
if (this.state.phase === 'polling') {
|
|
80
|
+
await this.state.pollingPromise;
|
|
81
|
+
}
|
|
82
|
+
this.state = { phase: 'stopped' };
|
|
83
|
+
if (this.timerRef) {
|
|
84
|
+
clearTimeout(this.timerRef);
|
|
85
|
+
this.timerRef = null;
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private async updateSupergraphSdl() {
|
|
92
|
+
const result = await loadServicesFromRemoteEndpoint({
|
|
93
|
+
serviceList: this.subgraphs!,
|
|
94
|
+
getServiceIntrospectionHeaders: async (service) => {
|
|
95
|
+
return typeof this.config.introspectionHeaders === 'function'
|
|
96
|
+
? await this.config.introspectionHeaders(service)
|
|
97
|
+
: this.config.introspectionHeaders;
|
|
98
|
+
},
|
|
99
|
+
serviceSdlCache: this.serviceSdlCache,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
if (!result.isNewSchema) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const supergraphSdl = this.createSupergraphFromSubgraphList(result.serviceDefinitions!);
|
|
107
|
+
// the healthCheck fn is only assigned if it's enabled in the config
|
|
108
|
+
await this.healthCheck?.(supergraphSdl);
|
|
109
|
+
|
|
110
|
+
return supergraphSdl;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private createSupergraphFromSubgraphList(subgraphs: ServiceDefinition[]) {
|
|
114
|
+
const compositionResult = composeServices(subgraphs);
|
|
115
|
+
|
|
116
|
+
if (compositionResult.errors) {
|
|
117
|
+
const { errors } = compositionResult;
|
|
118
|
+
throw Error(
|
|
119
|
+
"A valid schema couldn't be composed. The following composition errors were found:\n" +
|
|
120
|
+
errors.map((e) => '\t' + e.message).join('\n'),
|
|
121
|
+
);
|
|
122
|
+
} else {
|
|
123
|
+
const { supergraphSdl } = compositionResult;
|
|
124
|
+
return supergraphSdl;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private beginPolling() {
|
|
129
|
+
this.state = { phase: 'polling' };
|
|
130
|
+
this.poll();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private poll() {
|
|
134
|
+
this.timerRef = setTimeout(async () => {
|
|
135
|
+
if (this.state.phase === 'polling') {
|
|
136
|
+
const pollingPromise = resolvable();
|
|
137
|
+
|
|
138
|
+
this.state.pollingPromise = pollingPromise;
|
|
139
|
+
try {
|
|
140
|
+
const maybeNewSupergraphSdl = await this.updateSupergraphSdl();
|
|
141
|
+
if (maybeNewSupergraphSdl) {
|
|
142
|
+
this.update?.(maybeNewSupergraphSdl);
|
|
143
|
+
}
|
|
144
|
+
} catch (e) {
|
|
145
|
+
this.logUpdateFailure(e);
|
|
146
|
+
}
|
|
147
|
+
pollingPromise.resolve();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
this.poll();
|
|
151
|
+
}, this.config.pollIntervalInMs!);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private logUpdateFailure(e: any) {
|
|
155
|
+
this.config.logger?.error(
|
|
156
|
+
'IntrospectAndCompose failed to update supergraph with the following error: ' +
|
|
157
|
+
(e.message ?? e),
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { GraphQLRequest } from 'apollo-server-types';
|
|
2
2
|
import { parse } from 'graphql';
|
|
3
3
|
import { Headers, HeadersInit } from 'node-fetch';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
4
|
+
import { ServiceDefinition } from '@apollo/federation-internals';
|
|
5
|
+
import { GraphQLDataSource, GraphQLDataSourceRequestKind } from '../../datasources/types';
|
|
6
|
+
import { SERVICE_DEFINITION_QUERY } from '../..';
|
|
7
|
+
import { ServiceDefinitionUpdate, ServiceEndpointDefinition } from '../../config';
|
|
8
8
|
|
|
9
|
-
type Service = ServiceEndpointDefinition & {
|
|
9
|
+
export type Service = ServiceEndpointDefinition & {
|
|
10
10
|
dataSource: GraphQLDataSource;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
-
export async function
|
|
13
|
+
export async function loadServicesFromRemoteEndpoint({
|
|
14
14
|
serviceList,
|
|
15
15
|
getServiceIntrospectionHeaders,
|
|
16
16
|
serviceSdlCache,
|
|
@@ -20,7 +20,7 @@ export async function getServiceDefinitionsFromRemoteEndpoint({
|
|
|
20
20
|
service: ServiceEndpointDefinition,
|
|
21
21
|
) => Promise<HeadersInit | undefined>;
|
|
22
22
|
serviceSdlCache: Map<string, string>;
|
|
23
|
-
}): Promise<
|
|
23
|
+
}): Promise<ServiceDefinitionUpdate> {
|
|
24
24
|
if (!serviceList || !serviceList.length) {
|
|
25
25
|
throw new Error(
|
|
26
26
|
'Tried to load services from remote endpoints but none provided',
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Similar in concept to `IntrospectAndCompose`, but this handles
|
|
3
|
+
* the `experimental_updateComposition` and `experimental_updateSupergraphSdl`
|
|
4
|
+
* configuration options of the gateway and will be removed in a future release
|
|
5
|
+
* along with those options.
|
|
6
|
+
*/
|
|
7
|
+
import { Logger } from 'apollo-server-types';
|
|
8
|
+
import resolvable from '@josephg/resolvable';
|
|
9
|
+
import {
|
|
10
|
+
SupergraphManager,
|
|
11
|
+
SupergraphSdlHookOptions,
|
|
12
|
+
DynamicGatewayConfig,
|
|
13
|
+
isSupergraphSdlUpdate,
|
|
14
|
+
isServiceDefinitionUpdate,
|
|
15
|
+
ServiceDefinitionUpdate,
|
|
16
|
+
GetDataSourceFunction,
|
|
17
|
+
} from '../../config';
|
|
18
|
+
import {
|
|
19
|
+
Experimental_UpdateComposition,
|
|
20
|
+
SubgraphHealthCheckFunction,
|
|
21
|
+
SupergraphSdlUpdateFunction,
|
|
22
|
+
} from '../..';
|
|
23
|
+
import { composeServices } from '@apollo/composition';
|
|
24
|
+
import { ServiceDefinition } from '@apollo/federation-internals';
|
|
25
|
+
|
|
26
|
+
export interface LegacyFetcherOptions {
|
|
27
|
+
pollIntervalInMs?: number;
|
|
28
|
+
logger?: Logger;
|
|
29
|
+
subgraphHealthCheck?: boolean;
|
|
30
|
+
updateServiceDefinitions: Experimental_UpdateComposition;
|
|
31
|
+
gatewayConfig: DynamicGatewayConfig;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type State =
|
|
35
|
+
| { phase: 'initialized' }
|
|
36
|
+
| { phase: 'polling'; pollingPromise?: Promise<void> }
|
|
37
|
+
| { phase: 'stopped' };
|
|
38
|
+
|
|
39
|
+
export class LegacyFetcher implements SupergraphManager {
|
|
40
|
+
private config: LegacyFetcherOptions;
|
|
41
|
+
private update?: SupergraphSdlUpdateFunction;
|
|
42
|
+
private healthCheck?: SubgraphHealthCheckFunction;
|
|
43
|
+
private getDataSource?: GetDataSourceFunction;
|
|
44
|
+
private timerRef: NodeJS.Timeout | null = null;
|
|
45
|
+
private state: State;
|
|
46
|
+
private compositionId?: string;
|
|
47
|
+
private serviceDefinitions?: ServiceDefinition[];
|
|
48
|
+
|
|
49
|
+
constructor(options: LegacyFetcherOptions) {
|
|
50
|
+
this.config = options;
|
|
51
|
+
this.state = { phase: 'initialized' };
|
|
52
|
+
this.issueDeprecationWarnings();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private issueDeprecationWarnings() {
|
|
56
|
+
if ('experimental_updateSupergraphSdl' in this.config.gatewayConfig) {
|
|
57
|
+
this.config.logger?.warn(
|
|
58
|
+
'The `experimental_updateSupergraphSdl` option is deprecated and will be removed in a future version of `@apollo/gateway`. Please migrate to the function form of the `supergraphSdl` configuration option.',
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if ('experimental_updateServiceDefinitions' in this.config.gatewayConfig) {
|
|
63
|
+
this.config.logger?.warn(
|
|
64
|
+
'The `experimental_updateServiceDefinitions` option is deprecated and will be removed in a future version of `@apollo/gateway`. Please migrate to the function form of the `supergraphSdl` configuration option.',
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public async initialize({
|
|
70
|
+
update,
|
|
71
|
+
healthCheck,
|
|
72
|
+
getDataSource,
|
|
73
|
+
}: SupergraphSdlHookOptions) {
|
|
74
|
+
this.update = update;
|
|
75
|
+
this.getDataSource = getDataSource;
|
|
76
|
+
|
|
77
|
+
if (this.config.subgraphHealthCheck) {
|
|
78
|
+
this.healthCheck = healthCheck;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let initialSupergraphSdl: string | null = null;
|
|
82
|
+
try {
|
|
83
|
+
initialSupergraphSdl = await this.updateSupergraphSdl();
|
|
84
|
+
} catch (e) {
|
|
85
|
+
this.logUpdateFailure(e);
|
|
86
|
+
throw e;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Start polling after we resolve the first supergraph
|
|
90
|
+
if (this.config.pollIntervalInMs) {
|
|
91
|
+
this.beginPolling();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
// on init, this supergraphSdl should never actually be `null`.
|
|
96
|
+
// `this.updateSupergraphSdl()` will only return null if the schema hasn't
|
|
97
|
+
// changed over the course of an _update_.
|
|
98
|
+
supergraphSdl: initialSupergraphSdl!,
|
|
99
|
+
cleanup: async () => {
|
|
100
|
+
if (this.state.phase === 'polling') {
|
|
101
|
+
await this.state.pollingPromise;
|
|
102
|
+
}
|
|
103
|
+
this.state = { phase: 'stopped' };
|
|
104
|
+
if (this.timerRef) {
|
|
105
|
+
clearTimeout(this.timerRef);
|
|
106
|
+
this.timerRef = null;
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private async updateSupergraphSdl() {
|
|
113
|
+
const result = await this.config.updateServiceDefinitions(
|
|
114
|
+
this.config.gatewayConfig,
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
if (isSupergraphSdlUpdate(result)) {
|
|
118
|
+
// no change
|
|
119
|
+
if (this.compositionId === result.id) return null;
|
|
120
|
+
|
|
121
|
+
await this.healthCheck?.(result.supergraphSdl);
|
|
122
|
+
this.compositionId = result.id;
|
|
123
|
+
return result.supergraphSdl;
|
|
124
|
+
} else if (isServiceDefinitionUpdate(result)) {
|
|
125
|
+
const supergraphSdl = this.updateByComposition(result);
|
|
126
|
+
if (!supergraphSdl) return null;
|
|
127
|
+
await this.healthCheck?.(supergraphSdl);
|
|
128
|
+
return supergraphSdl;
|
|
129
|
+
} else {
|
|
130
|
+
throw new Error(
|
|
131
|
+
'Programming error: unexpected result type from `updateServiceDefinitions`',
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private updateByComposition(result: ServiceDefinitionUpdate) {
|
|
137
|
+
if (
|
|
138
|
+
!result.serviceDefinitions ||
|
|
139
|
+
JSON.stringify(this.serviceDefinitions) ===
|
|
140
|
+
JSON.stringify(result.serviceDefinitions)
|
|
141
|
+
) {
|
|
142
|
+
this.config.logger?.debug(
|
|
143
|
+
'No change in service definitions since last check.',
|
|
144
|
+
);
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (this.serviceDefinitions) {
|
|
149
|
+
this.config.logger?.info('New service definitions were found.');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
this.serviceDefinitions = result.serviceDefinitions;
|
|
153
|
+
|
|
154
|
+
const supergraphSdl = this.createSupergraphFromServiceList(
|
|
155
|
+
result.serviceDefinitions,
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
if (!supergraphSdl) {
|
|
159
|
+
throw new Error(
|
|
160
|
+
"A valid schema couldn't be composed. Falling back to previous schema.",
|
|
161
|
+
);
|
|
162
|
+
} else {
|
|
163
|
+
return supergraphSdl;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
private createSupergraphFromServiceList(serviceList: ServiceDefinition[]) {
|
|
168
|
+
this.config.logger?.debug(
|
|
169
|
+
`Composing schema from service list: \n${serviceList
|
|
170
|
+
.map(({ name, url }) => ` ${url || 'local'}: ${name}`)
|
|
171
|
+
.join('\n')}`,
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
const compositionResult = composeServices(serviceList);
|
|
175
|
+
|
|
176
|
+
if (compositionResult.errors) {
|
|
177
|
+
const { errors } = compositionResult;
|
|
178
|
+
throw Error(
|
|
179
|
+
"A valid schema couldn't be composed. The following composition errors were found:\n" +
|
|
180
|
+
errors.map((e) => '\t' + e.message).join('\n'),
|
|
181
|
+
);
|
|
182
|
+
} else {
|
|
183
|
+
const { supergraphSdl } = compositionResult;
|
|
184
|
+
for (const service of serviceList) {
|
|
185
|
+
this.getDataSource?.(service);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
this.config.logger?.debug('Schema loaded and ready for execution');
|
|
189
|
+
|
|
190
|
+
return supergraphSdl;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
private beginPolling() {
|
|
195
|
+
this.state = { phase: 'polling' };
|
|
196
|
+
this.poll();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
private poll() {
|
|
200
|
+
this.timerRef = setTimeout(async () => {
|
|
201
|
+
if (this.state.phase === 'polling') {
|
|
202
|
+
const pollingPromise = resolvable();
|
|
203
|
+
|
|
204
|
+
this.state.pollingPromise = pollingPromise;
|
|
205
|
+
try {
|
|
206
|
+
const maybeNewSupergraphSdl = await this.updateSupergraphSdl();
|
|
207
|
+
if (maybeNewSupergraphSdl) {
|
|
208
|
+
this.update?.(maybeNewSupergraphSdl);
|
|
209
|
+
}
|
|
210
|
+
} catch (e) {
|
|
211
|
+
this.logUpdateFailure(e);
|
|
212
|
+
}
|
|
213
|
+
pollingPromise.resolve();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
this.poll();
|
|
217
|
+
}, this.config.pollIntervalInMs!);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
private logUpdateFailure(e: any) {
|
|
221
|
+
this.config.logger?.error(
|
|
222
|
+
'UplinkFetcher failed to update supergraph with the following error: ' +
|
|
223
|
+
(e.message ?? e),
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// TODO(trevor:removeServiceList) the whole file goes away
|
|
2
|
+
import { Logger } from 'apollo-server-types';
|
|
3
|
+
import { composeServices } from '@apollo/composition';
|
|
4
|
+
import {
|
|
5
|
+
GetDataSourceFunction,
|
|
6
|
+
SupergraphSdlHookOptions,
|
|
7
|
+
SupergraphManager,
|
|
8
|
+
} from '../../config';
|
|
9
|
+
import { ServiceDefinition } from '@apollo/federation-internals';
|
|
10
|
+
|
|
11
|
+
export interface LocalComposeOptions {
|
|
12
|
+
logger?: Logger;
|
|
13
|
+
localServiceList: ServiceDefinition[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class LocalCompose implements SupergraphManager {
|
|
17
|
+
private config: LocalComposeOptions;
|
|
18
|
+
private getDataSource?: GetDataSourceFunction;
|
|
19
|
+
|
|
20
|
+
constructor(options: LocalComposeOptions) {
|
|
21
|
+
this.config = options;
|
|
22
|
+
this.issueDeprecationWarnings();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
private issueDeprecationWarnings() {
|
|
26
|
+
this.config.logger?.warn(
|
|
27
|
+
'The `localServiceList` option is deprecated and will be removed in a future version of `@apollo/gateway`. Please migrate to the `LocalCompose` supergraph manager exported by `@apollo/gateway`.',
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public async initialize({ getDataSource }: SupergraphSdlHookOptions) {
|
|
32
|
+
this.getDataSource = getDataSource;
|
|
33
|
+
let supergraphSdl: string | null = null;
|
|
34
|
+
try {
|
|
35
|
+
supergraphSdl = this.createSupergraphFromServiceList(
|
|
36
|
+
this.config.localServiceList,
|
|
37
|
+
);
|
|
38
|
+
} catch (e) {
|
|
39
|
+
this.logUpdateFailure(e);
|
|
40
|
+
throw e;
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
supergraphSdl,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private createSupergraphFromServiceList(serviceList: ServiceDefinition[]) {
|
|
48
|
+
this.config.logger?.debug(
|
|
49
|
+
`Composing schema from service list: \n${serviceList
|
|
50
|
+
.map(({ name, url }) => ` ${url || 'local'}: ${name}`)
|
|
51
|
+
.join('\n')}`,
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const compositionResult = composeServices(serviceList);
|
|
55
|
+
const errors = compositionResult.errors;
|
|
56
|
+
if (errors) {
|
|
57
|
+
throw Error(
|
|
58
|
+
"A valid schema couldn't be composed. The following composition errors were found:\n" +
|
|
59
|
+
errors.map((e) => '\t' + e.message).join('\n'),
|
|
60
|
+
);
|
|
61
|
+
} else {
|
|
62
|
+
const { supergraphSdl } = compositionResult;
|
|
63
|
+
for (const service of serviceList) {
|
|
64
|
+
this.getDataSource?.(service);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
this.config.logger?.debug('Schema loaded and ready for execution');
|
|
68
|
+
|
|
69
|
+
return supergraphSdl;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private logUpdateFailure(e: any) {
|
|
74
|
+
this.config.logger?.error(
|
|
75
|
+
'UplinkFetcher failed to update supergraph with the following error: ' +
|
|
76
|
+
(e.message ?? e),
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
}
|