@apollo/gateway 2.0.0-alpha.1 → 2.0.0-alpha.2

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.
Files changed (61) hide show
  1. package/README.md +1 -1
  2. package/dist/__generated__/graphqlTypes.d.ts +13 -11
  3. package/dist/__generated__/graphqlTypes.d.ts.map +1 -1
  4. package/dist/__generated__/graphqlTypes.js.map +1 -1
  5. package/dist/config.d.ts +2 -9
  6. package/dist/config.d.ts.map +1 -1
  7. package/dist/config.js +6 -17
  8. package/dist/config.js.map +1 -1
  9. package/dist/datasources/types.d.ts +1 -1
  10. package/dist/datasources/types.d.ts.map +1 -1
  11. package/dist/executeQueryPlan.d.ts.map +1 -1
  12. package/dist/executeQueryPlan.js +4 -4
  13. package/dist/executeQueryPlan.js.map +1 -1
  14. package/dist/index.d.ts +1 -2
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +14 -28
  17. package/dist/index.js.map +1 -1
  18. package/dist/loadSupergraphSdlFromStorage.d.ts +4 -3
  19. package/dist/loadSupergraphSdlFromStorage.d.ts.map +1 -1
  20. package/dist/loadSupergraphSdlFromStorage.js +7 -3
  21. package/dist/loadSupergraphSdlFromStorage.js.map +1 -1
  22. package/dist/operationContext.js +0 -1
  23. package/dist/operationContext.js.map +1 -1
  24. package/dist/utilities/array.js +1 -1
  25. package/dist/utilities/array.js.map +1 -1
  26. package/package.json +8 -9
  27. package/src/__generated__/graphqlTypes.ts +13 -11
  28. package/src/__tests__/buildQueryPlan.test.ts +1 -1
  29. package/src/__tests__/executeQueryPlan.test.ts +572 -76
  30. package/src/__tests__/execution-utils.ts +2 -4
  31. package/src/__tests__/gateway/composedSdl.test.ts +1 -1
  32. package/src/__tests__/gateway/executor.test.ts +2 -0
  33. package/src/__tests__/gateway/lifecycle-hooks.test.ts +8 -4
  34. package/src/__tests__/gateway/opentelemetry.test.ts +1 -0
  35. package/src/__tests__/gateway/queryPlanCache.test.ts +3 -0
  36. package/src/__tests__/integration/aliases.test.ts +1 -0
  37. package/src/__tests__/integration/configuration.test.ts +3 -21
  38. package/src/__tests__/integration/logger.test.ts +1 -1
  39. package/src/__tests__/integration/networkRequests.test.ts +53 -28
  40. package/src/__tests__/integration/nockMocks.ts +33 -5
  41. package/src/__tests__/loadServicesFromRemoteEndpoint.test.ts +2 -2
  42. package/src/__tests__/loadSupergraphSdlFromStorage.test.ts +36 -6
  43. package/src/config.ts +8 -43
  44. package/src/core/__tests__/core.test.ts +6 -6
  45. package/src/datasources/types.ts +1 -1
  46. package/src/executeQueryPlan.ts +4 -7
  47. package/src/index.ts +22 -50
  48. package/src/loadServicesFromRemoteEndpoint.ts +1 -1
  49. package/src/loadSupergraphSdlFromStorage.ts +7 -4
  50. package/src/make-fetch-happen.d.ts +1 -1
  51. package/src/operationContext.ts +2 -2
  52. package/src/utilities/__tests__/cleanErrorOfInaccessibleElements.test.ts +1 -1
  53. package/src/utilities/array.ts +1 -1
  54. package/CHANGELOG.md +0 -452
  55. package/dist/legacyLoadServicesFromStorage.d.ts +0 -20
  56. package/dist/legacyLoadServicesFromStorage.d.ts.map +0 -1
  57. package/dist/legacyLoadServicesFromStorage.js +0 -62
  58. package/dist/legacyLoadServicesFromStorage.js.map +0 -1
  59. package/src/__tests__/integration/legacyNetworkRequests.test.ts +0 -279
  60. package/src/__tests__/integration/legacyNockMocks.ts +0 -113
  61. package/src/legacyLoadServicesFromStorage.ts +0 -170
@@ -1,279 +0,0 @@
1
- import nock from 'nock';
2
- import { fetch } from 'apollo-server-env';
3
- import { Logger } from 'apollo-server-types';
4
- import { ApolloGateway, GCS_RETRY_COUNT, getDefaultFetcher } from '../..';
5
- import {
6
- mockServiceHealthCheckSuccess,
7
- mockServiceHealthCheck,
8
- mockStorageSecretSuccess,
9
- mockStorageSecret,
10
- mockCompositionConfigLinkSuccess,
11
- mockCompositionConfigLink,
12
- mockCompositionConfigsSuccess,
13
- mockCompositionConfigs,
14
- mockImplementingServicesSuccess,
15
- mockImplementingServices,
16
- mockRawPartialSchemaSuccess,
17
- mockRawPartialSchema,
18
- apiKeyHash,
19
- graphId,
20
- } from './legacyNockMocks';
21
-
22
- export interface MockService {
23
- gcsDefinitionPath: string;
24
- partialSchemaPath: string;
25
- url: string;
26
- sdl: string;
27
- }
28
-
29
- const service: MockService = {
30
- gcsDefinitionPath: 'service-definition.json',
31
- partialSchemaPath: 'accounts-partial-schema.json',
32
- url: 'http://localhost:4001',
33
- sdl: `#graphql
34
- extend type Query {
35
- me: User
36
- everyone: [User]
37
- }
38
- "This is my User"
39
- type User @key(fields: "id") {
40
- id: ID!
41
- name: String
42
- username: String
43
- }
44
- `,
45
- };
46
-
47
- const updatedService: MockService = {
48
- gcsDefinitionPath: 'updated-service-definition.json',
49
- partialSchemaPath: 'updated-accounts-partial-schema.json',
50
- url: 'http://localhost:4002',
51
- sdl: `#graphql
52
- extend type Query {
53
- me: User
54
- everyone: [User]
55
- }
56
- "This is my updated User"
57
- type User @key(fields: "id") {
58
- id: ID!
59
- name: String
60
- username: String
61
- }
62
- `,
63
- };
64
-
65
- let fetcher: typeof fetch;
66
- let logger: Logger;
67
- let gateway: ApolloGateway | null = null;
68
-
69
- beforeEach(() => {
70
- if (!nock.isActive()) nock.activate();
71
-
72
- fetcher = getDefaultFetcher().defaults({
73
- retry: {
74
- retries: GCS_RETRY_COUNT,
75
- minTimeout: 0,
76
- maxTimeout: 0,
77
- },
78
- });
79
-
80
- const warn = jest.fn();
81
- const debug = jest.fn();
82
- const error = jest.fn();
83
- const info = jest.fn();
84
-
85
- logger = {
86
- warn,
87
- debug,
88
- error,
89
- info,
90
- };
91
- });
92
-
93
- afterEach(async () => {
94
- expect(nock.isDone()).toBeTruthy();
95
- nock.cleanAll();
96
- nock.restore();
97
- if (gateway) {
98
- await gateway.stop();
99
- gateway = null;
100
- }
101
- });
102
-
103
- it('Extracts service definitions from remote storage', async () => {
104
- mockStorageSecretSuccess();
105
- mockCompositionConfigLinkSuccess();
106
- mockCompositionConfigsSuccess([service]);
107
- mockImplementingServicesSuccess(service);
108
- mockRawPartialSchemaSuccess(service);
109
-
110
- gateway = new ApolloGateway({ logger, schemaConfigDeliveryEndpoint: null });
111
-
112
- await gateway.load({
113
- apollo: { keyHash: apiKeyHash, graphId, graphVariant: 'current' },
114
- });
115
- expect(gateway.schema!.getType('User')!.description).toBe('This is my User');
116
- });
117
-
118
- function failNTimes(n: number, fn: () => nock.Interceptor) {
119
- for (let i = 0; i < n; i++) {
120
- fn().reply(500);
121
- }
122
- }
123
-
124
- it(`Retries GCS (up to ${GCS_RETRY_COUNT} times) on failure for each request and succeeds`, async () => {
125
- failNTimes(GCS_RETRY_COUNT, mockStorageSecret);
126
- mockStorageSecretSuccess();
127
-
128
- failNTimes(GCS_RETRY_COUNT, mockCompositionConfigLink);
129
- mockCompositionConfigLinkSuccess();
130
-
131
- failNTimes(GCS_RETRY_COUNT, mockCompositionConfigs);
132
- mockCompositionConfigsSuccess([service]);
133
-
134
- failNTimes(GCS_RETRY_COUNT, () => mockImplementingServices(service));
135
- mockImplementingServicesSuccess(service);
136
-
137
- failNTimes(GCS_RETRY_COUNT, () => mockRawPartialSchema(service));
138
- mockRawPartialSchemaSuccess(service);
139
-
140
- gateway = new ApolloGateway({
141
- fetcher,
142
- logger,
143
- schemaConfigDeliveryEndpoint: null,
144
- });
145
-
146
- await gateway.load({
147
- apollo: { keyHash: apiKeyHash, graphId, graphVariant: 'current' },
148
- });
149
- expect(gateway.schema!.getType('User')!.description).toBe('This is my User');
150
- });
151
-
152
- describe('Managed mode', () => {
153
- it('Performs health checks to downstream services on load', async () => {
154
- mockStorageSecretSuccess();
155
- mockCompositionConfigLinkSuccess();
156
- mockCompositionConfigsSuccess([service]);
157
- mockImplementingServicesSuccess(service);
158
- mockRawPartialSchemaSuccess(service);
159
-
160
- mockServiceHealthCheckSuccess(service);
161
-
162
- gateway = new ApolloGateway({
163
- serviceHealthCheck: true,
164
- logger,
165
- schemaConfigDeliveryEndpoint: null,
166
- });
167
-
168
- await gateway.load({
169
- apollo: { keyHash: apiKeyHash, graphId, graphVariant: 'current' },
170
- });
171
- expect(gateway.schema!.getType('User')!.description).toBe(
172
- 'This is my User',
173
- );
174
- });
175
-
176
- it('Rejects on initial load when health check fails', async () => {
177
- mockStorageSecretSuccess();
178
- mockCompositionConfigLinkSuccess();
179
- mockCompositionConfigsSuccess([service]);
180
- mockImplementingServicesSuccess(service);
181
- mockRawPartialSchemaSuccess(service);
182
-
183
- mockServiceHealthCheck(service).reply(500);
184
-
185
- const gateway = new ApolloGateway({
186
- serviceHealthCheck: true,
187
- logger,
188
- schemaConfigDeliveryEndpoint: null,
189
- });
190
-
191
- await expect(
192
- gateway.load({
193
- apollo: { keyHash: apiKeyHash, graphId, graphVariant: 'current' },
194
- }),
195
- ).rejects.toThrowErrorMatchingInlineSnapshot(`
196
- "The gateway did not update its schema due to failed service health checks. The gateway will continue to operate with the previous schema and reattempt updates. The following error occurred during the health check:
197
- [accounts]: 500: Internal Server Error"
198
- `);
199
- });
200
-
201
- it('Preserves original schema when health check fails', async () => {
202
- mockStorageSecretSuccess();
203
- mockCompositionConfigLinkSuccess();
204
- mockCompositionConfigsSuccess([service]);
205
- mockImplementingServicesSuccess(service);
206
- mockRawPartialSchemaSuccess(service);
207
- mockServiceHealthCheckSuccess(service);
208
-
209
- // Update
210
- mockStorageSecretSuccess();
211
- mockCompositionConfigLinkSuccess();
212
- mockCompositionConfigsSuccess([updatedService]);
213
- mockImplementingServicesSuccess(updatedService);
214
- mockRawPartialSchemaSuccess(updatedService);
215
- mockServiceHealthCheck(updatedService).reply(500);
216
-
217
- let resolve: () => void;
218
- const schemaChangeBlocker = new Promise<void>((res) => (resolve = res));
219
-
220
- gateway = new ApolloGateway({
221
- serviceHealthCheck: true,
222
- logger,
223
- schemaConfigDeliveryEndpoint: null,
224
- });
225
- // @ts-ignore for testing purposes, a short pollInterval is ideal so we'll override here
226
- gateway.experimental_pollInterval = 100;
227
-
228
- // @ts-ignore for testing purposes, we'll call the original `updateSchema`
229
- // function from our mock. The first call should mimic original behavior,
230
- // but the second call needs to handle the PromiseRejection. Typically for tests
231
- // like these we would leverage the `gateway.onSchemaChange` callback to drive
232
- // the test, but in this case, that callback isn't triggered when the update
233
- // fails (as expected) so we get creative with the second mock as seen below.
234
- const original = gateway.updateSchema;
235
- const mockUpdateSchema = jest
236
- .fn()
237
- .mockImplementationOnce(async () => {
238
- await original.apply(gateway);
239
- })
240
- .mockImplementationOnce(async () => {
241
- // mock the first poll and handle the error which would otherwise be caught
242
- // and logged from within the `pollServices` class method
243
- try {
244
- await original.apply(gateway);
245
- } catch (e) {
246
- var err = e;
247
- }
248
-
249
- expect(err.message).toMatchInlineSnapshot(`
250
- "The gateway did not update its schema due to failed service health checks. The gateway will continue to operate with the previous schema and reattempt updates. The following error occurred during the health check:
251
- [accounts]: 500: Internal Server Error"
252
- `);
253
- // finally resolve the promise which drives this test
254
- resolve();
255
- });
256
-
257
- // @ts-ignore for testing purposes, replace the `updateSchema`
258
- // function on the gateway with our mock
259
- gateway.updateSchema = mockUpdateSchema;
260
-
261
- // load the gateway as usual
262
- await gateway.load({
263
- apollo: { keyHash: apiKeyHash, graphId, graphVariant: 'current' },
264
- });
265
-
266
- expect(gateway.schema!.getType('User')!.description).toBe(
267
- 'This is my User',
268
- );
269
-
270
- await schemaChangeBlocker;
271
-
272
- // At this point, the mock update should have been called but the schema
273
- // should not have updated to the new one.
274
- expect(mockUpdateSchema.mock.calls.length).toBe(2);
275
- expect(gateway.schema!.getType('User')!.description).toBe(
276
- 'This is my User',
277
- );
278
- });
279
- });
@@ -1,113 +0,0 @@
1
- import { HEALTH_CHECK_QUERY, SERVICE_DEFINITION_QUERY } from '@apollo/gateway';
2
- import nock from 'nock';
3
- import { MockService } from './legacyNetworkRequests.test';
4
-
5
- export const graphId = 'federated-service';
6
- export const apiKeyHash = 'dd55a79d467976346d229a7b12b673ce';
7
- const storageSecret = 'my-storage-secret';
8
- const accountsService = 'accounts';
9
-
10
- // Service mocks
11
- function mockSdlQuery({ url }: MockService) {
12
- return nock(url).post('/', {
13
- query: SERVICE_DEFINITION_QUERY,
14
- });
15
- }
16
-
17
- export function mockSdlQuerySuccess(service: MockService) {
18
- mockSdlQuery(service).reply(200, {
19
- data: { _service: { sdl: service.sdl } },
20
- });
21
- }
22
-
23
- export function mockServiceHealthCheck({ url }: MockService) {
24
- return nock(url).post('/', {
25
- query: HEALTH_CHECK_QUERY,
26
- });
27
- }
28
-
29
- export function mockServiceHealthCheckSuccess(service: MockService) {
30
- return mockServiceHealthCheck(service).reply(200, {
31
- data: { __typename: 'Query' },
32
- });
33
- }
34
-
35
- // GCS mocks
36
- function gcsNock(url: Parameters<typeof nock>[0]): nock.Scope {
37
- const { name, version } = require('../../../package.json');
38
- return nock(url, {
39
- reqheaders: {
40
- 'apollographql-client-name': name,
41
- 'apollographql-client-version': version,
42
- 'user-agent': `${name}/${version}`,
43
- 'content-type': 'application/json',
44
- },
45
- });
46
- }
47
-
48
- export function mockStorageSecret() {
49
- return gcsNock('https://storage-secrets.api.apollographql.com:443').get(
50
- `/${graphId}/storage-secret/${apiKeyHash}.json`,
51
- );
52
- }
53
-
54
- export function mockStorageSecretSuccess() {
55
- return gcsNock('https://storage-secrets.api.apollographql.com:443')
56
- .get(`/${graphId}/storage-secret/${apiKeyHash}.json`)
57
- .reply(200, `"${storageSecret}"`);
58
- }
59
-
60
- // get composition config link, using received storage secret
61
- export function mockCompositionConfigLink() {
62
- return gcsNock('https://federation.api.apollographql.com:443').get(
63
- `/${storageSecret}/current/v1/composition-config-link`,
64
- );
65
- }
66
-
67
- export function mockCompositionConfigLinkSuccess() {
68
- return mockCompositionConfigLink().reply(200, {
69
- configPath: `${storageSecret}/current/v1/composition-configs/composition-config-path.json`,
70
- });
71
- }
72
-
73
- // get composition configs, using received composition config link
74
- export function mockCompositionConfigs() {
75
- return gcsNock('https://federation.api.apollographql.com:443').get(
76
- `/${storageSecret}/current/v1/composition-configs/composition-config-path.json`,
77
- );
78
- }
79
-
80
- export function mockCompositionConfigsSuccess(services: MockService[]) {
81
- return mockCompositionConfigs().reply(200, {
82
- implementingServiceLocations: services.map((service) => ({
83
- name: accountsService,
84
- path: `${storageSecret}/current/v1/implementing-services/${accountsService}/${service.gcsDefinitionPath}`,
85
- })),
86
- });
87
- }
88
-
89
- // get implementing service reference, using received composition-config
90
- export function mockImplementingServices({ gcsDefinitionPath }: MockService) {
91
- return gcsNock('https://federation.api.apollographql.com:443').get(
92
- `/${storageSecret}/current/v1/implementing-services/${accountsService}/${gcsDefinitionPath}`,
93
- );
94
- }
95
-
96
- export function mockImplementingServicesSuccess(service: MockService) {
97
- return mockImplementingServices(service).reply(200, {
98
- name: accountsService,
99
- partialSchemaPath: `${storageSecret}/current/raw-partial-schemas/${service.partialSchemaPath}`,
100
- url: service.url,
101
- });
102
- }
103
-
104
- // get raw-partial-schema, using received composition-config
105
- export function mockRawPartialSchema({ partialSchemaPath }: MockService) {
106
- return gcsNock('https://federation.api.apollographql.com:443').get(
107
- `/${storageSecret}/current/raw-partial-schemas/${partialSchemaPath}`,
108
- );
109
- }
110
-
111
- export function mockRawPartialSchemaSuccess(service: MockService) {
112
- return mockRawPartialSchema(service).reply(200, service.sdl);
113
- }
@@ -1,170 +0,0 @@
1
- import { fetch } from 'apollo-server-env';
2
- import { parse } from 'graphql';
3
- import { ServiceDefinitionUpdate } from './config';
4
-
5
- interface LinkFileResult {
6
- configPath: string;
7
- formatVersion: number;
8
- }
9
-
10
- interface ImplementingService {
11
- formatVersion: number;
12
- graphID: string;
13
- graphVariant: string;
14
- name: string;
15
- revision: string;
16
- url: string;
17
- partialSchemaPath: string;
18
- }
19
-
20
- interface ImplementingServiceLocation {
21
- name: string;
22
- path: string;
23
- }
24
-
25
- export interface CompositionMetadata {
26
- formatVersion: number;
27
- id: string;
28
- implementingServiceLocations: ImplementingServiceLocation[];
29
- schemaHash: string;
30
- }
31
-
32
- const envOverridePartialSchemaBaseUrl = 'APOLLO_PARTIAL_SCHEMA_BASE_URL';
33
- const envOverrideStorageSecretBaseUrl = 'APOLLO_STORAGE_SECRET_BASE_URL';
34
-
35
- const urlFromEnvOrDefault = (envKey: string, fallback: string) =>
36
- (process.env[envKey] || fallback).replace(/\/$/, '');
37
-
38
- // Generate and cache our desired operation manifest URL.
39
- const urlPartialSchemaBase = urlFromEnvOrDefault(
40
- envOverridePartialSchemaBaseUrl,
41
- 'https://federation.api.apollographql.com/',
42
- );
43
-
44
- const urlStorageSecretBase: string = urlFromEnvOrDefault(
45
- envOverrideStorageSecretBaseUrl,
46
- 'https://storage-secrets.api.apollographql.com/',
47
- );
48
-
49
- function getStorageSecretUrl(graphId: string, apiKeyHash: string): string {
50
- return `${urlStorageSecretBase}/${graphId}/storage-secret/${apiKeyHash}.json`;
51
- }
52
-
53
- function fetchApolloGcs(
54
- fetcher: typeof fetch,
55
- ...args: Parameters<typeof fetch>
56
- ): ReturnType<typeof fetch> {
57
- const [input, init] = args;
58
-
59
- // Used in logging.
60
- const url = (typeof input === 'object' && input.url) || input;
61
-
62
- return fetcher(input, init)
63
- .catch((fetchError) => {
64
- throw new Error('Cannot access Apollo storage: ' + fetchError);
65
- })
66
- .then(async (response) => {
67
- // If the fetcher has a cache and has implemented ETag validation, then
68
- // a 304 response may be returned. Either way, we will return the
69
- // non-JSON-parsed version and let the caller decide if that's important
70
- // to their needs.
71
- if (response.ok || response.status === 304) {
72
- return response;
73
- }
74
-
75
- // We won't make any assumptions that the body is anything but text, to
76
- // avoid parsing errors in this unknown condition.
77
- const body = await response.text();
78
-
79
- // Google Cloud Storage returns an `application/xml` error under error
80
- // conditions. We'll special-case our known errors, and resort to
81
- // printing the body for others.
82
- if (response.status === 403 && body.includes('AccessDenied')) {
83
- throw new Error(
84
- 'Unable to authenticate with Apollo storage ' +
85
- 'while fetching ' +
86
- url +
87
- '. Ensure that the API key is ' +
88
- 'configured properly and that a federated service has been ' +
89
- 'pushed. For details, see ' +
90
- 'https://go.apollo.dev/g/resolve-access-denied.',
91
- );
92
- }
93
-
94
- // Normally, we'll try to keep the logs clean with errors we expect.
95
- // If it's not a known error, reveal the full body for debugging.
96
- throw new Error('Could not communicate with Apollo storage: ' + body);
97
- });
98
- }
99
-
100
- export async function getServiceDefinitionsFromStorage({
101
- graphRef,
102
- apiKeyHash,
103
- federationVersion,
104
- fetcher,
105
- }: {
106
- graphRef: string;
107
- apiKeyHash: string;
108
- federationVersion: number;
109
- fetcher: typeof fetch;
110
- }): Promise<ServiceDefinitionUpdate> {
111
- // The protocol for talking to GCS requires us to split the graph ref
112
- // into ID and variant; sigh.
113
- const at = graphRef.indexOf('@');
114
- const graphId = at === -1 ? graphRef : graphRef.substring(0, at);
115
- const graphVariant = at === -1 ? 'current' : graphRef.substring(at + 1);
116
-
117
- // fetch the storage secret
118
- const storageSecretUrl = getStorageSecretUrl(graphId, apiKeyHash);
119
-
120
- // The storage secret is a JSON string (e.g. `"secret"`).
121
- const secret: string = await fetchApolloGcs(
122
- fetcher,
123
- storageSecretUrl,
124
- ).then((res) => res.json());
125
-
126
- const baseUrl = `${urlPartialSchemaBase}/${secret}/${graphVariant}/v${federationVersion}`;
127
-
128
- const compositionConfigResponse = await fetchApolloGcs(
129
- fetcher,
130
- `${baseUrl}/composition-config-link`,
131
- );
132
-
133
- if (compositionConfigResponse.status === 304) {
134
- return { isNewSchema: false };
135
- }
136
-
137
- const linkFileResult: LinkFileResult = await compositionConfigResponse.json();
138
-
139
- const compositionMetadata: CompositionMetadata = await fetchApolloGcs(
140
- fetcher,
141
- `${urlPartialSchemaBase}/${linkFileResult.configPath}`,
142
- ).then((res) => res.json());
143
-
144
- // It's important to maintain the original order here
145
- const serviceDefinitions = await Promise.all(
146
- compositionMetadata.implementingServiceLocations.map(
147
- async ({ name, path }) => {
148
- const { url, partialSchemaPath }: ImplementingService = await fetcher(
149
- `${urlPartialSchemaBase}/${path}`,
150
- ).then((response) => response.json());
151
-
152
- const sdl = await fetcher(
153
- `${urlPartialSchemaBase}/${partialSchemaPath}`,
154
- ).then((response) => response.text());
155
-
156
- return { name, url, typeDefs: parse(sdl) };
157
- },
158
- ),
159
- );
160
-
161
- // explicity return that this is a new schema, as the link file has changed.
162
- // we can't use the hit property of the fetchPartialSchemaFiles, as the partial
163
- // schema may all be cache hits with the final schema still being new
164
- // (for instance if a partial schema is removed or a partial schema is rolled back to a prior version, which is still in cache)
165
- return {
166
- serviceDefinitions,
167
- compositionMetadata,
168
- isNewSchema: true,
169
- };
170
- }