UNPKG

@wundergraph/cosmo-shared

Version:

Shared code between WunderGraph Cosmo packages

197 lines 8.55 kB
import crypto from 'node:crypto'; import { printSchemaWithDirectives } from '@graphql-tools/utils'; import { COMPOSITION_VERSION, ROOT_TYPE_NAMES, ROUTER_COMPATIBILITY_VERSIONS, } from '@wundergraph/composition'; import { GraphQLSubscriptionProtocol, GraphQLWebsocketSubprotocol, } from '@wundergraph/cosmo-connect/dist/common/common_pb'; import { lexicographicSortSchema } from 'graphql'; import { ConfigurationVariable, ConfigurationVariableKind, DataSourceConfiguration, DataSourceCustom_GraphQL, DataSourceCustomEvents, DataSourceKind, EngineConfiguration, GRPCConfiguration, HTTPMethod, InternedString, PluginConfiguration, RouterConfig, } from '@wundergraph/cosmo-connect/dist/node/v1/node_pb'; import { invalidRouterCompatibilityVersion, normalizationFailureError } from './errors.js'; import { configurationDatasToDataSourceConfiguration, generateFieldConfigurations } from './graphql-configuration.js'; export var SubgraphKind; (function (SubgraphKind) { SubgraphKind[SubgraphKind["Plugin"] = 0] = "Plugin"; SubgraphKind[SubgraphKind["Standard"] = 1] = "Standard"; SubgraphKind[SubgraphKind["GRPC"] = 2] = "GRPC"; })(SubgraphKind || (SubgraphKind = {})); export const internString = (config, str) => { const key = crypto.createHash('sha1').update(str).digest('hex'); config.stringStorage[key] = str; return new InternedString({ key, }); }; export const parseGraphQLSubscriptionProtocol = (protocolName) => { switch (protocolName) { case 'ws': { return GraphQLSubscriptionProtocol.GRAPHQL_SUBSCRIPTION_PROTOCOL_WS; } case 'sse': { return GraphQLSubscriptionProtocol.GRAPHQL_SUBSCRIPTION_PROTOCOL_SSE; } case 'sse_post': { return GraphQLSubscriptionProtocol.GRAPHQL_SUBSCRIPTION_PROTOCOL_SSE_POST; } } }; export const parseGraphQLWebsocketSubprotocol = (protocolName) => { switch (protocolName) { case 'auto': { return GraphQLWebsocketSubprotocol.GRAPHQL_WEBSOCKET_SUBPROTOCOL_AUTO; } case 'graphql-ws': { return GraphQLWebsocketSubprotocol.GRAPHQL_WEBSOCKET_SUBPROTOCOL_WS; } case 'graphql-transport-ws': { return GraphQLWebsocketSubprotocol.GRAPHQL_WEBSOCKET_SUBPROTOCOL_TRANSPORT_WS; } } }; export const buildRouterConfig = function (input) { if (!ROUTER_COMPATIBILITY_VERSIONS.has(input.routerCompatibilityVersion)) { throw invalidRouterCompatibilityVersion(input.routerCompatibilityVersion); } const engineConfig = new EngineConfiguration({ defaultFlushInterval: BigInt(500), datasourceConfigurations: [], fieldConfigurations: [], graphqlSchema: '', stringStorage: {}, typeConfigurations: [], }); for (const subgraph of input.subgraphs) { if (!subgraph.configurationDataByTypeName) { throw normalizationFailureError('ConfigurationDataByTypeName'); } if (!subgraph.schema) { throw normalizationFailureError('GraphQLSchema'); } const subscriptionConfig = { enabled: true, }; // IMPORTANT NOTE: printSchema and printSchemaWithDirectives promotes extension types to "full" types const upstreamSchema = internString(engineConfig, printSchemaWithDirectives(lexicographicSortSchema(subgraph.schema))); const { childNodes, entityInterfaces, events, interfaceObjects, keys, provides, requires, rootNodes } = configurationDatasToDataSourceConfiguration(subgraph.configurationDataByTypeName); let grcpConfig; switch (subgraph.kind) { case SubgraphKind.Standard: { subscriptionConfig.enabled = true; subscriptionConfig.protocol = parseGraphQLSubscriptionProtocol(subgraph.subscriptionProtocol || 'ws'); subscriptionConfig.websocketSubprotocol = parseGraphQLWebsocketSubprotocol(subgraph.websocketSubprotocol || 'auto'); // When changing this, please do it in the router subgraph override as well subscriptionConfig.url = new ConfigurationVariable({ kind: ConfigurationVariableKind.STATIC_CONFIGURATION_VARIABLE, staticVariableContent: subgraph.subscriptionUrl || subgraph.url, }); break; } case SubgraphKind.Plugin: { grcpConfig = new GRPCConfiguration({ mapping: subgraph.mapping, protoSchema: subgraph.protoSchema, plugin: new PluginConfiguration({ name: subgraph.name, version: subgraph.version, imageReference: subgraph.imageReference, }), }); break; } case SubgraphKind.GRPC: { grcpConfig = new GRPCConfiguration({ mapping: subgraph.mapping, protoSchema: subgraph.protoSchema, }); break; } // No default } let kind; let customGraphql; let customEvents; if (events.kafka.length > 0 || events.nats.length > 0 || events.redis.length > 0) { kind = DataSourceKind.PUBSUB; customEvents = new DataSourceCustomEvents({ kafka: events.kafka, nats: events.nats, redis: events.redis, }); // PUBSUB data sources cannot have root nodes other than // Query/Mutation/Subscription. Filter rootNodes in place // while moving items that do not pass the filter to childNodes. const isRootTypeNode = (node) => { return ROOT_TYPE_NAMES.has(node.typeName); }; let ii = 0; let filtered = 0; while (ii < rootNodes.length) { const node = rootNodes[ii]; if (isRootTypeNode(node)) { rootNodes[filtered++] = node; } else { childNodes.push(node); } ii++; } rootNodes.length = filtered; } else { kind = DataSourceKind.GRAPHQL; customGraphql = new DataSourceCustom_GraphQL({ customScalarTypeFields: [], federation: { enabled: true, serviceSdl: subgraph.sdl, }, upstreamSchema, grpc: grcpConfig, fetch: { url: new ConfigurationVariable({ kind: ConfigurationVariableKind.STATIC_CONFIGURATION_VARIABLE, staticVariableContent: subgraph.url, }), method: HTTPMethod.POST, header: {}, body: {}, baseUrl: {}, path: {}, }, subscription: subscriptionConfig, }); } const datasourceConfig = new DataSourceConfiguration({ // When changing the id, make sure to change it in the router subgraph override also // https://github.com/wundergraph/cosmo/blob/main/router/core/router.go#L342 id: subgraph.id, childNodes, customEvents, customGraphql, directives: [], entityInterfaces, interfaceObjects, keys, kind, overrideFieldPathFromAlias: true, provides, requestTimeoutSeconds: BigInt(10), requires, rootNodes, }); engineConfig.datasourceConfigurations.push(datasourceConfig); } engineConfig.fieldConfigurations = generateFieldConfigurations(input.fieldConfigurations); engineConfig.graphqlSchema = input.federatedSDL; if (input.federatedClientSDL !== '') { engineConfig.graphqlClientSchema = input.federatedClientSDL; } return new RouterConfig({ engineConfig, version: input.schemaVersionId, subgraphs: input.subgraphs.map((s) => ({ id: s.id, name: s.name, routingUrl: s.url, })), compatibilityVersion: `${input.routerCompatibilityVersion}:${COMPOSITION_VERSION}`, }); }; //# sourceMappingURL=builder.js.map