From 06c3267d27f72d2cbf071bdbfc98886a0250fb06 Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Thu, 20 Feb 2020 20:34:46 +0200 Subject: [PATCH 01/27] Adjust deceiving error message about previous schemas and falling back. This message was being issued when a new server starts up, prior to ever having a schema, when a storage secret (or any other artifact) can't be fetched from GCS (or any error within `updateServiceDefinitions`) despite the fact that there may be no `previousSchema`. While I could use `previousSchema` to change the error message, I don't think that it's this methods concern to decide what happens in the event of an error. I think it's only this methods concern to actually do the update, if it is in fact successful. --- packages/apollo-gateway/src/index.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/apollo-gateway/src/index.ts b/packages/apollo-gateway/src/index.ts index a34db0a362d..10f14e250d6 100644 --- a/packages/apollo-gateway/src/index.ts +++ b/packages/apollo-gateway/src/index.ts @@ -305,10 +305,7 @@ export class ApolloGateway implements GraphQLService { try { result = await this.updateServiceDefinitions(this.config); } catch (e) { - this.logger.warn( - 'Error checking for schema updates. Falling back to existing schema.', - e, - ); + this.logger.warn('Error checking for schema updates.', e); return; } From 1655f4ba23040d5d574184cb68c1b2d0bad74628 Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Thu, 20 Feb 2020 21:15:03 +0200 Subject: [PATCH 02/27] Throw when updating the schema doesn't fail as expected. Currently, we log the error and just return without throwing which causes `load` (one of two places where `updateComposition` is called to not actually fail and follow-up logic then suggests "Gateway successfully loaded schema", even though that cannot be true. The nice thing about this change (in addition to allowing someone to `catch` an error from `ApolloGateway.prototype.load`) is that this should bubble all the way up to the place where we currently call `load` within Apollo Server's constructor, and stop the server from starting in this failure condition: https://github.com/apollographql/apollo-server/blob/7dcee80ff33061c0911458d593ebbca5a9c73939/packages/apollo-server-core/src/ApolloServer.ts#L366 The other place we call `updateComposition` is within a `setTimeout`. That particular case is less troublesome since it will retry on the next interval. --- packages/apollo-gateway/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/apollo-gateway/src/index.ts b/packages/apollo-gateway/src/index.ts index 10f14e250d6..41b3732c7a6 100644 --- a/packages/apollo-gateway/src/index.ts +++ b/packages/apollo-gateway/src/index.ts @@ -306,7 +306,7 @@ export class ApolloGateway implements GraphQLService { result = await this.updateServiceDefinitions(this.config); } catch (e) { this.logger.warn('Error checking for schema updates.', e); - return; + throw e; } if ( From 827cba28a2aace2d82fd58e40e9319f6fa175ebc Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Thu, 20 Feb 2020 21:27:04 +0200 Subject: [PATCH 03/27] Adjust error message wording to be more concise. --- packages/apollo-gateway/src/loadServicesFromRemoteEndpoint.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/apollo-gateway/src/loadServicesFromRemoteEndpoint.ts b/packages/apollo-gateway/src/loadServicesFromRemoteEndpoint.ts index a11840c7911..829716ff8c4 100644 --- a/packages/apollo-gateway/src/loadServicesFromRemoteEndpoint.ts +++ b/packages/apollo-gateway/src/loadServicesFromRemoteEndpoint.ts @@ -29,7 +29,8 @@ export async function getServiceDefinitionsFromRemoteEndpoint({ const serviceDefinitions: ServiceDefinition[] = (await Promise.all( serviceList.map(({ name, url, dataSource }) => { if (!url) { - throw new Error(`Tried to load schema from ${name} but no url found`); + throw new Error( + `Tried to load schema for '${name}' but no 'url' was specified.`); } const request: GraphQLRequest = { From 7250bfed0fc969abd0337581e41630454a3dc233 Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Thu, 20 Feb 2020 22:13:54 +0200 Subject: [PATCH 04/27] Move "previous" variables closer to where they are used. No need to have them so far away from their usage and to even do these assignments at all when there are already escape routes that exit this code path. --- packages/apollo-gateway/src/index.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/apollo-gateway/src/index.ts b/packages/apollo-gateway/src/index.ts index 41b3732c7a6..2c6a446ee97 100644 --- a/packages/apollo-gateway/src/index.ts +++ b/packages/apollo-gateway/src/index.ts @@ -296,10 +296,6 @@ export class ApolloGateway implements GraphQLService { this.engineConfig = options.engine; } - const previousSchema = this.schema; - const previousServiceDefinitions = this.serviceDefinitions; - const previousCompositionMetadata = this.compositionMetadata; - let result: Await>; this.logger.debug('Loading configuration for gateway'); try { @@ -318,6 +314,10 @@ export class ApolloGateway implements GraphQLService { return; } + const previousSchema = this.schema; + const previousServiceDefinitions = this.serviceDefinitions; + const previousCompositionMetadata = this.compositionMetadata; + if (previousSchema) { this.logger.info('Gateway config has changed, updating schema'); } From c1ee91e93fe7aca024eaec90a3863552f34ad77f Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Thu, 20 Feb 2020 22:19:34 +0200 Subject: [PATCH 05/27] Align error messages with consistent terminology and add clarity. * Refer to the thing we are processing consistently as "schema definitions". * Also make them generally more factual. --- packages/apollo-gateway/src/index.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/apollo-gateway/src/index.ts b/packages/apollo-gateway/src/index.ts index 2c6a446ee97..eb238f22afc 100644 --- a/packages/apollo-gateway/src/index.ts +++ b/packages/apollo-gateway/src/index.ts @@ -297,11 +297,11 @@ export class ApolloGateway implements GraphQLService { } let result: Await>; - this.logger.debug('Loading configuration for gateway'); + this.logger.debug('Checking service definitions...'); try { result = await this.updateServiceDefinitions(this.config); } catch (e) { - this.logger.warn('Error checking for schema updates.', e); + this.logger.warn('Error checking for changes to service defintions:', e); throw e; } @@ -310,7 +310,7 @@ export class ApolloGateway implements GraphQLService { JSON.stringify(this.serviceDefinitions) === JSON.stringify(result.serviceDefinitions) ) { - this.logger.debug('No change in service definitions since last check'); + this.logger.debug('No change in service definitions since last check.'); return; } @@ -319,7 +319,7 @@ export class ApolloGateway implements GraphQLService { const previousCompositionMetadata = this.compositionMetadata; if (previousSchema) { - this.logger.info('Gateway config has changed, updating schema'); + this.logger.info("New service definitions were found."); } this.compositionMetadata = result.compositionMetadata; @@ -332,9 +332,8 @@ export class ApolloGateway implements GraphQLService { this.onSchemaChangeListeners.forEach(listener => listener(this.schema!)); } catch (e) { this.logger.error( - 'Error notifying schema change listener of update to schema.', - e, - ); + "An error was thrown from an 'onSchemaChange' listener. " + + "The schema will still update: ", e); } if (this.experimental_didUpdateComposition) { From c3fb481105ffeae657463011d03ee479a29b4e46 Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Fri, 21 Feb 2020 16:06:02 +0200 Subject: [PATCH 06/27] logging: Raise severity from `warn` to `error` for what is certainly an error. --- packages/apollo-gateway/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/apollo-gateway/src/index.ts b/packages/apollo-gateway/src/index.ts index eb238f22afc..d29af62bfff 100644 --- a/packages/apollo-gateway/src/index.ts +++ b/packages/apollo-gateway/src/index.ts @@ -301,7 +301,7 @@ export class ApolloGateway implements GraphQLService { try { result = await this.updateServiceDefinitions(this.config); } catch (e) { - this.logger.warn('Error checking for changes to service defintions:', e); + this.logger.error("Error checking for changes to service definitions", e); throw e; } From e1d97d33e29143ea60576be90468c652c3b81815 Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Mon, 24 Feb 2020 22:09:37 +0200 Subject: [PATCH 07/27] Switch to using single argument logger usage and serialization. Rely on `message` only if it's present, and fall back to serialization methods which might exist on the prototype otherwise (e.g. `toJSON`, `toString`). Also, switch to a single parameter usage of the logging facility. While `console.log` supports it, its certainly possible that the logger will _not_, and will need that positional parameter for something else. --- packages/apollo-gateway/src/index.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/apollo-gateway/src/index.ts b/packages/apollo-gateway/src/index.ts index d29af62bfff..cf4626028ac 100644 --- a/packages/apollo-gateway/src/index.ts +++ b/packages/apollo-gateway/src/index.ts @@ -301,7 +301,10 @@ export class ApolloGateway implements GraphQLService { try { result = await this.updateServiceDefinitions(this.config); } catch (e) { - this.logger.error("Error checking for changes to service definitions", e); + this.logger.error( + "Error checking for changes to service definitions: " + + (e && e.message || e) + ); throw e; } From da06fa8d453c2f939fc9a3de86db48100dab1554 Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Mon, 24 Feb 2020 22:23:14 +0200 Subject: [PATCH 08/27] gateway: Trap unhandled promise rejections in polling. While successful `updateComposition` invocations were working properly, failed invocations (including GCS access errors, network hiccups and just general configuration mistakes) were currently cluttering the logs with warnings of unhandled promise rejections. While those unhandled rejections did include the actual error messages, this adjusts them to be caught and logged in a structured manner with our `logger`, sparing our logs from those unnecessarily verbose (and scary!) messages. --- packages/apollo-gateway/src/index.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/apollo-gateway/src/index.ts b/packages/apollo-gateway/src/index.ts index cf4626028ac..cfd7e033017 100644 --- a/packages/apollo-gateway/src/index.ts +++ b/packages/apollo-gateway/src/index.ts @@ -414,8 +414,12 @@ export class ApolloGateway implements GraphQLService { private startPollingServices() { if (this.pollingTimer) clearInterval(this.pollingTimer); - this.pollingTimer = setInterval(() => { - this.updateComposition(); + this.pollingTimer = setInterval(async () => { + try { + await this.updateComposition(); + } catch (err) { + this.logger.error(err && err.message || err); + } }, this.experimental_pollInterval || 10000); // Prevent the Node.js event loop from remaining active (and preventing, From 88b87ea4257425eb0566e434cc66ce8c02c17c4a Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Mon, 24 Feb 2020 22:27:27 +0200 Subject: [PATCH 09/27] Remove incorrect comment about typings being an appropriate assertion. Adjusting the typings only works for users of TypeScript. On the other hand, this is a one-time assertion which happens at instantiation time which could save a JavaScript developer from the pain of not knowing what's going on! There might be typing improvements to be had, but claiming it as an alternate approach to handling this isn't correct. The typings can still be improved, of course. --- packages/apollo-server-core/src/ApolloServer.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/apollo-server-core/src/ApolloServer.ts b/packages/apollo-server-core/src/ApolloServer.ts index 6c21d422fc1..96bd943069a 100644 --- a/packages/apollo-server-core/src/ApolloServer.ts +++ b/packages/apollo-server-core/src/ApolloServer.ts @@ -186,7 +186,6 @@ export class ApolloServerBase { } = config; if (gateway && (modules || schema || typeDefs || resolvers)) { - // TODO: this could be handled by adjusting the typings to keep gateway configs and non-gateway configs seprate. throw new Error( 'Cannot define both `gateway` and any of: `modules`, `schema`, `typeDefs`, or `resolvers`', ); From 846e59ef01dfb26e2a56db9afb7fad90b1d0f3d2 Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Mon, 24 Feb 2020 22:30:34 +0200 Subject: [PATCH 10/27] typings: Remove `any` type! --- packages/apollo-server-core/src/ApolloServer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/apollo-server-core/src/ApolloServer.ts b/packages/apollo-server-core/src/ApolloServer.ts index 96bd943069a..5f3e5b0e1ea 100644 --- a/packages/apollo-server-core/src/ApolloServer.ts +++ b/packages/apollo-server-core/src/ApolloServer.ts @@ -422,7 +422,7 @@ export class ApolloServerBase { }); } - let constructedSchema; + let constructedSchema: GraphQLSchema; if (schema) { constructedSchema = schema; } else if (modules) { From ee4c9acf14df3b5985d6230fdecaabfc327b0b2c Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Mon, 24 Feb 2020 23:59:33 +0200 Subject: [PATCH 11/27] Use the same fetch response handling for the waterfall of GCS requests. Overall, the success/failure behavior should be expected to be similar for all of these requests, as they all access the same GCS store. --- .../src/loadServicesFromStorage.ts | 67 ++++++++++++++++--- 1 file changed, 59 insertions(+), 8 deletions(-) diff --git a/packages/apollo-gateway/src/loadServicesFromStorage.ts b/packages/apollo-gateway/src/loadServicesFromStorage.ts index 89d8f5bf260..48a294e8a28 100644 --- a/packages/apollo-gateway/src/loadServicesFromStorage.ts +++ b/packages/apollo-gateway/src/loadServicesFromStorage.ts @@ -50,6 +50,55 @@ function getStorageSecretUrl(graphId: string, apiKeyHash: string): string { return `${urlStorageSecretBase}/${graphId}/storage-secret/${apiKeyHash}.json`; } +function fetchApolloGcs( + fetcher: typeof fetch, + ...args: Parameters +): ReturnType { + const [input, init] = args; + + // Used in logging. + const url = typeof input === 'object' && input.url || input; + + return fetcher(input, init) + .catch(fetchError => { + throw new Error( + "Cannot access Apollo Graph Manager storage: " + fetchError) + }) + .then(async (response) => { + // If the fetcher has a cache and has implemented ETag validation, then + // a 304 response may be returned. Either way, we will return the + // non-JSON-parsed version and let the caller decide if that's important + // to their needs. + if (response.ok || response.status === 304) { + return response; + } + + // We won't make any assumptions that the body is anything but text, to + // avoid parsing errors in this unknown condition. + const body = await response.text(); + + // Google Cloud Storage returns an `application/xml` error under error + // conditions. We'll special-case our known errors, and resort to + // printing the body for others. + if (response.headers.get('content-type') === 'application/xml') { + if ( + response.status === 403 && + body.includes("AccessDenied") && + body.includes("Anonymous caller does not have storage.objects.get") + ) { + throw new Error( + "Unable to authenticate with Apollo Graph Manager storage " + + "while fetching " + url); + } + } + + // Normally, we'll try to keep the logs clean with errors we expect. + // If it's not a known error, reveal the full body for debugging. + throw new Error( + "Could not communicate with Apollo Graph Manager storage: " + body); + }); +}; + export async function getServiceDefinitionsFromStorage({ graphId, apiKeyHash, @@ -66,9 +115,9 @@ export async function getServiceDefinitionsFromStorage({ // fetch the storage secret const storageSecretUrl = getStorageSecretUrl(graphId, apiKeyHash); - const secret: string = await fetcher(storageSecretUrl).then(response => - response.json(), - ); + // The storage secret is a JSON string (e.g. `"secret"`). + const secret: string = + await fetchApolloGcs(fetcher, storageSecretUrl).then(res => res.json()); if (!graphVariant) { graphVariant = 'current'; @@ -76,17 +125,19 @@ export async function getServiceDefinitionsFromStorage({ const baseUrl = `${urlPartialSchemaBase}/${secret}/${graphVariant}/v${federationVersion}`; - const response = await fetcher(`${baseUrl}/composition-config-link`); + const compositionConfigResponse = + await fetchApolloGcs(fetcher, `${baseUrl}/composition-config-link`); - if (response.status === 304) { + if (compositionConfigResponse.status === 304) { return { isNewSchema: false }; } - const linkFileResult: LinkFileResult = await response.json(); + const linkFileResult: LinkFileResult = await compositionConfigResponse.json(); - const compositionMetadata: CompositionMetadata = await fetcher( + const compositionMetadata: CompositionMetadata = await fetchApolloGcs( + fetcher, `${urlPartialSchemaBase}/${linkFileResult.configPath}`, - ).then(response => response.json()); + ).then(res => res.json()); // It's important to maintain the original order here const serviceDefinitions = await Promise.all( From d8c682b3039aa3200fecb1f1d6bcce48829817de Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Tue, 25 Feb 2020 00:02:18 +0200 Subject: [PATCH 12/27] Do not prefix potentially the same error object on every request. Sometimes, the error object which is being caught here is in fact not a misconfiguration of server options, but rather an error which was thrown in a promise chain. By appending the "Invalid options provided to ApolloServer" string to the error object's `message` property in this method that is called on _every_ request to the server (`runHttpQuery`), we're risking appending the same prefix to the same error over and over (i.e. "a: a: a: a: a: b"). While this prefix may have been true at one point, it is no longer possible to enforce in a world where the schema is obtained asynchronously. --- packages/apollo-server-core/src/ApolloServer.ts | 9 +-------- packages/apollo-server-core/src/runHttpQuery.ts | 4 ---- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/packages/apollo-server-core/src/ApolloServer.ts b/packages/apollo-server-core/src/ApolloServer.ts index 5f3e5b0e1ea..bc49d591033 100644 --- a/packages/apollo-server-core/src/ApolloServer.ts +++ b/packages/apollo-server-core/src/ApolloServer.ts @@ -788,14 +788,7 @@ export class ApolloServerBase { } public async executeOperation(request: GraphQLRequest) { - let options; - - try { - options = await this.graphQLServerOptions(); - } catch (e) { - e.message = `Invalid options provided to ApolloServer: ${e.message}`; - throw new Error(e); - } + const options = await this.graphQLServerOptions(); if (typeof options.context === 'function') { options.context = (options.context as () => never)(); diff --git a/packages/apollo-server-core/src/runHttpQuery.ts b/packages/apollo-server-core/src/runHttpQuery.ts index 633c50daacd..c9d2d3ed0df 100644 --- a/packages/apollo-server-core/src/runHttpQuery.ts +++ b/packages/apollo-server-core/src/runHttpQuery.ts @@ -127,10 +127,6 @@ export async function runHttpQuery( // the normal options provided by the user, such as: formatError, // debug. Therefore, we need to do some unnatural things, such // as use NODE_ENV to determine the debug settings - e.message = `Invalid options provided to ApolloServer: ${e.message}`; - if (!debugDefault) { - e.warning = `To remove the stacktrace, set the NODE_ENV environment variable to production if the options creation can fail`; - } return throwHttpGraphQLError(500, [e], { debug: debugDefault }); } if (options.debug === undefined) { From c473f8896e6bcb7a26920c0691032c6a8748f0dc Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Thu, 27 Feb 2020 00:02:33 +0200 Subject: [PATCH 13/27] Ensure the `executor` is set in gateway mode after failed `load` recoveries. Previously, if the initial call to `load` failed, but its intention (fetching a federated schema configuration in managed federation) is eventually accomplished via a subsequent fetch (via setInterval polling), the `executor` would not be set. This resulted in a continued failure even if the `schema` was eventually set since federated `schema`s require the Gateway's `executor` to do pull off their much more complex (remote!) execution strategy! The solution was simple since `executor` was already present on the actual `ApolloGateway`, but that required exposing that property as a valid type to access from the interface that `ApolloGateway` implements: `GraphQLService`. I don't see why a `GraphQLService` wouldn't have an executor, so it seemed appropriate to add, particularly since our only `GraphQLService` is the `ApolloGateway` class itself. --- .../apollo-server-core/src/ApolloServer.ts | 12 ++++++++---- packages/apollo-server-core/src/types.ts | 14 +++++++++++++- .../src/ApolloServer.ts | 18 +++++++++++------- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/packages/apollo-server-core/src/ApolloServer.ts b/packages/apollo-server-core/src/ApolloServer.ts index bc49d591033..da4b31b0eec 100644 --- a/packages/apollo-server-core/src/ApolloServer.ts +++ b/packages/apollo-server-core/src/ApolloServer.ts @@ -416,10 +416,14 @@ export class ApolloServerBase { } : undefined; - return gateway.load({ engine: engineConfig }).then(config => { - this.requestOptions.executor = config.executor; - return config.schema; - }); + // Set the executor whether the gateway 'load' call succeeds or not. + // If the schema becomes available eventually (after a setInterval retry) + // this executor will still be necessary in order to be able to support + // a federated schema! + this.requestOptions.executor = gateway.executor; + + return gateway.load({ engine: engineConfig }) + .then(config => config.schema); } let constructedSchema: GraphQLSchema; diff --git a/packages/apollo-server-core/src/types.ts b/packages/apollo-server-core/src/types.ts index 6eeb844a07f..ea23bd0a906 100644 --- a/packages/apollo-server-core/src/types.ts +++ b/packages/apollo-server-core/src/types.ts @@ -5,7 +5,13 @@ import { IMocks, GraphQLParseOptions, } from 'graphql-tools'; -import { ValueOrPromise, GraphQLExecutor } from 'apollo-server-types'; +import { + ValueOrPromise, + GraphQLExecutor, + GraphQLExecutionResult, + WithRequired, + GraphQLRequestContext, +} from 'apollo-server-types'; import { ConnectionContext } from 'subscriptions-transport-ws'; // The types for `ws` use `export = WebSocket`, so we'll use the // matching `import =` to bring in its sole export. @@ -87,6 +93,12 @@ export interface GraphQLService { engine?: GraphQLServiceEngineConfig; }): Promise; onSchemaChange(callback: SchemaChangeCallback): Unsubscriber; + executor( + requestContext: WithRequired< + GraphQLRequestContext, + 'document' | 'queryHash' | 'operationName' | 'operation' + >, + ): ValueOrPromise; } // This configuration is shared between all integrations and should include diff --git a/packages/apollo-server-integration-testsuite/src/ApolloServer.ts b/packages/apollo-server-integration-testsuite/src/ApolloServer.ts index 05219c78d8b..7e5ba8fcbae 100644 --- a/packages/apollo-server-integration-testsuite/src/ApolloServer.ts +++ b/packages/apollo-server-integration-testsuite/src/ApolloServer.ts @@ -116,9 +116,11 @@ const schema = new GraphQLSchema({ const makeGatewayMock = ({ optionsSpy = _options => {}, unsubscribeSpy = () => {}, + executor = () => ({}), }: { optionsSpy?: (_options: any) => void; unsubscribeSpy?: () => void; + executor?: GraphQLExecutor; } = {}) => { const eventuallyAssigned = { resolveLoad: null as ({ schema, executor }) => void, @@ -134,6 +136,7 @@ const makeGatewayMock = ({ }); const mockedGateway: GraphQLService = { + executor, load: options => { optionsSpy(options); return mockedLoadResults; @@ -354,13 +357,13 @@ export function testApolloServer( }); it("accepts a gateway's schema and calls its executor", async () => { - const { gateway, triggers } = makeGatewayMock(); - const executor = jest.fn(); executor.mockReturnValue( Promise.resolve({ data: { testString: 'hi - but federated!' } }), ); + const { gateway, triggers } = makeGatewayMock({ executor }); + triggers.resolveLoad({ schema, executor }); const { url: uri } = await createApolloServer({ @@ -2833,13 +2836,13 @@ export function testApolloServer( }), }); - const { gateway, triggers } = makeGatewayMock(); - const executor = req => (req.source as string).match(/1/) ? Promise.resolve({ data: { testString1: 'hello' } }) : Promise.resolve({ data: { testString2: 'aloha' } }); + const { gateway, triggers } = makeGatewayMock({ executor }); + triggers.resolveLoad({ schema: makeQueryTypeWithField('testString1'), executor, @@ -2895,7 +2898,6 @@ export function testApolloServer( }); it('waits until gateway has resolved a schema to respond to queries', async () => { - const { gateway, triggers } = makeGatewayMock(); const wait = ms => new Promise(resolve => setTimeout(resolve, ms)); let resolveExecutor; const executor = () => @@ -2905,6 +2907,8 @@ export function testApolloServer( }; }); + const { gateway, triggers } = makeGatewayMock({ executor }); + triggers.resolveLoad({ schema, executor }); const { url: uri } = await createApolloServer({ gateway, @@ -2941,8 +2945,6 @@ export function testApolloServer( }), }); - const { gateway, triggers } = makeGatewayMock(); - const makeEventuallyResolvingPromise = val => { let resolver; const promise = new Promise( @@ -2968,6 +2970,8 @@ export function testApolloServer( ? p2 : p3; + const { gateway, triggers } = makeGatewayMock({ executor }); + triggers.resolveLoad({ schema: makeQueryTypeWithField('testString1'), executor, From a30bee772ed15b05d07c8fc56e4857ad5cd89f1d Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Thu, 27 Feb 2020 00:36:33 +0200 Subject: [PATCH 14/27] Ensure that initial gateway `load` failures trap the downstream error. In particular, this blocks any rejected promises which may come from GCS load prior to returning them to the `schemaDerivedData` promise (which is where the `initSchema` method assigns the result to). Failure to do this results in the server middleware sending the error directly to the client. --- .../apollo-server-core/src/ApolloServer.ts | 12 ++++- .../src/ApolloServer.ts | 47 ++++++++++++++++++- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/packages/apollo-server-core/src/ApolloServer.ts b/packages/apollo-server-core/src/ApolloServer.ts index da4b31b0eec..b4c352256fe 100644 --- a/packages/apollo-server-core/src/ApolloServer.ts +++ b/packages/apollo-server-core/src/ApolloServer.ts @@ -423,7 +423,17 @@ export class ApolloServerBase { this.requestOptions.executor = gateway.executor; return gateway.load({ engine: engineConfig }) - .then(config => config.schema); + .then(config => config.schema) + .catch(err => { + // We intentionally do not re-throw the exact error from the gateway + // configuration as it may contain implementation details and this + // error will propogate to the client. We will, however, log the error + // for observation in the logs. + const message = "This data graph lacks a valid configuration."; + console.error(message + " " + (err && err.message || err)); + throw new Error( + message + " More details may be available in the server logs."); + }); } let constructedSchema: GraphQLSchema; diff --git a/packages/apollo-server-integration-testsuite/src/ApolloServer.ts b/packages/apollo-server-integration-testsuite/src/ApolloServer.ts index 7e5ba8fcbae..d782758448e 100644 --- a/packages/apollo-server-integration-testsuite/src/ApolloServer.ts +++ b/packages/apollo-server-integration-testsuite/src/ApolloServer.ts @@ -124,15 +124,19 @@ const makeGatewayMock = ({ } = {}) => { const eventuallyAssigned = { resolveLoad: null as ({ schema, executor }) => void, + rejectLoad: null as (err: Error) => void, triggerSchemaChange: null as (newSchema) => void, }; const mockedLoadResults = new Promise<{ schema: GraphQLSchema; executor: GraphQLExecutor; - }>(resolve => { + }>((resolve, reject) => { eventuallyAssigned.resolveLoad = ({ schema, executor }) => { resolve({ schema, executor }); }; + eventuallyAssigned.rejectLoad = (err: Error) => { + reject(err); + }; }); const mockedGateway: GraphQLService = { @@ -379,6 +383,47 @@ export function testApolloServer( expect(executor).toHaveBeenCalled(); }); + it("rejected load promise acts as an error boundary", async () => { + const executor = jest.fn(); + executor.mockResolvedValueOnce( + { data: { testString: 'should not get this' } } + ); + + executor.mockRejectedValueOnce( + { errors: [{errorWhichShouldNot: "ever be triggered"}] } + ); + + const consoleErrorSpy = + jest.spyOn(console, 'error').mockImplementation(); + + const { gateway, triggers } = makeGatewayMock({ executor }); + + triggers.rejectLoad(new Error("load error which should be masked")); + + const { url: uri } = await createApolloServer({ + gateway, + subscriptions: false, + }); + + const apolloFetch = createApolloFetch({ uri }); + const result = await apolloFetch({ query: '{testString}' }); + + expect(result.data).toBeUndefined(); + expect(result.errors).toContainEqual( + expect.objectContaining({ + extensions: expect.objectContaining({ + code: "INTERNAL_SERVER_ERROR", + }), + message: "This data graph lacks a valid configuration. " + + "More details may be available in the server logs." + }) + ); + expect(consoleErrorSpy).toHaveBeenCalledWith( + "This data graph lacks a valid configuration. " + + "load error which should be masked"); + expect(executor).not.toHaveBeenCalled(); + }); + it('uses schema over resolvers + typeDefs', async () => { const typeDefs = gql` type Query { From 99229fe1218324973029eba70c84978b08f79897 Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Thu, 27 Feb 2020 00:08:41 +0200 Subject: [PATCH 15/27] Allow other middleware (e.g. Playground) when initial Gateway load fails. Another approach to this would be to throw an error here, but this is our only opportunity to allow the server to recover after initial failure. On the one hand, this approach is more graceful, but on the other hand, perhaps we actually want initial failure (after timeouts elapse) to not bind the other middleware. Either way, the server doesn't just `process.exit` right now, and with certain non-Node.js environments, it may be worthwhile to operate in this mode. --- packages/apollo-server-core/src/ApolloServer.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/apollo-server-core/src/ApolloServer.ts b/packages/apollo-server-core/src/ApolloServer.ts index b4c352256fe..c5bab2dea9e 100644 --- a/packages/apollo-server-core/src/ApolloServer.ts +++ b/packages/apollo-server-core/src/ApolloServer.ts @@ -573,7 +573,20 @@ export class ApolloServerBase { } protected async willStart() { - const { schema, schemaHash } = await this.schemaDerivedData; + try { + var { schema, schemaHash } = await this.schemaDerivedData; + } catch (err) { + // The `schemaDerivedData` can throw if the Promise it points to does not + // resolve with a `GraphQLSchema`. As errors from `willStart` are start-up + // errors, other Apollo middleware after us will not be called, including + // our health check, CORS, etc. + // + // Returning here allows the integration's other Apollo middleware to + // function properly in the event of a failure to obtain the data graph + // configuration from the gateway's `load` method during initialization. + return; + } + const service: GraphQLServiceContext = { schema: schema, schemaHash: schemaHash, From f878c7fbd662d6e7d815a8a7a4658ee4e0cdaea6 Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Thu, 27 Feb 2020 22:54:36 +0200 Subject: [PATCH 16/27] Ensure the `executor` is still set on `ApolloServer` when `load` rejects. Because we want the actual executor to work when a schema might eventually become available, as it may when `onSchemaChange` hooks eventually succeed. --- .../src/__tests__/gateway/executor.test.ts | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/apollo-gateway/src/__tests__/gateway/executor.test.ts b/packages/apollo-gateway/src/__tests__/gateway/executor.test.ts index 9d73387ab14..ef2675ad928 100644 --- a/packages/apollo-gateway/src/__tests__/gateway/executor.test.ts +++ b/packages/apollo-gateway/src/__tests__/gateway/executor.test.ts @@ -6,6 +6,7 @@ import * as books from '../__fixtures__/schemas/books'; import * as inventory from '../__fixtures__/schemas/inventory'; import * as product from '../__fixtures__/schemas/product'; import * as reviews from '../__fixtures__/schemas/reviews'; +import { ApolloServer } from "apollo-server"; describe('ApolloGateway executor', () => { it('validates requests prior to execution', async () => { @@ -35,4 +36,24 @@ describe('ApolloGateway executor', () => { 'Variable "$first" got invalid value "3"; Expected type Int.', ); }); + + it('still sets the ApolloServer executor on load rejection', async () => { + jest.spyOn(console, 'error').mockImplementation(); + + const gateway = new ApolloGateway({ + // Empty service list will throw, which is what we want. + serviceList: [], + }); + + const server = new ApolloServer({ + gateway, + subscriptions: false, + }); + + // Ensure the throw happens to maintain the correctness of this test. + await expect( + server.executeOperation({ query: '{ __typename }' })).rejects.toThrow(); + + expect(server.requestOptions.executor).toStrictEqual(gateway.executor); + }); }); From 4f2fbffe6e2f41a960e958c255055c70d72241f8 Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Thu, 27 Feb 2020 22:55:20 +0200 Subject: [PATCH 17/27] Expect the expected errors, rather than swallowing them. --- .../src/__tests__/gateway/lifecycle-hooks.test.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/apollo-gateway/src/__tests__/gateway/lifecycle-hooks.test.ts b/packages/apollo-gateway/src/__tests__/gateway/lifecycle-hooks.test.ts index 3b3f31dd692..267c15cc984 100644 --- a/packages/apollo-gateway/src/__tests__/gateway/lifecycle-hooks.test.ts +++ b/packages/apollo-gateway/src/__tests__/gateway/lifecycle-hooks.test.ts @@ -56,9 +56,7 @@ describe('lifecycle hooks', () => { experimental_didFailComposition, }); - try { - await gateway.load(); - } catch {} + await expect(gateway.load()).rejects.toThrowError(); const callbackArgs = experimental_didFailComposition.mock.calls[0][0]; expect(callbackArgs.serviceList).toHaveLength(1); From 03038e34d005aa1d7e23b1c8da3728cd5805d4df Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Tue, 3 Mar 2020 12:19:03 -0800 Subject: [PATCH 18/27] Update packages/apollo-gateway/src/__tests__/gateway/executor.test.ts Co-Authored-By: Trevor Scheer --- packages/apollo-gateway/src/__tests__/gateway/executor.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/apollo-gateway/src/__tests__/gateway/executor.test.ts b/packages/apollo-gateway/src/__tests__/gateway/executor.test.ts index ef2675ad928..7e8a7490f64 100644 --- a/packages/apollo-gateway/src/__tests__/gateway/executor.test.ts +++ b/packages/apollo-gateway/src/__tests__/gateway/executor.test.ts @@ -54,6 +54,6 @@ describe('ApolloGateway executor', () => { await expect( server.executeOperation({ query: '{ __typename }' })).rejects.toThrow(); - expect(server.requestOptions.executor).toStrictEqual(gateway.executor); + expect(server.requestOptions.executor).toBe(gateway.executor); }); }); From 4d4ab5b8219449d34e01d854371d141fdfea8b33 Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Tue, 3 Mar 2020 17:07:16 -0800 Subject: [PATCH 19/27] Update packages/apollo-gateway/src/loadServicesFromStorage.ts Co-Authored-By: Trevor Scheer --- .../src/loadServicesFromStorage.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/packages/apollo-gateway/src/loadServicesFromStorage.ts b/packages/apollo-gateway/src/loadServicesFromStorage.ts index 48a294e8a28..e42f1aed2fa 100644 --- a/packages/apollo-gateway/src/loadServicesFromStorage.ts +++ b/packages/apollo-gateway/src/loadServicesFromStorage.ts @@ -80,16 +80,15 @@ function fetchApolloGcs( // Google Cloud Storage returns an `application/xml` error under error // conditions. We'll special-case our known errors, and resort to // printing the body for others. - if (response.headers.get('content-type') === 'application/xml') { - if ( - response.status === 403 && - body.includes("AccessDenied") && - body.includes("Anonymous caller does not have storage.objects.get") - ) { - throw new Error( - "Unable to authenticate with Apollo Graph Manager storage " + - "while fetching " + url); - } + if ( + response.headers.get('content-type') === 'application/xml' && + response.status === 403 && + body.includes("AccessDenied") && + body.includes("Anonymous caller does not have storage.objects.get") + ) { + throw new Error( + Unable to authenticate with Apollo Graph Manager storage " + + "while fetching " + url); } // Normally, we'll try to keep the logs clean with errors we expect. From 0a50943e7e2173b0aa6c9d976e7b7cd5a345d65c Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Wed, 4 Mar 2020 07:41:10 -0800 Subject: [PATCH 20/27] Revert "Update packages/apollo-gateway/src/loadServicesFromStorage.ts" This reverts commit 4d4ab5b8219449d34e01d854371d141fdfea8b33. --- .../src/loadServicesFromStorage.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/apollo-gateway/src/loadServicesFromStorage.ts b/packages/apollo-gateway/src/loadServicesFromStorage.ts index e42f1aed2fa..48a294e8a28 100644 --- a/packages/apollo-gateway/src/loadServicesFromStorage.ts +++ b/packages/apollo-gateway/src/loadServicesFromStorage.ts @@ -80,15 +80,16 @@ function fetchApolloGcs( // Google Cloud Storage returns an `application/xml` error under error // conditions. We'll special-case our known errors, and resort to // printing the body for others. - if ( - response.headers.get('content-type') === 'application/xml' && - response.status === 403 && - body.includes("AccessDenied") && - body.includes("Anonymous caller does not have storage.objects.get") - ) { - throw new Error( - Unable to authenticate with Apollo Graph Manager storage " + - "while fetching " + url); + if (response.headers.get('content-type') === 'application/xml') { + if ( + response.status === 403 && + body.includes("AccessDenied") && + body.includes("Anonymous caller does not have storage.objects.get") + ) { + throw new Error( + "Unable to authenticate with Apollo Graph Manager storage " + + "while fetching " + url); + } } // Normally, we'll try to keep the logs clean with errors we expect. From 554e20c6ff7bf952020de99ab9f352ff9c1a83f9 Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Tue, 3 Mar 2020 17:07:16 -0800 Subject: [PATCH 21/27] Re-apply 4d4ab5b8: apollo-gateway/src/loadServicesFromStorage.ts To correct the syntax error. Co-Authored-By: Trevor Scheer --- .../apollo-gateway/src/loadServicesFromStorage.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/apollo-gateway/src/loadServicesFromStorage.ts b/packages/apollo-gateway/src/loadServicesFromStorage.ts index 48a294e8a28..79ef860b656 100644 --- a/packages/apollo-gateway/src/loadServicesFromStorage.ts +++ b/packages/apollo-gateway/src/loadServicesFromStorage.ts @@ -80,16 +80,15 @@ function fetchApolloGcs( // Google Cloud Storage returns an `application/xml` error under error // conditions. We'll special-case our known errors, and resort to // printing the body for others. - if (response.headers.get('content-type') === 'application/xml') { - if ( - response.status === 403 && - body.includes("AccessDenied") && - body.includes("Anonymous caller does not have storage.objects.get") - ) { + if ( + response.headers.get('content-type') === 'application/xml' && + response.status === 403 && + body.includes("AccessDenied") && + body.includes("Anonymous caller does not have storage.objects.get") + ) { throw new Error( "Unable to authenticate with Apollo Graph Manager storage " + "while fetching " + url); - } } // Normally, we'll try to keep the logs clean with errors we expect. From a75a366d1ad5a0404b96d08b04e0bc1a14808db2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 6 Mar 2020 01:14:20 +0000 Subject: [PATCH 22/27] chore(deps): update dependency gatsby-theme-apollo-docs to v4.0.13 (#3856) Co-authored-by: WhiteSource Renovate --- docs/package-lock.json | 278 +++++++++++++++++++++++++---------------- docs/package.json | 2 +- 2 files changed, 170 insertions(+), 110 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index 085581d9fa8..7ee058e28cb 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -1889,9 +1889,9 @@ "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==" }, "@babel/types": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.6.tgz", - "integrity": "sha512-wqz7pgWMIrht3gquyEFPVXeXCti72Rm8ep9b5tQKz9Yg9LzJA3HxosF1SB3Kc81KD1A3XBkkVYtJvCKS2Z/QrA==", + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", + "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", "requires": { "esutils": "^2.0.2", "lodash": "^4.17.13", @@ -2061,9 +2061,9 @@ } }, "@babel/plugin-transform-typescript": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.8.3.tgz", - "integrity": "sha512-Ebj230AxcrKGZPKIp4g4TdQLrqX95TobLUWKd/CwG7X1XHUH1ZpkpFvXuXqWbtGRWb7uuEWNlrl681wsOArAdQ==", + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.8.7.tgz", + "integrity": "sha512-7O0UsPQVNKqpHeHLpfvOG4uXmlw+MOxYvUv6Otc9uH5SYMIxvF6eBdjkWvC3f9G+VXe0RsNExyAQBeTRug/wqQ==", "requires": { "@babel/helper-create-class-features-plugin": "^7.8.3", "@babel/helper-plugin-utils": "^7.8.3", @@ -2369,9 +2369,9 @@ } }, "@emotion/cache": { - "version": "10.0.27", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.27.tgz", - "integrity": "sha512-Zp8BEpbMunFsTcqAK4D7YTm3MvCp1SekflSLJH8lze2fCcSZ/yMkXHo8kb3t1/1Tdd3hAqf3Fb7z9VZ+FMiC9w==", + "version": "10.0.29", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.29.tgz", + "integrity": "sha512-fU2VtSVlHiF27empSbxi1O2JFdNWZO+2NFHfwO0pxgTep6Xa3uGb+3pVKfLww2l/IBGLNEZl5Xf/++A4wAYDYQ==", "requires": { "@emotion/sheet": "0.9.4", "@emotion/stylis": "0.8.5", @@ -2403,9 +2403,9 @@ } }, "@emotion/hash": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.7.4.tgz", - "integrity": "sha512-fxfMSBMX3tlIbKUdtGKxqB1fyrH6gVrX39Gsv3y8lRYKUqlgDt3UMqQyGnR1bQMa2B8aGnhLZokZgg8vT0Le+A==" + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" }, "@emotion/is-prop-valid": { "version": "0.8.7", @@ -2421,11 +2421,11 @@ "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==" }, "@emotion/serialize": { - "version": "0.11.15", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.11.15.tgz", - "integrity": "sha512-YE+qnrmGwyR+XB5j7Bi+0GT1JWsdcjM/d4POu+TXkcnrRs4RFCCsi3d/Ebf+wSStHqAlTT2+dfd+b9N9EO2KBg==", + "version": "0.11.16", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.11.16.tgz", + "integrity": "sha512-G3J4o8by0VRrO+PFeSc3js2myYNOXVJ3Ya+RGVxnshRYgsvErfAOglKAiy1Eo1vhzxqtUvjCyS5gtewzkmvSSg==", "requires": { - "@emotion/hash": "0.7.4", + "@emotion/hash": "0.8.0", "@emotion/memoize": "0.7.4", "@emotion/unitless": "0.7.5", "@emotion/utils": "0.11.3", @@ -2618,9 +2618,9 @@ } }, "@babel/types": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.6.tgz", - "integrity": "sha512-wqz7pgWMIrht3gquyEFPVXeXCti72Rm8ep9b5tQKz9Yg9LzJA3HxosF1SB3Kc81KD1A3XBkkVYtJvCKS2Z/QrA==", + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", + "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", "requires": { "esutils": "^2.0.2", "lodash": "^4.17.13", @@ -3916,14 +3916,14 @@ } }, "babel-plugin-emotion": { - "version": "10.0.28", - "resolved": "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-10.0.28.tgz", - "integrity": "sha512-h25EMmPxYVNOgsEkGIjCv2Ok+HzW/e/b5lf2v2U17T9k6y6g0ku3TG9b+jy94ZrqMh+b/njRF4uOQrwVr28QfQ==", + "version": "10.0.29", + "resolved": "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-10.0.29.tgz", + "integrity": "sha512-7Jpi1OCxjyz0k163lKtqP+LHMg5z3S6A7vMBfHnF06l2unmtsOmFDzZBpGf0CWo1G4m8UACfVcDJiSiRuu/cSw==", "requires": { "@babel/helper-module-imports": "^7.0.0", - "@emotion/hash": "0.7.4", + "@emotion/hash": "0.8.0", "@emotion/memoize": "0.7.4", - "@emotion/serialize": "^0.11.15", + "@emotion/serialize": "^0.11.16", "babel-plugin-macros": "^2.0.0", "babel-plugin-syntax-jsx": "^6.18.0", "convert-source-map": "^1.5.0", @@ -5205,9 +5205,9 @@ "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=" }, "clipboard": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.4.tgz", - "integrity": "sha512-Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.6.tgz", + "integrity": "sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg==", "optional": true, "requires": { "good-listener": "^1.2.2", @@ -9393,21 +9393,26 @@ } }, "gatsby-plugin-emotion": { - "version": "4.1.22", - "resolved": "https://registry.npmjs.org/gatsby-plugin-emotion/-/gatsby-plugin-emotion-4.1.22.tgz", - "integrity": "sha512-XG9YpkyUgbTHs/Uq7W6tDVDVQ2XHlj9rHPhCYiZHlJTdrJIHhviousHZ8+vEI/h0FQ4oW/Hs0CuX2gi5SlvWSQ==", + "version": "4.1.23", + "resolved": "https://registry.npmjs.org/gatsby-plugin-emotion/-/gatsby-plugin-emotion-4.1.23.tgz", + "integrity": "sha512-SP3hGbyj2Kq42iIS9tDR6aZMvBsbH7GhPizfmr+1L1KxYjFedjd3U/gWa346wJbvtiwnSkeoLZKMUATX4w1VCA==", "requires": { "@babel/runtime": "^7.7.6", "@emotion/babel-preset-css-prop": "^10.0.23" }, "dependencies": { "@babel/runtime": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.4.tgz", - "integrity": "sha512-neAp3zt80trRVBI1x0azq6c57aNBqYZH8KhMm3TaB7wEI5Q4A2SHfBHE8w9gOhI/lrqxtEbXZgQIrHP+wvSGwQ==", + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.7.tgz", + "integrity": "sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg==", "requires": { - "regenerator-runtime": "^0.13.2" + "regenerator-runtime": "^0.13.4" } + }, + "regenerator-runtime": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.4.tgz", + "integrity": "sha512-plpwicqEzfEyTQohIKktWigcLzmNStMGwbOUbykx51/29Z3JOGYldaaNGK7ngNXV+UcoqvIMmloZ48Sr74sd+g==" } } }, @@ -9421,19 +9426,24 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.4.tgz", - "integrity": "sha512-neAp3zt80trRVBI1x0azq6c57aNBqYZH8KhMm3TaB7wEI5Q4A2SHfBHE8w9gOhI/lrqxtEbXZgQIrHP+wvSGwQ==", + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.7.tgz", + "integrity": "sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg==", "requires": { - "regenerator-runtime": "^0.13.2" + "regenerator-runtime": "^0.13.4" } + }, + "regenerator-runtime": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.4.tgz", + "integrity": "sha512-plpwicqEzfEyTQohIKktWigcLzmNStMGwbOUbykx51/29Z3JOGYldaaNGK7ngNXV+UcoqvIMmloZ48Sr74sd+g==" } } }, "gatsby-plugin-mdx": { - "version": "1.0.74", - "resolved": "https://registry.npmjs.org/gatsby-plugin-mdx/-/gatsby-plugin-mdx-1.0.74.tgz", - "integrity": "sha512-mN+68a1qmsNEC6zDDUFCbEUJ2a/1E7S7tlidFUy7doOAMVYkpvfJBpBI3IPoGHiIaOIreo0jj7BRDI6NA9nlxQ==", + "version": "1.0.75", + "resolved": "https://registry.npmjs.org/gatsby-plugin-mdx/-/gatsby-plugin-mdx-1.0.75.tgz", + "integrity": "sha512-CMjAk8EbQh7uufWenUG4yQJ7fa7Jitp8txJmlIwxm4+xgKlrMvmJ9fg5PhDFlCjG8t09mxoJt8dlh8izJZXQwg==", "requires": { "@babel/core": "^7.7.5", "@babel/generator": "^7.7.4", @@ -9473,9 +9483,9 @@ }, "dependencies": { "@babel/types": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.6.tgz", - "integrity": "sha512-wqz7pgWMIrht3gquyEFPVXeXCti72Rm8ep9b5tQKz9Yg9LzJA3HxosF1SB3Kc81KD1A3XBkkVYtJvCKS2Z/QrA==", + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", + "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", "requires": { "esutils": "^2.0.2", "lodash": "^4.17.13", @@ -9556,12 +9566,17 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.4.tgz", - "integrity": "sha512-neAp3zt80trRVBI1x0azq6c57aNBqYZH8KhMm3TaB7wEI5Q4A2SHfBHE8w9gOhI/lrqxtEbXZgQIrHP+wvSGwQ==", + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.7.tgz", + "integrity": "sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg==", "requires": { - "regenerator-runtime": "^0.13.2" + "regenerator-runtime": "^0.13.4" } + }, + "regenerator-runtime": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.4.tgz", + "integrity": "sha512-plpwicqEzfEyTQohIKktWigcLzmNStMGwbOUbykx51/29Z3JOGYldaaNGK7ngNXV+UcoqvIMmloZ48Sr74sd+g==" } } }, @@ -9616,13 +9631,18 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.4.tgz", - "integrity": "sha512-neAp3zt80trRVBI1x0azq6c57aNBqYZH8KhMm3TaB7wEI5Q4A2SHfBHE8w9gOhI/lrqxtEbXZgQIrHP+wvSGwQ==", + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.7.tgz", + "integrity": "sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg==", "requires": { - "regenerator-runtime": "^0.13.2" + "regenerator-runtime": "^0.13.4" } }, + "regenerator-runtime": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.4.tgz", + "integrity": "sha512-plpwicqEzfEyTQohIKktWigcLzmNStMGwbOUbykx51/29Z3JOGYldaaNGK7ngNXV+UcoqvIMmloZ48Sr74sd+g==" + }, "unist-util-visit": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz", @@ -9651,6 +9671,44 @@ } } }, + "gatsby-remark-code-titles": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/gatsby-remark-code-titles/-/gatsby-remark-code-titles-1.1.0.tgz", + "integrity": "sha512-RuNqziXi99eBIj5NJP0TgdzAxzWFL+ArGRb3961Ff9Tto/nCvmyqR1qySaWKXtkOgeqoVUlqAFNUCyEAyNuc8w==", + "requires": { + "query-string": "~6.0.0", + "unist-util-visit": "~1.3.0" + }, + "dependencies": { + "query-string": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.0.0.tgz", + "integrity": "sha1-i485RHtz6CkNb141gXeSGOkXEUI=", + "requires": { + "decode-uri-component": "^0.2.0", + "strict-uri-encode": "^2.0.0" + } + }, + "strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=" + }, + "unist-util-is": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-2.1.3.tgz", + "integrity": "sha512-4WbQX2iwfr/+PfM4U3zd2VNXY+dWtZsN1fLnWEi2QQXA4qyDYAZcDMfXUX0Cu6XZUHHAO9q4nyxxLT4Awk1qUA==" + }, + "unist-util-visit": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.3.1.tgz", + "integrity": "sha512-0fdB9EQJU0tho5tK0VzOJzAQpPv2LyLZ030b10GxuzAWEfvd54mpY7BMjQ1L69k2YNvL+SvxRzH0yUIehOO8aA==", + "requires": { + "unist-util-is": "^2.1.1" + } + } + } + }, "gatsby-remark-copy-linked-files": { "version": "2.1.37", "resolved": "https://registry.npmjs.org/gatsby-remark-copy-linked-files/-/gatsby-remark-copy-linked-files-2.1.37.tgz", @@ -9667,11 +9725,11 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.4.tgz", - "integrity": "sha512-neAp3zt80trRVBI1x0azq6c57aNBqYZH8KhMm3TaB7wEI5Q4A2SHfBHE8w9gOhI/lrqxtEbXZgQIrHP+wvSGwQ==", + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.7.tgz", + "integrity": "sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg==", "requires": { - "regenerator-runtime": "^0.13.2" + "regenerator-runtime": "^0.13.4" } }, "cheerio": { @@ -9709,6 +9767,11 @@ "@types/node": "*" } }, + "regenerator-runtime": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.4.tgz", + "integrity": "sha512-plpwicqEzfEyTQohIKktWigcLzmNStMGwbOUbykx51/29Z3JOGYldaaNGK7ngNXV+UcoqvIMmloZ48Sr74sd+g==" + }, "unist-util-visit": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz", @@ -9740,9 +9803,9 @@ } }, "gatsby-remark-prismjs": { - "version": "3.3.31", - "resolved": "https://registry.npmjs.org/gatsby-remark-prismjs/-/gatsby-remark-prismjs-3.3.31.tgz", - "integrity": "sha512-n6tczCq/w5LazZ5yk9UXu/6YjyLR7p1rQbBxqgkOL1xEFRmQcK5BwFhcpmCh5OKiqWBvqLDJq561UIFL0jcI/A==", + "version": "3.3.32", + "resolved": "https://registry.npmjs.org/gatsby-remark-prismjs/-/gatsby-remark-prismjs-3.3.32.tgz", + "integrity": "sha512-n/9VLOs5xNOgGQj4m1//PVmvQLEgbmLPqQo5/Hmuw4b+x76KFHfZGVrvwUHpSB0/yCrv6UCykOFI5J8ZxPXjkg==", "requires": { "@babel/runtime": "^7.7.6", "parse-numeric-range": "^0.0.2", @@ -9750,31 +9813,18 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.4.tgz", - "integrity": "sha512-neAp3zt80trRVBI1x0azq6c57aNBqYZH8KhMm3TaB7wEI5Q4A2SHfBHE8w9gOhI/lrqxtEbXZgQIrHP+wvSGwQ==", + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.7.tgz", + "integrity": "sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg==", "requires": { - "regenerator-runtime": "^0.13.2" + "regenerator-runtime": "^0.13.4" } }, - "unist-util-visit": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz", - "integrity": "sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==", - "requires": { - "unist-util-visit-parents": "^2.0.0" - } - } - } - }, - "gatsby-remark-prismjs-title": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gatsby-remark-prismjs-title/-/gatsby-remark-prismjs-title-1.0.0.tgz", - "integrity": "sha512-VKAw7LGAbzyDlztUfhOri+jDTjLyOPCJCNgkdt2+61+SP8M9wYzzma8NvVyzHP7J9hx0jcYq8F50XQH5dE42ow==", - "requires": { - "unist-util-visit": "~1.4.0" - }, - "dependencies": { + "regenerator-runtime": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.4.tgz", + "integrity": "sha512-plpwicqEzfEyTQohIKktWigcLzmNStMGwbOUbykx51/29Z3JOGYldaaNGK7ngNXV+UcoqvIMmloZ48Sr74sd+g==" + }, "unist-util-visit": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz", @@ -9816,17 +9866,22 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.4.tgz", - "integrity": "sha512-neAp3zt80trRVBI1x0azq6c57aNBqYZH8KhMm3TaB7wEI5Q4A2SHfBHE8w9gOhI/lrqxtEbXZgQIrHP+wvSGwQ==", + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.7.tgz", + "integrity": "sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg==", "requires": { - "regenerator-runtime": "^0.13.2" + "regenerator-runtime": "^0.13.4" } }, "bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "regenerator-runtime": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.4.tgz", + "integrity": "sha512-plpwicqEzfEyTQohIKktWigcLzmNStMGwbOUbykx51/29Z3JOGYldaaNGK7ngNXV+UcoqvIMmloZ48Sr74sd+g==" } } }, @@ -9981,9 +10036,9 @@ } }, "gatsby-theme-apollo-core": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/gatsby-theme-apollo-core/-/gatsby-theme-apollo-core-3.0.9.tgz", - "integrity": "sha512-P6Y+uB25VdX72cfsjRb6r1aJYAG2Wnqrgq/goXbaADliikfoJIV41Ld5v1IdH15KFGD0AMHdNFt9y93Q90uiYg==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/gatsby-theme-apollo-core/-/gatsby-theme-apollo-core-3.0.10.tgz", + "integrity": "sha512-vXh1Yu1H2mdDCtuoc6UVGhXLwxXzN4YmWgnweEW+e1ZQYH1WKYQhMM/XjuUQUxXz0aI34y7IZeL3rvXUiDhh9Q==", "requires": { "@apollo/space-kit": "2.15.0", "@emotion/core": "^10.0.7", @@ -10002,9 +10057,9 @@ } }, "gatsby-theme-apollo-docs": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/gatsby-theme-apollo-docs/-/gatsby-theme-apollo-docs-4.0.10.tgz", - "integrity": "sha512-w0kc1c1JI5JuxwyE3AhGEUwftGoaPXhFXeWFWp+QezvHs8YENzpI0pByQrC4Y6iqU09GuTuOktlc/Hi9pObcdQ==", + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/gatsby-theme-apollo-docs/-/gatsby-theme-apollo-docs-4.0.13.tgz", + "integrity": "sha512-584b21cJmmT/7WyEoSKgxLRJUtRsLrapQWThxRUN8+pQMn4rv+jzAfkOwwNf1l7ahbKRWrA1Gnb/hF0bmaOuAw==", "requires": { "@mdx-js/mdx": "^1.1.0", "@mdx-js/react": "^1.0.27", @@ -10013,14 +10068,14 @@ "gatsby-plugin-segment-js": "^3.0.1", "gatsby-remark-autolink-headers": "^2.0.16", "gatsby-remark-check-links": "^2.1.0", + "gatsby-remark-code-titles": "^1.1.0", "gatsby-remark-copy-linked-files": "^2.0.12", "gatsby-remark-mermaid": "^1.2.0", "gatsby-remark-prismjs": "^3.2.8", - "gatsby-remark-prismjs-title": "^1.0.0", "gatsby-remark-rewrite-relative-links": "^1.0.7", "gatsby-source-filesystem": "^2.0.29", "gatsby-source-git": "^1.0.1", - "gatsby-theme-apollo-core": "^3.0.9", + "gatsby-theme-apollo-core": "^3.0.10", "gatsby-transformer-remark": "^2.6.30", "js-yaml": "^3.13.1", "prismjs": "^1.15.0", @@ -10063,11 +10118,11 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.4.tgz", - "integrity": "sha512-neAp3zt80trRVBI1x0azq6c57aNBqYZH8KhMm3TaB7wEI5Q4A2SHfBHE8w9gOhI/lrqxtEbXZgQIrHP+wvSGwQ==", + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.7.tgz", + "integrity": "sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg==", "requires": { - "regenerator-runtime": "^0.13.2" + "regenerator-runtime": "^0.13.4" } }, "bluebird": { @@ -10204,6 +10259,11 @@ "xtend": "^4.0.1" } }, + "regenerator-runtime": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.4.tgz", + "integrity": "sha512-plpwicqEzfEyTQohIKktWigcLzmNStMGwbOUbykx51/29Z3JOGYldaaNGK7ngNXV+UcoqvIMmloZ48Sr74sd+g==" + }, "remark-parse": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-6.0.3.tgz", @@ -15671,9 +15731,9 @@ } }, "proxy-from-env": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", - "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, "prr": { "version": "1.0.1", @@ -16754,9 +16814,9 @@ } }, "@babel/types": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.6.tgz", - "integrity": "sha512-wqz7pgWMIrht3gquyEFPVXeXCti72Rm8ep9b5tQKz9Yg9LzJA3HxosF1SB3Kc81KD1A3XBkkVYtJvCKS2Z/QrA==", + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", + "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", "requires": { "esutils": "^2.0.2", "lodash": "^4.17.13", @@ -19872,9 +19932,9 @@ } }, "vfile": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.0.2.tgz", - "integrity": "sha512-yhoTU5cDMSsaeaMfJ5g0bUKYkYmZhAh9fn9TZicxqn+Cw4Z439il2v3oT9S0yjlpqlI74aFOQCt3nOV+pxzlkw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.0.3.tgz", + "integrity": "sha512-lREgT5sF05TQk68LO6APy0In+TkFGnFEgKChK2+PHIaTrFQ9oHCKXznZ7VILwgYVBcl0gv4lGATFZBLhi2kVQg==", "requires": { "@types/unist": "^2.0.0", "is-buffer": "^2.0.0", @@ -19896,9 +19956,9 @@ "integrity": "sha512-sSFdyCP3G6Ka0CEmN83A2YCMKIieHx0EDaj5IDP4g1pa5ZJ4FJDvpO0WODLxo4LUX4oe52gmSCK7Jw4SBghqxA==" }, "vfile-message": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.2.tgz", - "integrity": "sha512-gNV2Y2fDvDOOqq8bEe7cF3DXU6QgV4uA9zMR2P8tix11l1r7zju3zry3wZ8sx+BEfuO6WQ7z2QzfWTvqHQiwsA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.3.tgz", + "integrity": "sha512-qQg/2z8qnnBHL0psXyF72kCjb9YioIynvyltuNKFaUhRtqTIcIMP3xnBaPzirVZNuBrUe1qwFciSx2yApa4byw==", "requires": { "@types/unist": "^2.0.0", "unist-util-stringify-position": "^2.0.0" diff --git a/docs/package.json b/docs/package.json index 3015a90ab59..1d8e7335bf0 100644 --- a/docs/package.json +++ b/docs/package.json @@ -7,7 +7,7 @@ }, "dependencies": { "gatsby": "2.19.23", - "gatsby-theme-apollo-docs": "4.0.10", + "gatsby-theme-apollo-docs": "4.0.13", "react": "16.13.0", "react-dom": "16.13.0" } From 81a72e237460e6a8bf2912cf0b6436ef7f0dc9d3 Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Thu, 5 Mar 2020 17:19:13 -0800 Subject: [PATCH 23/27] Add comment to preserve concern about typings from @trevor-scheer. Ref: https://github.com/apollographql/apollo-server/pull/3811#discussion_r387381605 --- packages/apollo-server-core/src/types.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/apollo-server-core/src/types.ts b/packages/apollo-server-core/src/types.ts index ea23bd0a906..ddd1652c9bd 100644 --- a/packages/apollo-server-core/src/types.ts +++ b/packages/apollo-server-core/src/types.ts @@ -93,6 +93,8 @@ export interface GraphQLService { engine?: GraphQLServiceEngineConfig; }): Promise; onSchemaChange(callback: SchemaChangeCallback): Unsubscriber; + // Note: The `TContext` typing here is not conclusively behaving as we expect: + // https://github.com/apollographql/apollo-server/pull/3811#discussion_r387381605 executor( requestContext: WithRequired< GraphQLRequestContext, From 140090520fdee1a65734b6099448f268ed6ffaa4 Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Fri, 6 Mar 2020 12:04:08 -0800 Subject: [PATCH 24/27] Change error message to not include word "lacks". No particular reason, but I just didn't enjoy the previous wording (my own!). --- packages/apollo-server-core/src/ApolloServer.ts | 2 +- .../apollo-server-integration-testsuite/src/ApolloServer.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/apollo-server-core/src/ApolloServer.ts b/packages/apollo-server-core/src/ApolloServer.ts index c5bab2dea9e..6d225b4c6cf 100644 --- a/packages/apollo-server-core/src/ApolloServer.ts +++ b/packages/apollo-server-core/src/ApolloServer.ts @@ -429,7 +429,7 @@ export class ApolloServerBase { // configuration as it may contain implementation details and this // error will propogate to the client. We will, however, log the error // for observation in the logs. - const message = "This data graph lacks a valid configuration."; + const message = "This data graph is missing a valid configuration."; console.error(message + " " + (err && err.message || err)); throw new Error( message + " More details may be available in the server logs."); diff --git a/packages/apollo-server-integration-testsuite/src/ApolloServer.ts b/packages/apollo-server-integration-testsuite/src/ApolloServer.ts index d782758448e..176033dd152 100644 --- a/packages/apollo-server-integration-testsuite/src/ApolloServer.ts +++ b/packages/apollo-server-integration-testsuite/src/ApolloServer.ts @@ -414,12 +414,12 @@ export function testApolloServer( extensions: expect.objectContaining({ code: "INTERNAL_SERVER_ERROR", }), - message: "This data graph lacks a valid configuration. " + + message: "This data graph is missing a valid configuration. " + "More details may be available in the server logs." }) ); expect(consoleErrorSpy).toHaveBeenCalledWith( - "This data graph lacks a valid configuration. " + + "This data graph is missing a valid configuration. " + "load error which should be masked"); expect(executor).not.toHaveBeenCalled(); }); From 5dc847a0724ab8266eb2b8ce3e1c0e56db13b2c5 Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Fri, 6 Mar 2020 14:12:02 -0800 Subject: [PATCH 25/27] Add more helpful error message and docs link to `fetchApolloGcs`. --- packages/apollo-gateway/src/loadServicesFromStorage.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/apollo-gateway/src/loadServicesFromStorage.ts b/packages/apollo-gateway/src/loadServicesFromStorage.ts index 79ef860b656..e1577a7e39a 100644 --- a/packages/apollo-gateway/src/loadServicesFromStorage.ts +++ b/packages/apollo-gateway/src/loadServicesFromStorage.ts @@ -88,7 +88,10 @@ function fetchApolloGcs( ) { throw new Error( "Unable to authenticate with Apollo Graph Manager storage " + - "while fetching " + url); + "while fetching " + url + ". Ensure that the API key is " + + "configured properly and that a federated service has been " + + "pushed. For details, see " + + "https://go.apollo.dev/g/resolve-access-denied."); } // Normally, we'll try to keep the logs clean with errors we expect. From 3d3ed9def1670c680a4739f620dc3258c511e8ab Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Fri, 6 Mar 2020 14:27:10 -0800 Subject: [PATCH 26/27] Remove CHANGELOG.md annotations for pre-release version designators. These have been released! --- packages/apollo-federation/CHANGELOG.md | 2 +- packages/apollo-gateway/CHANGELOG.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/apollo-federation/CHANGELOG.md b/packages/apollo-federation/CHANGELOG.md index a4f2d9e93dc..3d3f38a8eae 100644 --- a/packages/apollo-federation/CHANGELOG.md +++ b/packages/apollo-federation/CHANGELOG.md @@ -1,6 +1,6 @@ # CHANGELOG for `@apollo/federation` -## 0.13.2 (pre-release; `@next` tag) +## 0.13.2 - Only changes in the similarly versioned `@apollo/gateway` package. diff --git a/packages/apollo-gateway/CHANGELOG.md b/packages/apollo-gateway/CHANGELOG.md index 7cfaed85024..563535733be 100644 --- a/packages/apollo-gateway/CHANGELOG.md +++ b/packages/apollo-gateway/CHANGELOG.md @@ -1,6 +1,6 @@ # CHANGELOG for `@apollo/gateway` -## 0.13.2 (pre-release; `@next` tag) +## 0.13.2 - __BREAKING__: The behavior and signature of `RemoteGraphQLDataSource`'s `didReceiveResponse` method has been changed. No changes are necessary _unless_ your implementation has overridden the default behavior of this method by either extending the class and overriding the method or by providing `didReceiveResponse` as a parameter to the `RemoteGraphQLDataSource`'s constructor options. Implementations which have provided their own `didReceiveResponse` using either of these methods should view the PR linked here for details on what has changed. [PR #3743](https://github.com/apollographql/apollo-server/pull/3743) - __NEW__: Setting the `apq` option to `true` on the `RemoteGraphQLDataSource` will enable the use of [automated persisted queries (APQ)](https://www.apollographql.com/docs/apollo-server/performance/apq/) when sending queries to downstream services. Depending on the complexity of queries sent to downstream services, this technique can greatly reduce the size of the payloads being transmitted over the network. Downstream implementing services must also support APQ functionality to participate in this feature (Apollo Server does by default unless it has been explicitly disabled). As with normal APQ behavior, a downstream server must have received and registered a query once before it will be able to serve an APQ request. [#3744](https://github.com/apollographql/apollo-server/pull/3744) From 264547c24c3dc9ee28bdd8a55aabf99804ee1f2c Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Fri, 6 Mar 2020 14:30:42 -0800 Subject: [PATCH 27/27] Add CHANGELOG.md for #3811. --- CHANGELOG.md | 3 +++ packages/apollo-federation/CHANGELOG.md | 4 ++++ packages/apollo-gateway/CHANGELOG.md | 8 +++++++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c6a72c1e7cd..794801e7fc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ The version headers in this history reflect the versions of Apollo Server itself ### v2.12.0 +- `apollo-server-core`: When operating in gateway mode using the `gateway` property of the Apollo Server constructor options, the failure to initialize a schema during initial start-up, e.g. connectivity problems, will no longer result in the federated executor from being assigned when the schema eventually becomes available. This precludes a state where the gateway may never become available to serve federated requests, even when failure conditions are no longer present. [PR #3811](https://github.com/apollographql/apollo-server/pull/3811) +- `apollo-server-core`: Prevent a condition which prefixed an error message on each request when the initial gateway initialization resulted in a Promise-rejection which was memoized and re-prepended with `Invalid options provided to ApolloServer:` on each request. [PR #3811](https://github.com/apollographql/apollo-server/pull/3811) + ### v2.11.0 - The range of accepted `peerDepedencies` versions for `graphql` has been widened to include `graphql@^15.0.0-rc.2` so as to accommodate the latest release-candidate of the `graphql@15` package, and an intention to support it when it is finally released on the `latest` npm tag. While this change will subdue peer dependency warnings for Apollo Server packages, many dependencies from outside of this repository will continue to raise similar warnings until those packages own `peerDependencies` are updated. It is unlikely that all of those packages will update their ranges prior to the final version of `graphql@15` being released, but if everything is working as expected, the warnings can be safely ignored. [PR #3825](https://github.com/apollographql/apollo-server/pull/3825) diff --git a/packages/apollo-federation/CHANGELOG.md b/packages/apollo-federation/CHANGELOG.md index 3d3f38a8eae..d78c53f24bc 100644 --- a/packages/apollo-federation/CHANGELOG.md +++ b/packages/apollo-federation/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG for `@apollo/federation` +## 0.14.0 + +- Only changes in the similarly versioned `@apollo/gateway` package. + ## 0.13.2 - Only changes in the similarly versioned `@apollo/gateway` package. diff --git a/packages/apollo-gateway/CHANGELOG.md b/packages/apollo-gateway/CHANGELOG.md index 563535733be..b29578cbd08 100644 --- a/packages/apollo-gateway/CHANGELOG.md +++ b/packages/apollo-gateway/CHANGELOG.md @@ -1,4 +1,10 @@ -# CHANGELOG for `@apollo/gateway` +# CHANGELOG for `@apollo/gatewae` + +## 0.14.0 (pre-release; `@next` tag) + +- Several previously unhandled Promise rejection errors stemming from, e.g. connectivity, failures when communicating with Apollo Graph Manager within asynchronous code are now handled. [PR #3811](https://github.com/apollographql/apollo-server/pull/3811) +- Provide a more helpful error message when encountering expected errors. [PR #3811](https://github.com/apollographql/apollo-server/pull/3811) +- General improvements and clarity to error messages and logging. [PR #3811](https://github.com/apollographql/apollo-server/pull/3811) ## 0.13.2