1 | "use strict";
|
2 | var ResolversExplorerService_1;
|
3 | Object.defineProperty(exports, "__esModule", { value: true });
|
4 | exports.ResolversExplorerService = void 0;
|
5 | const tslib_1 = require("tslib");
|
6 | const common_1 = require("@nestjs/common");
|
7 | const shared_utils_1 = require("@nestjs/common/utils/shared.utils");
|
8 | const core_1 = require("@nestjs/core");
|
9 | const external_context_creator_1 = require("@nestjs/core/helpers/external-context-creator");
|
10 | const constants_1 = require("@nestjs/core/injector/constants");
|
11 | const injector_1 = require("@nestjs/core/injector/injector");
|
12 | const internal_core_module_1 = require("@nestjs/core/injector/internal-core-module");
|
13 | const serialized_graph_1 = require("@nestjs/core/inspector/serialized-graph");
|
14 | const request_constants_1 = require("@nestjs/core/router/request/request-constants");
|
15 | const lodash_1 = require("lodash");
|
16 | const abstract_graphql_driver_1 = require("../drivers/abstract-graphql.driver");
|
17 | const gql_paramtype_enum_1 = require("../enums/gql-paramtype.enum");
|
18 | const resolver_enum_1 = require("../enums/resolver.enum");
|
19 | const params_factory_1 = require("../factories/params.factory");
|
20 | const graphql_constants_1 = require("../graphql.constants");
|
21 | const decorate_field_resolver_util_1 = require("../utils/decorate-field-resolver.util");
|
22 | const extract_metadata_util_1 = require("../utils/extract-metadata.util");
|
23 | const base_explorer_service_1 = require("./base-explorer.service");
|
24 | let ResolversExplorerService = ResolversExplorerService_1 = class ResolversExplorerService extends base_explorer_service_1.BaseExplorerService {
|
25 | constructor(modulesContainer, metadataScanner, externalContextCreator, gqlOptions, moduleRef, serializedGraph) {
|
26 | super();
|
27 | this.modulesContainer = modulesContainer;
|
28 | this.metadataScanner = metadataScanner;
|
29 | this.externalContextCreator = externalContextCreator;
|
30 | this.gqlOptions = gqlOptions;
|
31 | this.moduleRef = moduleRef;
|
32 | this.serializedGraph = serializedGraph;
|
33 | this.logger = new common_1.Logger(ResolversExplorerService_1.name);
|
34 | this.gqlParamsFactory = new params_factory_1.GqlParamsFactory();
|
35 | this.injector = new injector_1.Injector();
|
36 | }
|
37 | explore() {
|
38 | const modules = this.getModules(this.modulesContainer, this.gqlOptions.include || []);
|
39 | const gqlAdapter = this.moduleRef.get(abstract_graphql_driver_1.AbstractGraphQLDriver);
|
40 | const resolvers = this.flatMap(modules, (instance, moduleRef) => this.filterResolvers(gqlAdapter, instance, moduleRef));
|
41 | return this.groupMetadata(resolvers);
|
42 | }
|
43 | filterResolvers(gqlAdapter, wrapper, moduleRef) {
|
44 | const { instance } = wrapper;
|
45 | if (!instance) {
|
46 | return undefined;
|
47 | }
|
48 | const prototype = Object.getPrototypeOf(instance);
|
49 | const predicate = (resolverType, isReferenceResolver, isPropertyResolver) => (0, shared_utils_1.isUndefined)(resolverType) ||
|
50 | (!isReferenceResolver &&
|
51 | !isPropertyResolver &&
|
52 | ![resolver_enum_1.Resolver.MUTATION, resolver_enum_1.Resolver.QUERY, resolver_enum_1.Resolver.SUBSCRIPTION].some((type) => type === resolverType));
|
53 | const resolvers = this.metadataScanner
|
54 | .getAllMethodNames(prototype)
|
55 | .map((name) => (0, extract_metadata_util_1.extractMetadata)(instance, prototype, name, predicate))
|
56 | .filter((resolver) => !!resolver);
|
57 | const isRequestScoped = !wrapper.isDependencyTreeStatic();
|
58 | return resolvers.map((resolver) => {
|
59 | this.assignResolverConstructorUniqueId(instance.constructor, moduleRef);
|
60 | const entrypointDefinition = {
|
61 | id: `${wrapper.id}_${resolver.methodName}`,
|
62 | type: 'graphql-entrypoint',
|
63 | methodName: resolver.methodName,
|
64 | className: wrapper.name,
|
65 | classNodeId: wrapper.id,
|
66 | metadata: {
|
67 | key: resolver.name,
|
68 | parentType: resolver.type,
|
69 | },
|
70 | };
|
71 | this.serializedGraph.insertEntrypoint(entrypointDefinition, wrapper.id);
|
72 | const createContext = (transform) => this.createContextCallback(instance, prototype, wrapper, moduleRef, resolver, isRequestScoped, transform);
|
73 | if (resolver.type === graphql_constants_1.SUBSCRIPTION_TYPE) {
|
74 | if (!wrapper.isDependencyTreeStatic()) {
|
75 |
|
76 |
|
77 | this.logger.error(`"${wrapper.metatype.name}" resolver is request or transient-scoped. Resolvers that register subscriptions with the "@Subscription()" decorator must be static (singleton).`);
|
78 | }
|
79 | const subscriptionOptions = Reflect.getMetadata(graphql_constants_1.SUBSCRIPTION_OPTIONS_METADATA, instance[resolver.methodName]);
|
80 | return this.createSubscriptionMetadata(gqlAdapter, createContext, subscriptionOptions, resolver, instance);
|
81 | }
|
82 | return {
|
83 | ...resolver,
|
84 | callback: createContext(),
|
85 | };
|
86 | });
|
87 | }
|
88 | createContextCallback(instance, prototype, wrapper, moduleRef, resolver, isRequestScoped, transform = lodash_1.identity) {
|
89 | const paramsFactory = this.gqlParamsFactory;
|
90 | const isPropertyResolver = ![
|
91 | resolver_enum_1.Resolver.MUTATION,
|
92 | resolver_enum_1.Resolver.QUERY,
|
93 | resolver_enum_1.Resolver.SUBSCRIPTION,
|
94 | ].some((type) => type === resolver.type);
|
95 | const fieldResolverEnhancers = this.gqlOptions.fieldResolverEnhancers || [];
|
96 | const contextOptions = resolver.methodName === graphql_constants_1.FIELD_TYPENAME
|
97 | ? { guards: false, filters: false, interceptors: false }
|
98 | : isPropertyResolver
|
99 | ? {
|
100 | guards: fieldResolverEnhancers.includes('guards'),
|
101 | filters: fieldResolverEnhancers.includes('filters'),
|
102 | interceptors: fieldResolverEnhancers.includes('interceptors'),
|
103 | }
|
104 | : undefined;
|
105 | if (isRequestScoped) {
|
106 | const resolverCallback = async (...args) => {
|
107 | const gqlContext = paramsFactory.exchangeKeyForValue(gql_paramtype_enum_1.GqlParamtype.CONTEXT, undefined, args);
|
108 | const contextId = this.getContextId(gqlContext);
|
109 | this.registerContextProvider(gqlContext, contextId);
|
110 | const contextInstance = await this.injector.loadPerContext(instance, moduleRef, moduleRef.providers, contextId);
|
111 | const callback = this.externalContextCreator.create(contextInstance, transform(contextInstance[resolver.methodName]), resolver.methodName, graphql_constants_1.PARAM_ARGS_METADATA, paramsFactory, contextId, wrapper.id, contextOptions, 'graphql');
|
112 | return callback(...args);
|
113 | };
|
114 | return isPropertyResolver
|
115 | ? this.registerFieldMiddlewareIfExists(resolverCallback, instance, resolver.methodName)
|
116 | : resolverCallback;
|
117 | }
|
118 | const resolverCallback = this.externalContextCreator.create(instance, prototype[resolver.methodName], resolver.methodName, graphql_constants_1.PARAM_ARGS_METADATA, paramsFactory, undefined, undefined, contextOptions, 'graphql');
|
119 | return isPropertyResolver
|
120 | ? this.registerFieldMiddlewareIfExists(resolverCallback, instance, resolver.methodName)
|
121 | : resolverCallback;
|
122 | }
|
123 | createSubscriptionMetadata(gqlAdapter, createSubscribeContext, subscriptionOptions, resolverMetadata, instanceRef) {
|
124 | const resolveFunc = subscriptionOptions &&
|
125 | subscriptionOptions.resolve &&
|
126 | subscriptionOptions.resolve.bind(instanceRef);
|
127 | const baseCallbackMetadata = {
|
128 | resolve: resolveFunc,
|
129 | };
|
130 | if (subscriptionOptions && subscriptionOptions.filter) {
|
131 | return {
|
132 | ...resolverMetadata,
|
133 | callback: {
|
134 | ...baseCallbackMetadata,
|
135 | subscribe: gqlAdapter.subscriptionWithFilter(instanceRef, subscriptionOptions.filter, createSubscribeContext),
|
136 | },
|
137 | };
|
138 | }
|
139 | return {
|
140 | ...resolverMetadata,
|
141 | callback: {
|
142 | ...baseCallbackMetadata,
|
143 | subscribe: createSubscribeContext(),
|
144 | },
|
145 | };
|
146 | }
|
147 | getAllCtors() {
|
148 | const modules = this.getModules(this.modulesContainer, this.gqlOptions.include || []);
|
149 | const resolvers = this.flatMap(modules, this.mapToCtor).filter(Boolean);
|
150 | return resolvers;
|
151 | }
|
152 | mapToCtor(wrapper) {
|
153 | const { instance } = wrapper;
|
154 | if (!instance) {
|
155 | return undefined;
|
156 | }
|
157 | return instance.constructor;
|
158 | }
|
159 | registerContextProvider(request, contextId) {
|
160 | const coreModuleArray = [...this.modulesContainer.entries()]
|
161 | .filter(([key, { metatype }]) => metatype && metatype.name === internal_core_module_1.InternalCoreModule.name)
|
162 | .map(([key, value]) => value);
|
163 | const coreModuleRef = (0, lodash_1.head)(coreModuleArray);
|
164 | if (!coreModuleRef) {
|
165 | return;
|
166 | }
|
167 | const wrapper = coreModuleRef.getProviderByKey(core_1.REQUEST);
|
168 | wrapper.setInstanceByContextId(contextId, {
|
169 | instance: contextId.getParent ? contextId.payload : request,
|
170 | isResolved: true,
|
171 | });
|
172 | }
|
173 | registerFieldMiddlewareIfExists(resolverFn, instance, methodKey) {
|
174 | const fieldMiddleware = Reflect.getMetadata(graphql_constants_1.FIELD_RESOLVER_MIDDLEWARE_METADATA, instance[methodKey]);
|
175 | const middlewareFunctions = (this.gqlOptions?.buildSchemaOptions?.fieldMiddleware || []).concat(fieldMiddleware || []);
|
176 | if (middlewareFunctions?.length === 0) {
|
177 | return resolverFn;
|
178 | }
|
179 | const originalResolveFnFactory = (...args) => () => resolverFn(...args);
|
180 | return (0, decorate_field_resolver_util_1.decorateFieldResolverWithMiddleware)(originalResolveFnFactory, middlewareFunctions);
|
181 | }
|
182 | getContextId(gqlContext) {
|
183 | const contextId = core_1.ContextIdFactory.getByRequest(gqlContext, ['req']);
|
184 | if (!gqlContext[request_constants_1.REQUEST_CONTEXT_ID]) {
|
185 | Object.defineProperty(gqlContext, request_constants_1.REQUEST_CONTEXT_ID, {
|
186 | value: contextId,
|
187 | enumerable: false,
|
188 | configurable: false,
|
189 | writable: false,
|
190 | });
|
191 | }
|
192 | return contextId;
|
193 | }
|
194 | assignResolverConstructorUniqueId(resolverConstructor, moduleRef) {
|
195 | if (resolverConstructor.hasOwnProperty(constants_1.CONTROLLER_ID_KEY)) {
|
196 | return;
|
197 | }
|
198 | moduleRef.assignControllerUniqueId(resolverConstructor);
|
199 | }
|
200 | };
|
201 | exports.ResolversExplorerService = ResolversExplorerService;
|
202 | exports.ResolversExplorerService = ResolversExplorerService = ResolversExplorerService_1 = tslib_1.__decorate([
|
203 | (0, common_1.Injectable)(),
|
204 | tslib_1.__param(3, (0, common_1.Inject)(graphql_constants_1.GRAPHQL_MODULE_OPTIONS)),
|
205 | tslib_1.__metadata("design:paramtypes", [core_1.ModulesContainer,
|
206 | core_1.MetadataScanner,
|
207 | external_context_creator_1.ExternalContextCreator, Object, core_1.ModuleRef,
|
208 | serialized_graph_1.SerializedGraph])
|
209 | ], ResolversExplorerService);
|