import { defineConfig } from '@graphql-hive/gateway'; import { hiveTracingSetup } from '@graphql-hive/plugin-opentelemetry/setup'; import type { Context } from '@opentelemetry/api'; import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks'; import { globalErrorHandler } from '@opentelemetry/core'; import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; import { BatchSpanProcessor, SpanProcessor, type ReadableSpan, type Span, } from '@opentelemetry/sdk-trace-base'; /** Note: this is inlined for now... */ class MultiSpanProcessor implements SpanProcessor { constructor(private readonly _spanProcessors: SpanProcessor[]) {} forceFlush(): Promise { const promises: Promise[] = []; for (const spanProcessor of this._spanProcessors) { promises.push(spanProcessor.forceFlush()); } return new Promise(resolve => { Promise.all(promises) .then(() => { resolve(); }) .catch(error => { globalErrorHandler(error || new Error('MultiSpanProcessor: forceFlush failed')); resolve(); }); }); } onStart(span: Span, context: Context): void { for (const spanProcessor of this._spanProcessors) { spanProcessor.onStart(span, context); } } onEnd(span: ReadableSpan): void { for (const spanProcessor of this._spanProcessors) { spanProcessor.onEnd(span); } } shutdown(): Promise { const promises: Promise[] = []; for (const spanProcessor of this._spanProcessors) { promises.push(spanProcessor.shutdown()); } return new Promise((resolve, reject) => { Promise.all(promises).then(() => { resolve(); }, reject); }); } } if ( process.env['OPENTELEMETRY_COLLECTOR_ENDPOINT'] || process.env['HIVE_HIVE_TRACE_ACCESS_TOKEN'] ) { hiveTracingSetup({ // Noop is only there to not raise an exception in case we do not hive console tracing. target: process.env['HIVE_HIVE_TARGET'] ?? 'noop', contextManager: new AsyncLocalStorageContextManager(), // @ts-expect-error console uses otel v1 and hive gateway uses v2 // TODO: upgrade console to otel v2 processor: new MultiSpanProcessor([ ...(process.env['HIVE_HIVE_TRACE_ACCESS_TOKEN'] && process.env['HIVE_HIVE_TRACE_ENDPOINT'] && process.env['HIVE_HIVE_TARGET'] ? [ new BatchSpanProcessor( new OTLPTraceExporter({ url: process.env['HIVE_HIVE_TRACE_ENDPOINT'], headers: { Authorization: `Bearer ${process.env['HIVE_HIVE_TRACE_ACCESS_TOKEN']}`, 'X-Hive-Target-Ref': process.env['HIVE_HIVE_TARGET'], }, }), ), ] : []), ...(process.env['OPENTELEMETRY_COLLECTOR_ENDPOINT'] ? [ new BatchSpanProcessor( new OTLPTraceExporter({ url: process.env['OPENTELEMETRY_COLLECTOR_ENDPOINT']!, }), ), ] : []), ]), }); } const defaultQuery = `# # Welcome to the Hive Console GraphQL API. # `; export const gatewayConfig = defineConfig({ transportEntries: { graphql: { location: process.env['GRAPHQL_SERVICE_ENDPOINT'], }, }, supergraph: { type: 'hive', endpoint: process.env['SUPERGRAPH_ENDPOINT'] || '', key: process.env['HIVE_CDN_ACCESS_TOKEN'] || '', }, graphiql: { title: 'Hive Console - GraphQL API', defaultQuery, }, propagateHeaders: { fromClientToSubgraphs({ request }) { return { 'x-request-id': request.headers.get('x-request-id'), authorization: request.headers.get('authorization'), }; }, }, disableWebsockets: true, prometheus: { metrics: true, }, openTelemetry: process.env['OPENTELEMETRY_COLLECTOR_ENDPOINT'] || process.env['HIVE_HIVE_TRACE_ACCESS_TOKEN'] ? { traces: true, } : undefined, demandControl: { maxCost: 1000, includeExtensionMetadata: true, }, maxTokens: 1_000, maxDepth: 20, cors: { origin: '*', // allow all origins credentials: false, // do not allow credentials in cross-origin requests }, });