|
1 | 1 | import { deprecate } from 'util'; |
2 | 2 | import { createHash } from '@apollo/utils.createhash'; |
3 | 3 | import type { Logger } from '@apollo/utils.logger'; |
4 | | -import { QueryPlanCache } from '@apollo/query-planner' |
| 4 | +import { QueryPlanCache } from '@apollo/query-planner'; |
5 | 5 | import { InMemoryLRUCache } from '@apollo/utils.keyvaluecache'; |
6 | 6 | import { |
7 | 7 | GraphQLSchema, |
@@ -47,7 +47,7 @@ import { |
47 | 47 | requestContextSpanAttributes, |
48 | 48 | operationContextSpanAttributes, |
49 | 49 | recordExceptions, |
50 | | - OpenTelemetryAttributeNames |
| 50 | + OpenTelemetryAttributeNames, |
51 | 51 | } from './utilities/opentelemetry'; |
52 | 52 | import { addExtensions } from './schema-helper/addExtensions'; |
53 | 53 | import { |
@@ -141,6 +141,12 @@ export class ApolloGateway implements GatewayInterface { |
141 | 141 | coreSupergraphSdl: string; |
142 | 142 | }) => void |
143 | 143 | >(); |
| 144 | + private onSchemaWillBeUsedListeners = new Set< |
| 145 | + (schemaContext: { |
| 146 | + apiSchema: GraphQLSchema; |
| 147 | + coreSupergraphSdl: string; |
| 148 | + }) => void |
| 149 | + >(); |
144 | 150 | private warnedStates: WarnedStates = Object.create(null); |
145 | 151 | private queryPlanner?: QueryPlanner; |
146 | 152 | private supergraphSdl?: string; |
@@ -198,8 +204,8 @@ export class ApolloGateway implements GatewayInterface { |
198 | 204 | } |
199 | 205 |
|
200 | 206 | private initQueryPlanStore(approximateQueryPlanStoreMiB?: number) { |
201 | | - if(this.config.queryPlannerConfig?.cache){ |
202 | | - return this.config.queryPlannerConfig?.cache |
| 207 | + if (this.config.queryPlannerConfig?.cache) { |
| 208 | + return this.config.queryPlannerConfig?.cache; |
203 | 209 | } |
204 | 210 | // Create ~about~ a 30MiB InMemoryLRUCache (or 50MiB if the full operation ASTs are |
205 | 211 | // enabled in query plans as this requires plans to use more memory). This is |
@@ -569,6 +575,23 @@ export class ApolloGateway implements GatewayInterface { |
569 | 575 | legacyDontNotifyOnSchemaChangeListeners: boolean = false, |
570 | 576 | ): void { |
571 | 577 | this.queryPlanStore.clear(); |
| 578 | + |
| 579 | + // Notify before use of new schema |
| 580 | + this.onSchemaWillBeUsedListeners.forEach((listener) => { |
| 581 | + try { |
| 582 | + listener({ |
| 583 | + apiSchema: this.schema!, |
| 584 | + coreSupergraphSdl: supergraphSdl, |
| 585 | + }); |
| 586 | + } catch (e) { |
| 587 | + this.logger.error( |
| 588 | + "An error was thrown from an 'onSchemaWillBeUsed' listener. " + |
| 589 | + 'The schema will still update: ' + |
| 590 | + ((e && e.message) || e), |
| 591 | + ); |
| 592 | + } |
| 593 | + }); |
| 594 | + |
572 | 595 | this.apiSchema = supergraph.apiSchema(); |
573 | 596 | this.schema = addExtensions(this.apiSchema.toGraphQLJSSchema()); |
574 | 597 |
|
@@ -681,6 +704,19 @@ export class ApolloGateway implements GatewayInterface { |
681 | 704 | }; |
682 | 705 | } |
683 | 706 |
|
| 707 | + public onSchemaWillBeUsed( |
| 708 | + callback: (schemaContext: { |
| 709 | + apiSchema: GraphQLSchema; |
| 710 | + coreSupergraphSdl: string; |
| 711 | + }) => void, |
| 712 | + ): GatewayUnsubscriber { |
| 713 | + this.onSchemaWillBeUsedListeners.add(callback); |
| 714 | + |
| 715 | + return () => { |
| 716 | + this.onSchemaWillBeUsedListeners.delete(callback); |
| 717 | + }; |
| 718 | + } |
| 719 | + |
684 | 720 | private getOrCreateDataSource( |
685 | 721 | serviceDef: ServiceEndpointDefinition, |
686 | 722 | ): GraphQLDataSource { |
@@ -850,7 +886,7 @@ export class ApolloGateway implements GatewayInterface { |
850 | 886 | operationContext, |
851 | 887 | this.supergraphSchema!, |
852 | 888 | this.apiSchema!, |
853 | | - this.config.telemetry |
| 889 | + this.config.telemetry, |
854 | 890 | ); |
855 | 891 |
|
856 | 892 | const shouldShowQueryPlan = |
|
0 commit comments