@wundergraph/cosmo-shared
Version:
Shared code between WunderGraph Cosmo packages
197 lines • 8.55 kB
JavaScript
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