@graphql-mesh/fusion-runtime
Version:
Runtime for GraphQL Mesh Fusion Supergraph
195 lines (194 loc) • 9.87 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.handleFederationSupergraph = exports.restoreExtraDirectives = void 0;
exports.getStitchingDirectivesTransformerForSubschema = getStitchingDirectivesTransformerForSubschema;
const graphql_1 = require("graphql");
const utils_1 = require("@graphql-mesh/utils");
const federation_1 = require("@graphql-tools/federation");
const merge_1 = require("@graphql-tools/merge");
const stitching_directives_1 = require("@graphql-tools/stitching-directives");
const utils_2 = require("@graphql-tools/utils");
const filterHiddenPartsInSchema_js_1 = require("../filterHiddenPartsInSchema.js");
const subgraph_js_1 = require("./subgraph.js");
// Memoize to avoid re-parsing the same schema AST
// Workaround for unsupported directives on composition: restore extra directives
exports.restoreExtraDirectives = (0, utils_2.memoize1)(function restoreExtraDirectives(schema) {
const queryType = schema.getQueryType();
const queryTypeExtensions = (0, utils_2.getDirectiveExtensions)(queryType);
const extraSchemaDefinitionDirectives = queryTypeExtensions?.extraSchemaDefinitionDirective;
if (extraSchemaDefinitionDirectives?.length) {
schema = (0, utils_2.mapSchema)(schema, {
[utils_2.MapperKind.TYPE]: type => {
const typeDirectiveExtensions = (0, utils_2.getDirectiveExtensions)(type) || {};
const TypeCtor = Object.getPrototypeOf(type).constructor;
if (type.name === queryType.name) {
const typeConfig = type.toConfig();
// Cleanup extra directives on Query type
return new TypeCtor({
...typeConfig,
extensions: {
...(type.extensions || {}),
directives: {
...typeDirectiveExtensions,
extraSchemaDefinitionDirective: [],
},
},
// Cleanup ASTNode to prevent conflicts
astNode: undefined,
});
}
},
});
if (extraSchemaDefinitionDirectives?.length) {
const schemaDirectives = (0, utils_2.getDirectiveExtensions)(schema);
for (const { directives } of extraSchemaDefinitionDirectives) {
for (const directiveName in directives) {
schemaDirectives[directiveName] ||= [];
schemaDirectives[directiveName].push(...directives[directiveName]);
}
}
const schemaExtensions = (schema.extensions ||= {});
schemaExtensions.directives = schemaDirectives;
}
}
return schema;
});
function getStitchingDirectivesTransformerForSubschema() {
const { stitchingDirectivesTransformer } = (0, stitching_directives_1.stitchingDirectives)({
keyDirectiveName: 'stitch__key',
computedDirectiveName: 'stitch__computed',
mergeDirectiveName: 'merge',
canonicalDirectiveName: 'stitch__canonical',
});
return stitchingDirectivesTransformer;
}
const handleFederationSupergraph = function ({ unifiedGraph, onSubgraphExecute, additionalTypeDefs: additionalTypeDefsFromConfig = [], additionalResolvers: additionalResolversFromConfig = [], transportEntryAdditions, batch = true, }) {
const additionalTypeDefs = [...(0, utils_2.asArray)(additionalTypeDefsFromConfig)];
const additionalResolvers = [...(0, utils_2.asArray)(additionalResolversFromConfig)];
const transportEntryMap = {};
let subschemas = [];
const stitchingDirectivesTransformer = getStitchingDirectivesTransformerForSubschema();
unifiedGraph = (0, exports.restoreExtraDirectives)(unifiedGraph);
// Get Transport Information from Schema Directives
const schemaDirectives = (0, utils_2.getDirectiveExtensions)(unifiedGraph);
// Workaround to get the real name of the subschema
const realSubgraphNameMap = new Map();
const joinGraphType = unifiedGraph.getType('join__Graph');
if ((0, graphql_1.isEnumType)(joinGraphType)) {
for (const enumValue of joinGraphType.getValues()) {
const enumValueDirectives = (0, utils_2.getDirectiveExtensions)(enumValue);
const joinGraphDirectives = enumValueDirectives?.join__graph;
if (joinGraphDirectives?.length) {
for (const joinGraphDirective of joinGraphDirectives) {
realSubgraphNameMap.set(enumValue.name, joinGraphDirective.name);
}
}
}
}
let executableUnifiedGraph = (0, federation_1.getStitchedSchemaFromSupergraphSdl)({
supergraphSdl: (0, utils_2.getDocumentNodeFromSchema)(unifiedGraph),
/**
* This visits over the subgraph schema to get;
* - Extra Type Defs and Resolvers (additionalTypeDefs & additionalResolvers)
* - Transport Entries (transportEntryMap)
* - Type Merging Configuration for the subgraph (subschemaConfig.merge)
* - Set the executor for the subschema (subschemaConfig.executor)
*/
onSubschemaConfig: subschemaConfig => (0, subgraph_js_1.handleFederationSubschema)({
subschemaConfig,
realSubgraphNameMap,
schemaDirectives,
transportEntryMap,
additionalTypeDefs,
stitchingDirectivesTransformer,
onSubgraphExecute,
}),
batch,
onStitchingOptions(opts) {
subschemas = opts.subschemas;
const mergedTypeDefs = (0, merge_1.mergeTypeDefs)([opts.typeDefs, additionalTypeDefs]);
(0, graphql_1.visit)(mergedTypeDefs, {
[graphql_1.Kind.FIELD_DEFINITION](field, _key, _parent, _path, ancestors) {
const fieldDirectives = (0, utils_2.getDirectiveExtensions)({ astNode: field });
const resolveToDirectives = fieldDirectives?.resolveTo;
if (resolveToDirectives?.length) {
const targetTypeName = ancestors[ancestors.length - 1]
.name.value;
const targetFieldName = field.name.value;
for (const resolveToDirective of resolveToDirectives) {
additionalResolvers.push((0, utils_1.resolveAdditionalResolversWithoutImport)({
targetTypeName,
targetFieldName,
...resolveToDirective,
}));
}
}
},
});
opts.typeDefs = mergedTypeDefs;
opts.resolvers = additionalResolvers;
},
onSubgraphAST(_name, subgraphAST) {
return (0, graphql_1.visit)(subgraphAST, {
[graphql_1.Kind.OBJECT_TYPE_DEFINITION](node) {
const typeName = node.name.value;
return {
...node,
fields: node.fields.filter(fieldNode => {
const fieldDirectives = (0, utils_2.getDirectiveExtensions)({ astNode: fieldNode });
const resolveToDirectives = fieldDirectives.resolveTo;
if (resolveToDirectives?.length > 0) {
additionalTypeDefs.push({
kind: graphql_1.Kind.DOCUMENT,
definitions: [
{
kind: graphql_1.Kind.OBJECT_TYPE_DEFINITION,
name: { kind: graphql_1.Kind.NAME, value: typeName },
fields: [fieldNode],
},
],
});
}
const additionalFieldDirectives = fieldDirectives.additionalField;
if (additionalFieldDirectives?.length > 0) {
return false;
}
return true;
}),
};
},
});
},
});
// Filter hidden elements with @hidden directive
executableUnifiedGraph = (0, filterHiddenPartsInSchema_js_1.filterHiddenPartsInSchema)(executableUnifiedGraph);
if (transportEntryAdditions) {
const wildcardTransportOptions = transportEntryAdditions['*'];
for (const subgraphName in transportEntryMap) {
const toBeMerged = [];
const transportEntry = transportEntryMap[subgraphName];
if (transportEntry) {
toBeMerged.push(transportEntry);
}
const transportOptionBySubgraph = transportEntryAdditions[subgraphName];
if (transportOptionBySubgraph) {
toBeMerged.push(transportOptionBySubgraph);
}
const transportOptionByKind = transportEntryAdditions['*.' + transportEntry?.kind];
if (transportOptionByKind) {
toBeMerged.push(transportOptionByKind);
}
if (wildcardTransportOptions) {
toBeMerged.push(wildcardTransportOptions);
}
transportEntryMap[subgraphName] = (0, utils_2.mergeDeep)(toBeMerged);
}
}
return {
unifiedGraph: executableUnifiedGraph,
subschemas,
transportEntryMap,
additionalResolvers,
};
};
exports.handleFederationSupergraph = handleFederationSupergraph;