UNPKG

@nestjs/graphql

Version:

Nest - modern, fast, powerful node.js web framework (@graphql)

211 lines (210 loc) 11.5 kB
"use strict"; var ResolversExplorerService_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.ResolversExplorerService = void 0; const tslib_1 = require("tslib"); const common_1 = require("@nestjs/common"); const shared_utils_1 = require("@nestjs/common/utils/shared.utils"); const core_1 = require("@nestjs/core"); const external_context_creator_1 = require("@nestjs/core/helpers/external-context-creator"); const constants_1 = require("@nestjs/core/injector/constants"); const injector_1 = require("@nestjs/core/injector/injector"); const internal_core_module_1 = require("@nestjs/core/injector/internal-core-module"); const serialized_graph_1 = require("@nestjs/core/inspector/serialized-graph"); const request_constants_1 = require("@nestjs/core/router/request/request-constants"); const lodash_1 = require("lodash"); const abstract_graphql_driver_1 = require("../drivers/abstract-graphql.driver"); const gql_paramtype_enum_1 = require("../enums/gql-paramtype.enum"); const resolver_enum_1 = require("../enums/resolver.enum"); const params_factory_1 = require("../factories/params.factory"); const graphql_constants_1 = require("../graphql.constants"); const decorate_field_resolver_util_1 = require("../utils/decorate-field-resolver.util"); const extract_metadata_util_1 = require("../utils/extract-metadata.util"); const base_explorer_service_1 = require("./base-explorer.service"); let ResolversExplorerService = ResolversExplorerService_1 = class ResolversExplorerService extends base_explorer_service_1.BaseExplorerService { constructor(modulesContainer, metadataScanner, externalContextCreator, gqlOptions, moduleRef, serializedGraph) { super(); this.modulesContainer = modulesContainer; this.metadataScanner = metadataScanner; this.externalContextCreator = externalContextCreator; this.gqlOptions = gqlOptions; this.moduleRef = moduleRef; this.serializedGraph = serializedGraph; this.logger = new common_1.Logger(ResolversExplorerService_1.name); this.gqlParamsFactory = new params_factory_1.GqlParamsFactory(); this.injector = new injector_1.Injector(); } explore() { const modules = this.getModules(this.modulesContainer, this.gqlOptions.include || []); const gqlAdapter = this.moduleRef.get(abstract_graphql_driver_1.AbstractGraphQLDriver); const resolvers = this.flatMap(modules, (instance, moduleRef) => this.filterResolvers(gqlAdapter, instance, moduleRef)); return this.groupMetadata(resolvers); } filterResolvers(gqlAdapter, wrapper, moduleRef) { const { instance } = wrapper; if (!instance) { return undefined; } const prototype = Object.getPrototypeOf(instance); const predicate = (resolverType, isReferenceResolver, isPropertyResolver) => (0, shared_utils_1.isUndefined)(resolverType) || (!isReferenceResolver && !isPropertyResolver && ![resolver_enum_1.Resolver.MUTATION, resolver_enum_1.Resolver.QUERY, resolver_enum_1.Resolver.SUBSCRIPTION].some((type) => type === resolverType)); const resolvers = this.metadataScanner .getAllMethodNames(prototype) .map((name) => (0, extract_metadata_util_1.extractMetadata)(instance, prototype, name, predicate)) .filter((resolver) => !!resolver); const isRequestScoped = !wrapper.isDependencyTreeStatic(); return resolvers.map((resolver) => { this.assignResolverConstructorUniqueId(instance.constructor, moduleRef); const entrypointDefinition = { id: `${wrapper.id}_${resolver.methodName}`, type: 'graphql-entrypoint', methodName: resolver.methodName, className: wrapper.name, classNodeId: wrapper.id, metadata: { key: resolver.name, parentType: resolver.type, }, }; this.serializedGraph.insertEntrypoint(entrypointDefinition, wrapper.id); const createContext = (transform) => this.createContextCallback(instance, prototype, wrapper, moduleRef, resolver, isRequestScoped, transform); if (resolver.type === graphql_constants_1.SUBSCRIPTION_TYPE) { if (!wrapper.isDependencyTreeStatic()) { // Note: We don't throw an exception here for backward // compatibility reasons. this.logger.error(`"${wrapper.metatype.name}" resolver is request or transient-scoped. Resolvers that register subscriptions with the "@Subscription()" decorator must be static (singleton).`); } const subscriptionOptions = Reflect.getMetadata(graphql_constants_1.SUBSCRIPTION_OPTIONS_METADATA, instance[resolver.methodName]); return this.createSubscriptionMetadata(gqlAdapter, createContext, subscriptionOptions, resolver, instance); } return { ...resolver, callback: createContext(), }; }); } createContextCallback(instance, prototype, wrapper, moduleRef, resolver, isRequestScoped, transform = lodash_1.identity) { const paramsFactory = this.gqlParamsFactory; const isPropertyResolver = ![ resolver_enum_1.Resolver.MUTATION, resolver_enum_1.Resolver.QUERY, resolver_enum_1.Resolver.SUBSCRIPTION, ].some((type) => type === resolver.type); const fieldResolverEnhancers = this.gqlOptions.fieldResolverEnhancers || []; const contextOptions = resolver.methodName === graphql_constants_1.FIELD_TYPENAME ? { guards: false, filters: false, interceptors: false } : isPropertyResolver ? { guards: fieldResolverEnhancers.includes('guards'), filters: fieldResolverEnhancers.includes('filters'), interceptors: fieldResolverEnhancers.includes('interceptors'), } : undefined; if (isRequestScoped) { const resolverCallback = async (...args) => { const gqlContext = paramsFactory.exchangeKeyForValue(gql_paramtype_enum_1.GqlParamtype.CONTEXT, undefined, args); const contextId = this.getContextId(gqlContext); this.registerContextProvider(gqlContext, contextId); const contextInstance = await this.injector.loadPerContext(instance, moduleRef, moduleRef.providers, contextId); const callback = this.externalContextCreator.create(contextInstance, transform(contextInstance[resolver.methodName]), resolver.methodName, graphql_constants_1.PARAM_ARGS_METADATA, paramsFactory, contextId, wrapper.id, contextOptions, 'graphql'); return callback(...args); }; return isPropertyResolver ? this.registerFieldMiddlewareIfExists(resolverCallback, instance, resolver.methodName) : resolverCallback; } const resolverCallback = this.externalContextCreator.create(instance, prototype[resolver.methodName], resolver.methodName, graphql_constants_1.PARAM_ARGS_METADATA, paramsFactory, undefined, undefined, contextOptions, 'graphql'); return isPropertyResolver ? this.registerFieldMiddlewareIfExists(resolverCallback, instance, resolver.methodName) : resolverCallback; } createSubscriptionMetadata(gqlAdapter, createSubscribeContext, subscriptionOptions, resolverMetadata, instanceRef) { const resolveFunc = subscriptionOptions && subscriptionOptions.resolve && subscriptionOptions.resolve.bind(instanceRef); const baseCallbackMetadata = { resolve: resolveFunc, }; if (subscriptionOptions && subscriptionOptions.filter) { return { ...resolverMetadata, callback: { ...baseCallbackMetadata, subscribe: gqlAdapter.subscriptionWithFilter(instanceRef, subscriptionOptions.filter, createSubscribeContext), }, }; } return { ...resolverMetadata, callback: { ...baseCallbackMetadata, subscribe: createSubscribeContext(), }, }; } getAllCtors() { const modules = this.getModules(this.modulesContainer, this.gqlOptions.include || []); const resolvers = this.flatMap(modules, this.mapToCtor).filter(Boolean); return resolvers; } mapToCtor(wrapper) { const { instance } = wrapper; if (!instance) { return undefined; } return instance.constructor; } registerContextProvider(request, contextId) { const coreModuleArray = [...this.modulesContainer.entries()] .filter(([key, { metatype }]) => metatype && metatype.name === internal_core_module_1.InternalCoreModule.name) .map(([key, value]) => value); const coreModuleRef = (0, lodash_1.head)(coreModuleArray); if (!coreModuleRef) { return; } const wrapper = coreModuleRef.getProviderByKey(core_1.REQUEST); wrapper.setInstanceByContextId(contextId, { instance: contextId.getParent ? contextId.payload : request, isResolved: true, }); } registerFieldMiddlewareIfExists(resolverFn, instance, methodKey) { const fieldMiddleware = Reflect.getMetadata(graphql_constants_1.FIELD_RESOLVER_MIDDLEWARE_METADATA, instance[methodKey]); const middlewareFunctions = (this.gqlOptions?.buildSchemaOptions?.fieldMiddleware || []).concat(fieldMiddleware || []); if (middlewareFunctions?.length === 0) { return resolverFn; } const originalResolveFnFactory = (...args) => () => resolverFn(...args); return (0, decorate_field_resolver_util_1.decorateFieldResolverWithMiddleware)(originalResolveFnFactory, middlewareFunctions); } getContextId(gqlContext) { const contextId = core_1.ContextIdFactory.getByRequest(gqlContext, ['req']); if (!gqlContext[request_constants_1.REQUEST_CONTEXT_ID]) { Object.defineProperty(gqlContext, request_constants_1.REQUEST_CONTEXT_ID, { value: contextId, enumerable: false, configurable: false, writable: false, }); } return contextId; } assignResolverConstructorUniqueId(resolverConstructor, moduleRef) { // eslint-disable-next-line no-prototype-builtins if (resolverConstructor.hasOwnProperty(constants_1.CONTROLLER_ID_KEY)) { return; } moduleRef.assignControllerUniqueId(resolverConstructor); } }; exports.ResolversExplorerService = ResolversExplorerService; exports.ResolversExplorerService = ResolversExplorerService = ResolversExplorerService_1 = tslib_1.__decorate([ (0, common_1.Injectable)(), tslib_1.__param(3, (0, common_1.Inject)(graphql_constants_1.GRAPHQL_MODULE_OPTIONS)), tslib_1.__metadata("design:paramtypes", [core_1.ModulesContainer, core_1.MetadataScanner, external_context_creator_1.ExternalContextCreator, Object, core_1.ModuleRef, serialized_graph_1.SerializedGraph]) ], ResolversExplorerService);