@graphql-mesh/merger-stitching
Version:
130 lines (129 loc) • 5.62 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const graphql_1 = require("graphql");
const store_1 = require("@graphql-mesh/store");
const utils_1 = require("@graphql-mesh/utils");
const schema_1 = require("@graphql-tools/schema");
const stitch_1 = require("@graphql-tools/stitch");
const stitching_directives_1 = require("@graphql-tools/stitching-directives");
const utils_2 = require("@graphql-tools/utils");
const APOLLO_GET_SERVICE_DEFINITION_QUERY = /* GraphQL */ `
query __ApolloGetServiceDefinition__ {
_service {
sdl
}
}
`;
class StitchingMerger {
constructor(options) {
this.name = 'stitching';
this.logger = options.logger;
this.store = options.store;
}
isFederatedSchema(schema) {
const queryType = schema.getQueryType();
if (queryType) {
const queryFields = queryType.getFields();
return '_service' in queryFields;
}
return false;
}
async replaceFederationSDLWithStitchingSDL(name, oldSchema, executor, stitchingDirectives) {
const rawSourceLogger = this.logger.child(name);
rawSourceLogger.debug(`Extracting existing resolvers if available`);
const resolvers = (0, utils_1.extractResolvers)(oldSchema);
let newSchema = await this.store
.proxy(`${name}_stitching`, store_1.PredefinedProxyOptions.GraphQLSchemaWithDiffing)
.getWithSet(async () => {
this.logger.debug(`Fetching Apollo Federated Service SDL for ${name}`);
let federationSdl;
if (oldSchema.extensions?.directives?.link) {
federationSdl = (0, utils_2.printSchemaWithDirectives)(oldSchema);
}
else {
const sdlQueryResult = (await executor({
document: (0, graphql_1.parse)(APOLLO_GET_SERVICE_DEFINITION_QUERY),
}));
if (sdlQueryResult.errors?.length) {
throw new AggregateError(sdlQueryResult.errors, `Failed on fetching Federated SDL for ${name}`);
}
federationSdl = sdlQueryResult.data._service.sdl;
}
this.logger.debug(`Generating Stitching SDL for ${name}`);
const stitchingSdl = (0, stitching_directives_1.federationToStitchingSDL)(federationSdl, stitchingDirectives);
return (0, graphql_1.buildSchema)(stitchingSdl, {
assumeValid: true,
assumeValidSDL: true,
});
});
rawSourceLogger.debug(`Adding existing resolvers back to the schema`);
newSchema = (0, schema_1.addResolversToSchema)({
schema: newSchema,
resolvers,
updateResolversInPlace: true,
resolverValidationOptions: {
requireResolversToMatchSchema: 'ignore',
},
});
newSchema.extensions = oldSchema.extensions;
return newSchema;
}
async getUnifiedSchema(context) {
const { rawSources, typeDefs, resolvers } = context;
this.logger.debug(`Stitching directives are being generated`);
const defaultStitchingDirectives = (0, stitching_directives_1.stitchingDirectives)({
pathToDirectivesInExtensions: ['directives'],
});
this.logger.debug(`Checking if any of sources has federation metadata`);
const subschemas = await Promise.all(rawSources.map(async (rawSource) => {
if (rawSource.batch == null) {
rawSource.batch = true;
}
if (this.isFederatedSchema(rawSource.schema)) {
this.logger.debug(`${rawSource.name} has federated schema.`);
rawSource.schema = await this.replaceFederationSDLWithStitchingSDL(rawSource.name, rawSource.schema, rawSource.executor, defaultStitchingDirectives);
}
rawSource.merge =
defaultStitchingDirectives.stitchingDirectivesTransformer(rawSource).merge;
return rawSource;
}));
this.logger.debug(`Stitching the source schemas`);
const unifiedSchema = (0, stitch_1.stitchSchemas)({
subschemas,
typeDefs,
resolvers,
typeMergingOptions: {
validationSettings: {
validationLevel: stitch_1.ValidationLevel.Off,
},
},
mergeDirectives: true,
});
this.logger.debug(`sourceMap is being generated and attached to the unified schema`);
unifiedSchema.extensions = unifiedSchema.extensions || {};
Object.assign(unifiedSchema.extensions, {
sourceMap: new Proxy({}, {
get: (_, pKey) => {
if (pKey === 'get') {
return (rawSource) => {
const stitchingInfo = unifiedSchema.extensions.stitchingInfo;
for (const [subschemaConfig, subschema] of stitchingInfo.subschemaMap) {
if (subschemaConfig.name === rawSource.name) {
return subschema.transformedSchema;
}
}
return undefined;
};
}
return () => {
throw new Error('Not Implemented');
};
},
}),
});
return {
schema: unifiedSchema,
};
}
}
exports.default = StitchingMerger;