@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
|
@@ -2,11 +2,8 @@ import {
|
|
|
2
2
|
GraphQLSchemaValidationError,
|
|
3
3
|
GraphQLSchemaModule,
|
|
4
4
|
GraphQLResolverMap,
|
|
5
|
-
} from '
|
|
5
|
+
} from '../schema-helper';
|
|
6
6
|
import { GraphQLRequest, GraphQLExecutionResult, Logger } from 'apollo-server-types';
|
|
7
|
-
import {
|
|
8
|
-
ServiceDefinition,
|
|
9
|
-
} from '@apollo/federation';
|
|
10
7
|
import { buildSubgraphSchema } from '@apollo/subgraph';
|
|
11
8
|
import {
|
|
12
9
|
executeQueryPlan,
|
|
@@ -20,7 +17,7 @@ import { queryPlanSerializer, astSerializer } from 'apollo-federation-integratio
|
|
|
20
17
|
import gql from 'graphql-tag';
|
|
21
18
|
import { fixtures } from 'apollo-federation-integration-testsuite';
|
|
22
19
|
import { composeServices } from '@apollo/composition';
|
|
23
|
-
import { buildSchema, operationFromDocument } from '@apollo/federation-internals';
|
|
20
|
+
import { buildSchema, operationFromDocument, ServiceDefinition } from '@apollo/federation-internals';
|
|
24
21
|
|
|
25
22
|
const prettyFormat = require('pretty-format');
|
|
26
23
|
|
|
@@ -67,6 +64,7 @@ export async function execute(
|
|
|
67
64
|
const result = await executeQueryPlan(
|
|
68
65
|
queryPlan,
|
|
69
66
|
serviceMap,
|
|
67
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
70
68
|
// @ts-ignore
|
|
71
69
|
{
|
|
72
70
|
cache: undefined as any,
|
|
@@ -111,8 +109,8 @@ export function getTestingSupergraphSdl(services: typeof fixtures = fixtures) {
|
|
|
111
109
|
throw new Error(`Testing fixtures don't compose properly!\nCauses:\n${compositionResult.errors.join('\n\n')}`);
|
|
112
110
|
}
|
|
113
111
|
|
|
114
|
-
export function wait(ms: number) {
|
|
115
|
-
return new Promise(r => setTimeout(r, ms));
|
|
112
|
+
export function wait(ms: number, toResolveTo?: any) {
|
|
113
|
+
return new Promise((r) => setTimeout(() => r(toResolveTo), ms));
|
|
116
114
|
}
|
|
117
115
|
|
|
118
116
|
export function printPlan(queryPlan: QueryPlan): string {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { fetch } from '../../__mocks__/
|
|
1
|
+
import { fetch } from '../../__mocks__/make-fetch-happen-fetcher';
|
|
2
2
|
import { ApolloServerBase as ApolloServer } from 'apollo-server-core';
|
|
3
3
|
|
|
4
4
|
import { RemoteGraphQLDataSource } from '../../datasources/RemoteGraphQLDataSource';
|
|
@@ -218,7 +218,7 @@ it('does not share service definition cache between gateways', async () => {
|
|
|
218
218
|
url: 'https://api.example.com/repeat',
|
|
219
219
|
},
|
|
220
220
|
],
|
|
221
|
-
|
|
221
|
+
experimental_didUpdateSupergraph: updateObserver,
|
|
222
222
|
});
|
|
223
223
|
|
|
224
224
|
await gateway.load();
|
|
@@ -237,7 +237,7 @@ it('does not share service definition cache between gateways', async () => {
|
|
|
237
237
|
url: 'https://api.example.com/repeat',
|
|
238
238
|
},
|
|
239
239
|
],
|
|
240
|
-
|
|
240
|
+
experimental_didUpdateSupergraph: updateObserver,
|
|
241
241
|
});
|
|
242
242
|
|
|
243
243
|
await gateway.load();
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { GraphQLSchemaModule } from 'apollo-graphql';
|
|
2
1
|
import { buildSubgraphSchema } from '@apollo/subgraph';
|
|
3
2
|
import { ApolloServer } from 'apollo-server';
|
|
4
3
|
import fetch from 'node-fetch';
|
|
5
4
|
import { ApolloGateway } from '../..';
|
|
6
5
|
import { fixtures } from 'apollo-federation-integration-testsuite';
|
|
7
6
|
import { ApolloServerPluginInlineTrace } from 'apollo-server-core';
|
|
7
|
+
import { GraphQLSchemaModule } from '../../schema-helper';
|
|
8
8
|
|
|
9
9
|
async function startFederatedServer(modules: GraphQLSchemaModule[]) {
|
|
10
10
|
const schema = buildSubgraphSchema(modules);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { fetch } from '../../__mocks__/make-fetch-happen-fetcher';
|
|
1
2
|
import gql from 'graphql-tag';
|
|
2
3
|
import { ApolloGateway } from '../../';
|
|
3
4
|
import { fixtures } from 'apollo-federation-integration-testsuite';
|
|
4
5
|
import { Logger } from 'apollo-server-types';
|
|
5
|
-
import { fetch } from '../../__mocks__/apollo-server-env';
|
|
6
6
|
|
|
7
7
|
let logger: {
|
|
8
8
|
warn: jest.MockedFunction<Logger['warn']>,
|
|
@@ -38,6 +38,7 @@ describe('ApolloGateway executor', () => {
|
|
|
38
38
|
}
|
|
39
39
|
`;
|
|
40
40
|
|
|
41
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
41
42
|
// @ts-ignore
|
|
42
43
|
const { errors } = await executor({
|
|
43
44
|
source,
|
|
@@ -73,6 +74,7 @@ describe('ApolloGateway executor', () => {
|
|
|
73
74
|
}
|
|
74
75
|
`;
|
|
75
76
|
|
|
77
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
76
78
|
// @ts-ignore
|
|
77
79
|
const { errors, data } = await executor({
|
|
78
80
|
source,
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import gql from 'graphql-tag';
|
|
2
2
|
import { ApolloGateway } from '../..';
|
|
3
3
|
import {
|
|
4
|
+
DynamicGatewayConfig,
|
|
4
5
|
Experimental_DidResolveQueryPlanCallback,
|
|
5
6
|
Experimental_UpdateServiceDefinitions,
|
|
7
|
+
ServiceDefinitionUpdate,
|
|
6
8
|
} from '../../config';
|
|
7
9
|
import {
|
|
8
10
|
product,
|
|
@@ -11,8 +13,11 @@ import {
|
|
|
11
13
|
accounts,
|
|
12
14
|
books,
|
|
13
15
|
documents,
|
|
16
|
+
fixtures,
|
|
17
|
+
fixturesWithUpdate,
|
|
14
18
|
} from 'apollo-federation-integration-testsuite';
|
|
15
19
|
import { Logger } from 'apollo-server-types';
|
|
20
|
+
import resolvable from '@josephg/resolvable';
|
|
16
21
|
|
|
17
22
|
// The order of this was specified to preserve existing test coverage. Typically
|
|
18
23
|
// we would just import and use the `fixtures` array.
|
|
@@ -47,16 +52,14 @@ beforeEach(() => {
|
|
|
47
52
|
|
|
48
53
|
describe('lifecycle hooks', () => {
|
|
49
54
|
it('uses updateServiceDefinitions override', async () => {
|
|
50
|
-
const experimental_updateServiceDefinitions: Experimental_UpdateServiceDefinitions =
|
|
51
|
-
async () => {
|
|
55
|
+
const experimental_updateServiceDefinitions: Experimental_UpdateServiceDefinitions =
|
|
56
|
+
jest.fn(async () => {
|
|
52
57
|
return { serviceDefinitions, isNewSchema: true };
|
|
53
|
-
}
|
|
54
|
-
);
|
|
58
|
+
});
|
|
55
59
|
|
|
56
60
|
const gateway = new ApolloGateway({
|
|
57
61
|
serviceList: serviceDefinitions,
|
|
58
62
|
experimental_updateServiceDefinitions,
|
|
59
|
-
experimental_didUpdateComposition: jest.fn(),
|
|
60
63
|
logger,
|
|
61
64
|
});
|
|
62
65
|
|
|
@@ -67,61 +70,7 @@ describe('lifecycle hooks', () => {
|
|
|
67
70
|
await gateway.stop();
|
|
68
71
|
});
|
|
69
72
|
|
|
70
|
-
it('calls
|
|
71
|
-
const experimental_didFailComposition = jest.fn();
|
|
72
|
-
|
|
73
|
-
// Creating 2 subservices that clearly cannot composed.
|
|
74
|
-
const s1 = {
|
|
75
|
-
name: 'S1',
|
|
76
|
-
url: 'http://S1',
|
|
77
|
-
typeDefs: gql`
|
|
78
|
-
type T {
|
|
79
|
-
a: Int
|
|
80
|
-
}
|
|
81
|
-
`
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
const s2 = {
|
|
85
|
-
name: 'S2',
|
|
86
|
-
url: 'http://S2',
|
|
87
|
-
typeDefs: gql`
|
|
88
|
-
type T {
|
|
89
|
-
a: String
|
|
90
|
-
}
|
|
91
|
-
`
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const gateway = new ApolloGateway({
|
|
96
|
-
async experimental_updateServiceDefinitions() {
|
|
97
|
-
return {
|
|
98
|
-
serviceDefinitions: [s1, s2],
|
|
99
|
-
compositionMetadata: {
|
|
100
|
-
formatVersion: 1,
|
|
101
|
-
id: 'abc',
|
|
102
|
-
implementingServiceLocations: [],
|
|
103
|
-
schemaHash: 'abc',
|
|
104
|
-
},
|
|
105
|
-
isNewSchema: true,
|
|
106
|
-
};
|
|
107
|
-
},
|
|
108
|
-
serviceList: [],
|
|
109
|
-
experimental_didFailComposition,
|
|
110
|
-
logger,
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
await expect(gateway.load()).rejects.toThrowError("A valid schema couldn't be composed");
|
|
114
|
-
|
|
115
|
-
const callbackArgs = experimental_didFailComposition.mock.calls[0][0];
|
|
116
|
-
expect(callbackArgs.serviceList).toHaveLength(2);
|
|
117
|
-
expect(callbackArgs.errors[0]).toMatchInlineSnapshot(
|
|
118
|
-
`[GraphQLError: Field "T.a" has incompatible types accross subgraphs: it has type "Int" in subgraph "S1" but type "String" in subgraph "S2"]`,
|
|
119
|
-
);
|
|
120
|
-
expect(callbackArgs.compositionMetadata.id).toEqual('abc');
|
|
121
|
-
expect(experimental_didFailComposition).toBeCalled();
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it('calls experimental_didUpdateComposition on schema update', async () => {
|
|
73
|
+
it('calls experimental_didUpdateSupergraph on schema update', async () => {
|
|
125
74
|
const compositionMetadata = {
|
|
126
75
|
formatVersion: 1,
|
|
127
76
|
id: 'abc',
|
|
@@ -129,81 +78,73 @@ describe('lifecycle hooks', () => {
|
|
|
129
78
|
schemaHash: 'hash1',
|
|
130
79
|
};
|
|
131
80
|
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
// This is the simplest way I could find to achieve mocked functions that leverage our types
|
|
143
|
-
const mockUpdate = jest.fn(update);
|
|
144
|
-
|
|
145
|
-
// We want to return a different composition across two ticks, so we mock it
|
|
146
|
-
// slightly differenty
|
|
147
|
-
mockUpdate.mockImplementationOnce(async () => {
|
|
148
|
-
const services = serviceDefinitions.filter(s => s.name !== 'books');
|
|
149
|
-
return {
|
|
150
|
-
serviceDefinitions: [
|
|
151
|
-
...services,
|
|
152
|
-
{
|
|
153
|
-
name: 'book',
|
|
154
|
-
typeDefs: books.typeDefs,
|
|
155
|
-
url: 'http://localhost:32542',
|
|
81
|
+
const mockUpdate = jest
|
|
82
|
+
.fn<Promise<ServiceDefinitionUpdate>, [config: DynamicGatewayConfig]>()
|
|
83
|
+
.mockImplementationOnce(async () => {
|
|
84
|
+
return {
|
|
85
|
+
serviceDefinitions: fixtures,
|
|
86
|
+
isNewSchema: true,
|
|
87
|
+
compositionMetadata: {
|
|
88
|
+
...compositionMetadata,
|
|
89
|
+
id: '123',
|
|
90
|
+
schemaHash: 'hash2',
|
|
156
91
|
},
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
92
|
+
};
|
|
93
|
+
})
|
|
94
|
+
// We want to return a different composition across two ticks, so we mock it
|
|
95
|
+
// slightly differently
|
|
96
|
+
.mockImplementationOnce(async () => {
|
|
97
|
+
return {
|
|
98
|
+
serviceDefinitions: fixturesWithUpdate,
|
|
99
|
+
isNewSchema: true,
|
|
100
|
+
compositionMetadata,
|
|
101
|
+
};
|
|
102
|
+
});
|
|
162
103
|
|
|
163
104
|
const mockDidUpdate = jest.fn();
|
|
164
105
|
|
|
165
106
|
const gateway = new ApolloGateway({
|
|
166
107
|
experimental_updateServiceDefinitions: mockUpdate,
|
|
167
|
-
|
|
108
|
+
experimental_didUpdateSupergraph: mockDidUpdate,
|
|
168
109
|
logger,
|
|
169
110
|
});
|
|
170
|
-
//
|
|
171
|
-
gateway
|
|
111
|
+
// for testing purposes, a short pollInterval is ideal so we'll override here
|
|
112
|
+
gateway['pollIntervalInMs'] = 100;
|
|
172
113
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
const schemaChangeBlocker1 = new Promise(res => (resolve1 = res));
|
|
176
|
-
const schemaChangeBlocker2 = new Promise(res => (resolve2 = res));
|
|
114
|
+
const schemaChangeBlocker1 = resolvable();
|
|
115
|
+
const schemaChangeBlocker2 = resolvable();
|
|
177
116
|
|
|
178
|
-
gateway.
|
|
117
|
+
gateway.onSchemaLoadOrUpdate(
|
|
179
118
|
jest
|
|
180
119
|
.fn()
|
|
181
|
-
.mockImplementationOnce(() =>
|
|
182
|
-
.mockImplementationOnce(() =>
|
|
120
|
+
.mockImplementationOnce(() => schemaChangeBlocker1.resolve())
|
|
121
|
+
.mockImplementationOnce(() => schemaChangeBlocker2.resolve()),
|
|
183
122
|
);
|
|
184
123
|
|
|
185
124
|
await gateway.load();
|
|
186
125
|
|
|
187
126
|
await schemaChangeBlocker1;
|
|
127
|
+
|
|
188
128
|
expect(mockUpdate).toBeCalledTimes(1);
|
|
189
129
|
expect(mockDidUpdate).toBeCalledTimes(1);
|
|
190
130
|
|
|
191
131
|
await schemaChangeBlocker2;
|
|
132
|
+
|
|
192
133
|
expect(mockUpdate).toBeCalledTimes(2);
|
|
193
134
|
expect(mockDidUpdate).toBeCalledTimes(2);
|
|
194
135
|
|
|
195
136
|
const [firstCall, secondCall] = mockDidUpdate.mock.calls;
|
|
196
137
|
|
|
197
|
-
|
|
198
|
-
expect(firstCall[0]
|
|
138
|
+
const expectedFirstId = 'd20cfb7a9c51179aa494ed9e98153f0042892bd225437af064bf1c1aa68eab86'
|
|
139
|
+
expect(firstCall[0]!.compositionId).toEqual(expectedFirstId);
|
|
199
140
|
// first call should have no second "previous" argument
|
|
200
141
|
expect(firstCall[1]).toBeUndefined();
|
|
201
142
|
|
|
202
|
-
expect(secondCall[0]
|
|
203
|
-
|
|
143
|
+
expect(secondCall[0]!.compositionId).toEqual(
|
|
144
|
+
'7dad7ab8284165a86241b8973d71f0d6ac8cb142095c717dd23443522850c225',
|
|
145
|
+
);
|
|
204
146
|
// second call should have previous info in the second arg
|
|
205
|
-
expect(secondCall[1]!.
|
|
206
|
-
expect(secondCall[1]!.compositionMetadata!.schemaHash).toEqual('hash1');
|
|
147
|
+
expect(secondCall[1]!.compositionId).toEqual(expectedFirstId);
|
|
207
148
|
|
|
208
149
|
await gateway.stop();
|
|
209
150
|
});
|
|
@@ -228,34 +169,31 @@ describe('lifecycle hooks', () => {
|
|
|
228
169
|
it('warns when polling on the default fetcher', async () => {
|
|
229
170
|
new ApolloGateway({
|
|
230
171
|
serviceList: serviceDefinitions,
|
|
231
|
-
|
|
172
|
+
pollIntervalInMs: 10,
|
|
232
173
|
logger,
|
|
233
174
|
});
|
|
234
|
-
expect(logger.warn).toHaveBeenCalledTimes(1);
|
|
235
175
|
expect(logger.warn).toHaveBeenCalledWith(
|
|
236
176
|
'Polling running services is dangerous and not recommended in production. Polling should only be used against a registry. If you are polling running services, use with caution.',
|
|
237
177
|
);
|
|
238
178
|
});
|
|
239
179
|
|
|
240
180
|
it('registers schema change callbacks when experimental_pollInterval is set for unmanaged configs', async () => {
|
|
241
|
-
const experimental_updateServiceDefinitions: Experimental_UpdateServiceDefinitions =
|
|
242
|
-
async (_config) => {
|
|
181
|
+
const experimental_updateServiceDefinitions: Experimental_UpdateServiceDefinitions =
|
|
182
|
+
jest.fn(async (_config) => {
|
|
243
183
|
return { serviceDefinitions, isNewSchema: true };
|
|
244
|
-
}
|
|
245
|
-
);
|
|
184
|
+
});
|
|
246
185
|
|
|
247
186
|
const gateway = new ApolloGateway({
|
|
248
187
|
serviceList: [{ name: 'book', url: 'http://localhost:32542' }],
|
|
249
188
|
experimental_updateServiceDefinitions,
|
|
250
|
-
|
|
189
|
+
pollIntervalInMs: 100,
|
|
251
190
|
logger,
|
|
252
191
|
});
|
|
253
192
|
|
|
254
|
-
|
|
255
|
-
const
|
|
256
|
-
const schemaChangeCallback = jest.fn(() => resolve());
|
|
193
|
+
const schemaChangeBlocker = resolvable();
|
|
194
|
+
const schemaChangeCallback = jest.fn(() => schemaChangeBlocker.resolve());
|
|
257
195
|
|
|
258
|
-
gateway.
|
|
196
|
+
gateway.onSchemaLoadOrUpdate(schemaChangeCallback);
|
|
259
197
|
await gateway.load();
|
|
260
198
|
|
|
261
199
|
await schemaChangeBlocker;
|
|
@@ -265,12 +203,11 @@ describe('lifecycle hooks', () => {
|
|
|
265
203
|
});
|
|
266
204
|
|
|
267
205
|
it('calls experimental_didResolveQueryPlan when executor is called', async () => {
|
|
268
|
-
const experimental_didResolveQueryPlan: Experimental_DidResolveQueryPlanCallback =
|
|
206
|
+
const experimental_didResolveQueryPlan: Experimental_DidResolveQueryPlanCallback =
|
|
207
|
+
jest.fn();
|
|
269
208
|
|
|
270
209
|
const gateway = new ApolloGateway({
|
|
271
|
-
localServiceList: [
|
|
272
|
-
books
|
|
273
|
-
],
|
|
210
|
+
localServiceList: [books],
|
|
274
211
|
experimental_didResolveQueryPlan,
|
|
275
212
|
});
|
|
276
213
|
|
|
@@ -280,7 +217,8 @@ describe('lifecycle hooks', () => {
|
|
|
280
217
|
{ book(isbn: "0262510871") { year } }
|
|
281
218
|
`;
|
|
282
219
|
|
|
283
|
-
|
|
220
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
221
|
+
// @ts-ignore
|
|
284
222
|
await executor({
|
|
285
223
|
source,
|
|
286
224
|
document: gql(source),
|
|
@@ -36,11 +36,16 @@ describe('opentelemetry', () => {
|
|
|
36
36
|
describe('with local data', () =>
|
|
37
37
|
{
|
|
38
38
|
async function gateway() {
|
|
39
|
+
const localDataSources = Object.fromEntries(
|
|
40
|
+
fixtures.map((f) => [
|
|
41
|
+
f.name,
|
|
42
|
+
new LocalGraphQLDataSource(buildSubgraphSchema(f)),
|
|
43
|
+
]),
|
|
44
|
+
);
|
|
39
45
|
const gateway = new ApolloGateway({
|
|
40
46
|
localServiceList: fixtures,
|
|
41
|
-
buildService
|
|
42
|
-
|
|
43
|
-
return new LocalGraphQLDataSource(buildSubgraphSchema([service]));
|
|
47
|
+
buildService(service) {
|
|
48
|
+
return localDataSources[service.name];
|
|
44
49
|
},
|
|
45
50
|
});
|
|
46
51
|
|
|
@@ -6,14 +6,20 @@ import { LocalGraphQLDataSource } from '../../datasources/LocalGraphQLDataSource
|
|
|
6
6
|
import { ApolloGateway } from '../../';
|
|
7
7
|
import { fixtures } from 'apollo-federation-integration-testsuite';
|
|
8
8
|
import { QueryPlanner } from '@apollo/query-planner';
|
|
9
|
+
|
|
9
10
|
it('caches the query plan for a request', async () => {
|
|
10
11
|
const buildQueryPlanSpy = jest.spyOn(QueryPlanner.prototype, 'buildQueryPlan');
|
|
11
12
|
|
|
13
|
+
const localDataSources = Object.fromEntries(
|
|
14
|
+
fixtures.map((f) => [
|
|
15
|
+
f.name,
|
|
16
|
+
new LocalGraphQLDataSource(buildSubgraphSchema(f)),
|
|
17
|
+
]),
|
|
18
|
+
);
|
|
12
19
|
const gateway = new ApolloGateway({
|
|
13
20
|
localServiceList: fixtures,
|
|
14
|
-
buildService
|
|
15
|
-
|
|
16
|
-
return new LocalGraphQLDataSource(buildSubgraphSchema([service]));
|
|
21
|
+
buildService(service) {
|
|
22
|
+
return localDataSources[service.name];
|
|
17
23
|
},
|
|
18
24
|
});
|
|
19
25
|
|
|
@@ -65,11 +71,17 @@ it('supports multiple operations and operationName', async () => {
|
|
|
65
71
|
}
|
|
66
72
|
`;
|
|
67
73
|
|
|
74
|
+
const localDataSources = Object.fromEntries(
|
|
75
|
+
fixtures.map((f) => [
|
|
76
|
+
f.name,
|
|
77
|
+
new LocalGraphQLDataSource(buildSubgraphSchema(f)),
|
|
78
|
+
]),
|
|
79
|
+
);
|
|
80
|
+
|
|
68
81
|
const gateway = new ApolloGateway({
|
|
69
82
|
localServiceList: fixtures,
|
|
70
|
-
buildService
|
|
71
|
-
|
|
72
|
-
return new LocalGraphQLDataSource(buildSubgraphSchema([service]));
|
|
83
|
+
buildService(service) {
|
|
84
|
+
return localDataSources[service.name];
|
|
73
85
|
},
|
|
74
86
|
});
|
|
75
87
|
|
|
@@ -170,11 +182,15 @@ it('does not corrupt cached queryplan data across requests', async () => {
|
|
|
170
182
|
},
|
|
171
183
|
};
|
|
172
184
|
|
|
185
|
+
const dataSources: Record<string, LocalGraphQLDataSource> = {
|
|
186
|
+
a: new LocalGraphQLDataSource(buildSubgraphSchema(serviceA)),
|
|
187
|
+
b: new LocalGraphQLDataSource(buildSubgraphSchema(serviceB)),
|
|
188
|
+
};
|
|
189
|
+
|
|
173
190
|
const gateway = new ApolloGateway({
|
|
174
191
|
localServiceList: [serviceA, serviceB],
|
|
175
|
-
buildService
|
|
176
|
-
|
|
177
|
-
return new LocalGraphQLDataSource(buildSubgraphSchema([service]));
|
|
192
|
+
buildService(service) {
|
|
193
|
+
return dataSources[service.name];
|
|
178
194
|
},
|
|
179
195
|
});
|
|
180
196
|
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import { gunzipSync } from 'zlib';
|
|
2
2
|
import nock from 'nock';
|
|
3
|
-
import { GraphQLSchemaModule } from 'apollo-graphql';
|
|
4
3
|
import gql from 'graphql-tag';
|
|
5
4
|
import { buildSubgraphSchema } from '@apollo/subgraph';
|
|
6
5
|
import { ApolloServer } from 'apollo-server';
|
|
7
6
|
import { ApolloServerPluginUsageReporting } from 'apollo-server-core';
|
|
8
|
-
import { execute
|
|
9
|
-
import {
|
|
7
|
+
import { execute } from '@apollo/client/link/core';
|
|
8
|
+
import { toPromise } from '@apollo/client/link/utils';
|
|
9
|
+
import { createHttpLink } from '@apollo/client/link/http';
|
|
10
10
|
import fetch from 'node-fetch';
|
|
11
11
|
import { ApolloGateway } from '../..';
|
|
12
12
|
import { Plugin, Config, Refs } from 'pretty-format';
|
|
13
13
|
import { Report, Trace } from 'apollo-reporting-protobuf';
|
|
14
14
|
import { fixtures } from 'apollo-federation-integration-testsuite';
|
|
15
|
+
import { nockAfterEach, nockBeforeEach } from '../nockAssertions';
|
|
16
|
+
import { GraphQLSchemaModule } from '../../schema-helper';
|
|
17
|
+
import resolvable, { Resolvable } from '@josephg/resolvable';
|
|
15
18
|
|
|
16
19
|
// Normalize specific fields that change often (eg timestamps) to static values,
|
|
17
20
|
// to make snapshot testing viable. (If these helpers are more generally
|
|
@@ -88,19 +91,16 @@ describe('reporting', () => {
|
|
|
88
91
|
let backendServers: ApolloServer[];
|
|
89
92
|
let gatewayServer: ApolloServer;
|
|
90
93
|
let gatewayUrl: string;
|
|
91
|
-
let reportPromise:
|
|
92
|
-
let nockScope: nock.Scope;
|
|
94
|
+
let reportPromise: Resolvable<any>;
|
|
93
95
|
|
|
94
96
|
beforeEach(async () => {
|
|
95
|
-
|
|
96
|
-
reportPromise = new Promise<any>((resolve) => {
|
|
97
|
-
reportResolver = resolve;
|
|
98
|
-
});
|
|
97
|
+
reportPromise = resolvable();
|
|
99
98
|
|
|
100
|
-
|
|
99
|
+
nockBeforeEach();
|
|
100
|
+
nock('https://usage-reporting.api.apollographql.com')
|
|
101
101
|
.post('/api/ingress/traces')
|
|
102
102
|
.reply(200, (_: any, requestBody: string) => {
|
|
103
|
-
|
|
103
|
+
reportPromise.resolve(requestBody);
|
|
104
104
|
return 'ok';
|
|
105
105
|
});
|
|
106
106
|
|
|
@@ -137,7 +137,8 @@ describe('reporting', () => {
|
|
|
137
137
|
if (gatewayServer) {
|
|
138
138
|
await gatewayServer.stop();
|
|
139
139
|
}
|
|
140
|
-
|
|
140
|
+
|
|
141
|
+
nockAfterEach();
|
|
141
142
|
});
|
|
142
143
|
|
|
143
144
|
it(`queries three services`, async () => {
|
|
@@ -231,6 +232,34 @@ describe('reporting', () => {
|
|
|
231
232
|
"tracesPerQuery": Object {
|
|
232
233
|
"# -
|
|
233
234
|
{me{name{first last}}topProducts{name}}": Object {
|
|
235
|
+
"referencedFieldsByType": Object {
|
|
236
|
+
"Name": Object {
|
|
237
|
+
"fieldNames": Array [
|
|
238
|
+
"first",
|
|
239
|
+
"last",
|
|
240
|
+
],
|
|
241
|
+
"isInterface": false,
|
|
242
|
+
},
|
|
243
|
+
"Product": Object {
|
|
244
|
+
"fieldNames": Array [
|
|
245
|
+
"name",
|
|
246
|
+
],
|
|
247
|
+
"isInterface": true,
|
|
248
|
+
},
|
|
249
|
+
"Query": Object {
|
|
250
|
+
"fieldNames": Array [
|
|
251
|
+
"me",
|
|
252
|
+
"topProducts",
|
|
253
|
+
],
|
|
254
|
+
"isInterface": false,
|
|
255
|
+
},
|
|
256
|
+
"User": Object {
|
|
257
|
+
"fieldNames": Array [
|
|
258
|
+
"name",
|
|
259
|
+
],
|
|
260
|
+
"isInterface": false,
|
|
261
|
+
},
|
|
262
|
+
},
|
|
234
263
|
"trace": Array [
|
|
235
264
|
Object {
|
|
236
265
|
"cachePolicy": Object {
|
|
@@ -238,7 +267,6 @@ describe('reporting', () => {
|
|
|
238
267
|
"scope": "PRIVATE",
|
|
239
268
|
},
|
|
240
269
|
"clientName": "",
|
|
241
|
-
"clientReferenceId": "",
|
|
242
270
|
"clientVersion": "",
|
|
243
271
|
"details": Object {},
|
|
244
272
|
"durationNs": 12345,
|
|
@@ -246,6 +274,7 @@ describe('reporting', () => {
|
|
|
246
274
|
"nanos": 123000000,
|
|
247
275
|
"seconds": "1562203363",
|
|
248
276
|
},
|
|
277
|
+
"fieldExecutionWeight": 1,
|
|
249
278
|
"forbiddenOperation": false,
|
|
250
279
|
"fullQueryCacheHit": false,
|
|
251
280
|
"http": Object {
|