UNPKG

graphql-component

Version:

Build, customize and compose GraphQL schemas in a componentized fashion

365 lines 30.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const federation_1 = require("@apollo/federation"); const graphql_1 = require("graphql"); const merge_1 = require("@graphql-tools/merge"); const utils_1 = require("@graphql-tools/utils"); const schema_1 = require("@graphql-tools/schema"); const stitch_1 = require("@graphql-tools/stitch"); const mock_1 = require("@graphql-tools/mock"); /** * GraphQLComponent class for building modular GraphQL schemas * @template TContextType - The type of the context object * @implements {IGraphQLComponent} */ class GraphQLComponent { _schema; _types; _resolvers; _mocks; _imports; _context; _dataSources; _dataSourceOverrides; _pruneSchema; _pruneSchemaOptions; _federation; _dataSourceContextInject; _transforms; _transformedSchema; _middleware = []; constructor({ types, resolvers, mocks, imports, context, dataSources, dataSourceOverrides, pruneSchema, pruneSchemaOptions, federation, transforms }) { this._types = Array.isArray(types) ? types : [types]; this._resolvers = bindResolvers(this, resolvers); this._mocks = mocks; this._federation = federation; this._transforms = transforms; this._dataSources = dataSources || []; this._dataSourceOverrides = dataSourceOverrides || []; this._dataSourceContextInject = createDataSourceContextInjector(this._dataSources, this._dataSourceOverrides); this._pruneSchema = pruneSchema; this._pruneSchemaOptions = pruneSchemaOptions; this._imports = imports && imports.length > 0 ? imports.map((i) => { if (!i) { throw new Error('Import cannot be undefined or null'); } // Check if it's already a config object (has 'component' property) if ('component' in i && i.component) { const importConfiguration = i; if (this._federation === true) { importConfiguration.component.federation = true; } return importConfiguration; } // Otherwise, treat it as an IGraphQLComponent and wrap it const component = i; if (this._federation === true) { component.federation = true; } return { component }; }) : []; this._context = async (globalContext) => { //BREAKING: The context injected into data sources won't have data sources on it const ctx = { dataSources: globalContext.dataSources || {} }; // Add this component's dataSources if not already present or if empty if (!globalContext.dataSources || Object.keys(globalContext.dataSources).length === 0) { Object.assign(ctx.dataSources, this._dataSourceContextInject(globalContext)); } // Only process imports if they exist if (this._imports.length > 0) { // Process imports in parallel if they're independent const importPromises = this._imports.map(async ({ component }) => { const importContext = await component.context(globalContext); return importContext; }); const importResults = await Promise.all(importPromises); // Merge results efficiently for (const { dataSources, ...importedContext } of importResults) { Object.assign(ctx.dataSources, dataSources); Object.assign(ctx, importedContext); } } // Handle namespace context if present if (context) { if (!ctx[context.namespace]) { ctx[context.namespace] = {}; } const namespaceContext = await context.factory.call(this, globalContext); Object.assign(ctx[context.namespace], namespaceContext); } return ctx; }; this.validateConfig({ types, imports, mocks, federation }); } get context() { // Cache middleware array to avoid recreation const contextFn = async (context) => { // Inject dataSources early so middleware can access them const dataSources = this._dataSourceContextInject(context); // Also gather data sources from imported components for middleware const importedDataSources = {}; if (this._imports.length > 0) { for (const { component } of this._imports) { // Use the public dataSources getter and manually inject them const componentDataSourcesArray = component.dataSources; const componentDataSourceOverrides = component.dataSourceOverrides; const componentInjector = createDataSourceContextInjector(componentDataSourcesArray, componentDataSourceOverrides); const componentDataSources = componentInjector(context); Object.assign(importedDataSources, componentDataSources); } } // Combine all data sources for middleware const allDataSources = Object.assign({}, dataSources, importedDataSources); let processedContext = Object.assign({}, context, { dataSources: allDataSources }); // Apply middleware more efficiently if (this._middleware.length > 0) { for (const mw of this._middleware) { processedContext = await mw.fn(processedContext); } } const componentContext = await this._context(processedContext); // More efficient object composition return Object.assign({}, processedContext, componentContext); }; contextFn.use = (name, fn) => { if (typeof name === 'function') { fn = name; name = 'unknown'; } this._middleware.push({ name: name, fn: fn }); return contextFn; }; return contextFn; } get name() { return this.constructor.name; } get schema() { try { if (this._schema) { return this._schema; } let makeSchema; if (this._federation) { makeSchema = federation_1.buildFederatedSchema; } else { makeSchema = schema_1.makeExecutableSchema; } if (this._imports.length > 0) { // iterate through the imports and construct subschema configuration objects const subschemas = this._imports.map((imp) => { const { component, configuration = {} } = imp; return { schema: component.schema, ...configuration }; }); // construct an aggregate schema from the schemas of imported // components and this component's types/resolvers (if present) this._schema = (0, stitch_1.stitchSchemas)({ subschemas, typeDefs: this._types, resolvers: this._resolvers, mergeDirectives: true }); } else { const schemaConfig = { typeDefs: (0, merge_1.mergeTypeDefs)(this._types), resolvers: this._resolvers }; this._schema = makeSchema(schemaConfig); } if (this._transforms) { this._schema = this.transformSchema(this._schema, this._transforms); } if (this._mocks !== undefined && typeof this._mocks === 'boolean' && this._mocks === true) { // if mocks are a boolean support simply applying default mocks this._schema = (0, mock_1.addMocksToSchema)({ schema: this._schema, preserveResolvers: true }); } else if (this._mocks !== undefined && typeof this._mocks === 'object') { // else if mocks is an object, that means the user provided // custom mocks, with which we pass them to addMocksToSchema so they are applied this._schema = (0, mock_1.addMocksToSchema)({ schema: this._schema, mocks: this._mocks, preserveResolvers: true }); } if (this._pruneSchema) { this._schema = (0, utils_1.pruneSchema)(this._schema, this._pruneSchemaOptions); } return this._schema; } catch (error) { throw new Error(`Failed to create schema for component ${this.name}: ${error.message}`); } } get types() { return this._types; } get resolvers() { return this._resolvers; } get imports() { return this._imports; } get dataSources() { return this._dataSources; } get dataSourceOverrides() { return this._dataSourceOverrides; } set federation(flag) { this._federation = flag; } get federation() { return this._federation; } dispose() { this._schema = null; this._types = null; this._resolvers = null; this._imports = null; this._dataSources = null; this._dataSourceOverrides = null; } transformSchema(schema, transforms) { if (this._transformedSchema) { return this._transformedSchema; } const functions = {}; const mapping = {}; for (const transform of transforms) { for (const [key, fn] of Object.entries(transform)) { if (!mapping[key]) { functions[key] = []; mapping[key] = function (...args) { let result; while (functions[key].length) { const mapper = functions[key].shift(); result = mapper(...args); if (!result) { break; } } return result; }; } functions[key].push(fn); } } this._transformedSchema = (0, utils_1.mapSchema)(schema, mapping); return this._transformedSchema; } validateConfig(options) { if (options.federation && !options.types) { throw new Error('Federation requires type definitions'); } if (options.mocks && typeof options.mocks !== 'boolean' && typeof options.mocks !== 'object') { throw new Error('mocks must be either boolean or object'); } } } exports.default = GraphQLComponent; // For backward compatibility module.exports = GraphQLComponent; /** * Wraps data sources with a proxy that intercepts calls to data source methods and injects the current context * @param {IDataSource[]} dataSources * @param {IDataSource[]} dataSourceOverrides * @returns {DataSourceInjectionFunction} a function that returns a map of data sources with methods that have been intercepted */ const createDataSourceContextInjector = (dataSources, dataSourceOverrides) => { const intercept = (instance, context) => { return new Proxy(instance, { get(target, key) { if (typeof target[key] !== 'function' || key === instance.constructor.name) { return target[key]; } const original = target[key]; return function (...args) { return original.call(instance, context, ...args); }; } }); }; return (context = {}) => { const proxiedDataSources = {}; // Inject data sources for (const dataSource of dataSources) { proxiedDataSources[dataSource.name || dataSource.constructor.name] = intercept(dataSource, context); } // Override data sources for (const dataSourceOverride of dataSourceOverrides) { proxiedDataSources[dataSourceOverride.name || dataSourceOverride.constructor.name] = intercept(dataSourceOverride, context); } return proxiedDataSources; }; }; /** * memoizes resolver functions such that calls of an identical resolver (args/context/path) within the same request context are avoided * @param {string} parentType - the type whose field resolver is being * wrapped/memoized * @param {string} fieldName - the field on the parentType whose resolver * function is being wrapped/memoized * @param {function} resolve - the resolver function that parentType. * fieldName is mapped to * @returns {function} a function that wraps the input resolver function and * whose closure scope contains a WeakMap to achieve memoization of the wrapped * input resolver function */ const memoize = function (parentType, fieldName, resolve) { const _cache = new WeakMap(); return function _memoizedResolver(_, args, context, info) { const path = info && info.path && info.path.key; const key = `${path}_${JSON.stringify(args)}`; let cached = _cache.get(context); if (cached && cached[key]) { return cached[key]; } if (!cached) { cached = {}; } const result = resolve(_, args, context, info); cached[key] = result; _cache.set(context, cached); return result; }; }; /** * make 'this' in resolver functions equal to the input bindContext * @param {Object} bind - the object context to bind to resolver functions * @param {Object} resolvers - the resolver map containing the resolver * functions to bind * @returns {Object} - an object identical in structure to the input resolver * map, except with resolver function bound to the input argument bind */ const bindResolvers = function (bindContext, resolvers = {}) { const boundResolvers = {}; for (const [type, fields] of Object.entries(resolvers)) { // dont bind an object that is an instance of a graphql scalar if (fields instanceof graphql_1.GraphQLScalarType) { boundResolvers[type] = fields; continue; } if (!boundResolvers[type]) { boundResolvers[type] = {}; } for (const [field, resolver] of Object.entries(fields)) { if (['Query', 'Mutation'].indexOf(type) > -1) { boundResolvers[type][field] = memoize(type, field, resolver.bind(bindContext)); } else { // only bind resolvers that are functions if (typeof resolver === 'function') { boundResolvers[type][field] = resolver.bind(bindContext); } else { boundResolvers[type][field] = resolver; } } } } return boundResolvers; }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSxtREFBMEQ7QUFDMUQscUNBQStFO0FBRS9FLGdEQUFxRDtBQUNyRCxnREFPOEI7QUFDOUIsa0RBQTZEO0FBQzdELGtEQUFzRDtBQUN0RCw4Q0FBK0Q7QUE4Ri9EOzs7O0dBSUc7QUFDSCxNQUFxQixnQkFBZ0I7SUFDbkMsT0FBTyxDQUFnQjtJQUN2QixNQUFNLENBQWE7SUFDbkIsVUFBVSxDQUFnQztJQUMxQyxNQUFNLENBQW1CO0lBQ3pCLFFBQVEsQ0FBa0M7SUFDMUMsUUFBUSxDQUFrQjtJQUMxQixZQUFZLENBQWdCO0lBQzVCLG9CQUFvQixDQUFnQjtJQUNwQyxZQUFZLENBQVU7SUFDdEIsbUJBQW1CLENBQW9CO0lBQ3ZDLFdBQVcsQ0FBVTtJQUNyQix3QkFBd0IsQ0FBOEI7SUFDdEQsV0FBVyxDQUFnQjtJQUNuQixrQkFBa0IsQ0FBZ0I7SUFDbEMsV0FBVyxHQUFzQixFQUFFLENBQUM7SUFFNUMsWUFBWSxFQUNWLEtBQUssRUFDTCxTQUFTLEVBQ1QsS0FBSyxFQUNMLE9BQU8sRUFDUCxPQUFPLEVBQ1AsV0FBVyxFQUNYLG1CQUFtQixFQUNuQixXQUFXLEVBQ1gsa0JBQWtCLEVBQ2xCLFVBQVUsRUFDVixVQUFVLEVBQ2U7UUFFekIsSUFBSSxDQUFDLE1BQU0sR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUM7UUFFckQsSUFBSSxDQUFDLFVBQVUsR0FBRyxhQUFhLENBQUMsSUFBSSxFQUFFLFNBQVMsQ0FBQyxDQUFDO1FBRWpELElBQUksQ0FBQyxNQUFNLEdBQUcsS0FBSyxDQUFDO1FBRXBCLElBQUksQ0FBQyxXQUFXLEdBQUcsVUFBVSxDQUFDO1FBRTlCLElBQUksQ0FBQyxXQUFXLEdBQUcsVUFB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