UNPKG

18.9 kBJavaScriptView Raw
1"use strict";
2var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3 function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4 return new (P || (P = Promise))(function (resolve, reject) {
5 function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6 function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7 function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8 step((generator = generator.apply(thisArg, _arguments || [])).next());
9 });
10};
11var __importDefault = (this && this.__importDefault) || function (mod) {
12 return (mod && mod.__esModule) ? mod : { "default": mod };
13};
14Object.defineProperty(exports, "__esModule", { value: true });
15exports.processGraphQLRequest = exports.APQ_CACHE_PREFIX = exports.InvalidGraphQLRequestError = void 0;
16const graphql_1 = require("graphql");
17const graphql_extensions_1 = require("graphql-extensions");
18const schemaInstrumentation_1 = require("./utils/schemaInstrumentation");
19const apollo_server_errors_1 = require("apollo-server-errors");
20const apollo_server_types_1 = require("apollo-server-types");
21Object.defineProperty(exports, "InvalidGraphQLRequestError", { enumerable: true, get: function () { return apollo_server_types_1.InvalidGraphQLRequestError; } });
22const dispatcher_1 = require("./utils/dispatcher");
23const apollo_server_caching_1 = require("apollo-server-caching");
24const createSHA_1 = __importDefault(require("./utils/createSHA"));
25const runHttpQuery_1 = require("./runHttpQuery");
26exports.APQ_CACHE_PREFIX = 'apq:';
27function computeQueryHash(query) {
28 return createSHA_1.default('sha256')
29 .update(query)
30 .digest('hex');
31}
32const symbolExtensionDeprecationDone = Symbol("apolloServerExtensionDeprecationDone");
33function processGraphQLRequest(config, requestContext) {
34 var _a;
35 return __awaiter(this, void 0, void 0, function* () {
36 const logger = requestContext.logger || console;
37 const metrics = requestContext.metrics =
38 requestContext.metrics || Object.create(null);
39 const extensionStack = initializeExtensionStack();
40 requestContext.context._extensionStack = extensionStack;
41 const dispatcher = initializeRequestListenerDispatcher();
42 yield initializeDataSources();
43 const request = requestContext.request;
44 let { query, extensions } = request;
45 let queryHash;
46 let persistedQueryCache;
47 metrics.persistedQueryHit = false;
48 metrics.persistedQueryRegister = false;
49 if (extensions && extensions.persistedQuery) {
50 if (!config.persistedQueries || !config.persistedQueries.cache) {
51 return yield emitErrorAndThrow(new apollo_server_errors_1.PersistedQueryNotSupportedError());
52 }
53 else if (extensions.persistedQuery.version !== 1) {
54 return yield emitErrorAndThrow(new apollo_server_types_1.InvalidGraphQLRequestError('Unsupported persisted query version'));
55 }
56 persistedQueryCache = config.persistedQueries.cache;
57 if (!(persistedQueryCache instanceof apollo_server_caching_1.PrefixingKeyValueCache)) {
58 persistedQueryCache = new apollo_server_caching_1.PrefixingKeyValueCache(persistedQueryCache, exports.APQ_CACHE_PREFIX);
59 }
60 queryHash = extensions.persistedQuery.sha256Hash;
61 if (query === undefined) {
62 query = yield persistedQueryCache.get(queryHash);
63 if (query) {
64 metrics.persistedQueryHit = true;
65 }
66 else {
67 return yield emitErrorAndThrow(new apollo_server_errors_1.PersistedQueryNotFoundError());
68 }
69 }
70 else {
71 const computedQueryHash = computeQueryHash(query);
72 if (queryHash !== computedQueryHash) {
73 return yield emitErrorAndThrow(new apollo_server_types_1.InvalidGraphQLRequestError('provided sha does not match query'));
74 }
75 metrics.persistedQueryRegister = true;
76 }
77 }
78 else if (query) {
79 queryHash = computeQueryHash(query);
80 }
81 else {
82 return yield emitErrorAndThrow(new apollo_server_types_1.InvalidGraphQLRequestError('Must provide query string.'));
83 }
84 requestContext.queryHash = queryHash;
85 requestContext.source = query;
86 yield dispatcher.invokeHookAsync('didResolveSource', requestContext);
87 const requestDidEnd = extensionStack.requestDidStart({
88 request: request.http,
89 queryString: request.query,
90 operationName: request.operationName,
91 variables: request.variables,
92 extensions: request.extensions,
93 context: requestContext.context,
94 persistedQueryHit: metrics.persistedQueryHit,
95 persistedQueryRegister: metrics.persistedQueryRegister,
96 requestContext: requestContext,
97 });
98 try {
99 if (config.documentStore) {
100 try {
101 requestContext.document = yield config.documentStore.get(queryHash);
102 }
103 catch (err) {
104 logger.warn('An error occurred while attempting to read from the documentStore. '
105 + (err && err.message) || err);
106 }
107 }
108 if (!requestContext.document) {
109 const parsingDidEnd = yield dispatcher.invokeDidStartHook('parsingDidStart', requestContext);
110 try {
111 requestContext.document = parse(query, config.parseOptions);
112 parsingDidEnd();
113 }
114 catch (syntaxError) {
115 parsingDidEnd(syntaxError);
116 return yield sendErrorResponse(syntaxError, apollo_server_errors_1.SyntaxError);
117 }
118 const validationDidEnd = yield dispatcher.invokeDidStartHook('validationDidStart', requestContext);
119 const validationErrors = validate(requestContext.document);
120 if (validationErrors.length === 0) {
121 validationDidEnd();
122 }
123 else {
124 validationDidEnd(validationErrors);
125 return yield sendErrorResponse(validationErrors, apollo_server_errors_1.ValidationError);
126 }
127 if (config.documentStore) {
128 Promise.resolve(config.documentStore.set(queryHash, requestContext.document)).catch(err => logger.warn('Could not store validated document. ' +
129 (err && err.message) || err));
130 }
131 }
132 const operation = graphql_1.getOperationAST(requestContext.document, request.operationName);
133 requestContext.operation = operation || undefined;
134 requestContext.operationName =
135 (operation && operation.name && operation.name.value) || null;
136 try {
137 yield dispatcher.invokeHookAsync('didResolveOperation', requestContext);
138 }
139 catch (err) {
140 if (err instanceof runHttpQuery_1.HttpQueryError) {
141 const graphqlError = new graphql_1.GraphQLError(err.message);
142 graphqlError.stack = err.stack;
143 yield didEncounterErrors([graphqlError]);
144 throw err;
145 }
146 return yield sendErrorResponse(err);
147 }
148 if (metrics.persistedQueryRegister && persistedQueryCache) {
149 Promise.resolve(persistedQueryCache.set(queryHash, query, config.persistedQueries &&
150 typeof config.persistedQueries.ttl !== 'undefined'
151 ? {
152 ttl: config.persistedQueries.ttl,
153 }
154 : Object.create(null))).catch(logger.warn);
155 }
156 let response = yield dispatcher.invokeHooksUntilNonNull('responseForOperation', requestContext);
157 if (response == null) {
158 const executionListeners = [];
159 dispatcher.invokeHookSync('executionDidStart', requestContext).forEach(executionListener => {
160 if (typeof executionListener === 'function') {
161 executionListeners.push({
162 executionDidEnd: executionListener,
163 });
164 }
165 else if (typeof executionListener === 'object') {
166 executionListeners.push(executionListener);
167 }
168 });
169 const executionDispatcher = new dispatcher_1.Dispatcher(executionListeners);
170 const invokeWillResolveField = (...args) => executionDispatcher.invokeDidStartHook('willResolveField', ...args);
171 Object.defineProperty(requestContext.context, schemaInstrumentation_1.symbolExecutionDispatcherWillResolveField, { value: invokeWillResolveField });
172 if (config.fieldResolver) {
173 Object.defineProperty(requestContext.context, schemaInstrumentation_1.symbolUserFieldResolver, { value: config.fieldResolver });
174 }
175 schemaInstrumentation_1.enablePluginsForSchemaResolvers(config.schema);
176 try {
177 const result = yield execute(requestContext);
178 const resultErrors = (_a = result.errors) === null || _a === void 0 ? void 0 : _a.map((e) => {
179 var _a;
180 if (((_a = e.nodes) === null || _a === void 0 ? void 0 : _a.length) === 1 &&
181 e.nodes[0].kind === graphql_1.Kind.VARIABLE_DEFINITION &&
182 e.message.startsWith(`Variable "$${e.nodes[0].variable.name.value}" got invalid value `)) {
183 return apollo_server_errors_1.fromGraphQLError(e, {
184 errorClass: apollo_server_errors_1.UserInputError,
185 });
186 }
187 return e;
188 });
189 if (resultErrors) {
190 yield didEncounterErrors(resultErrors);
191 }
192 response = Object.assign(Object.assign({}, result), { errors: resultErrors ? formatErrors(resultErrors) : undefined });
193 executionDispatcher.reverseInvokeHookSync('executionDidEnd');
194 }
195 catch (executionError) {
196 executionDispatcher.reverseInvokeHookSync("executionDidEnd", executionError);
197 return yield sendErrorResponse(executionError);
198 }
199 }
200 const formattedExtensions = extensionStack.format();
201 if (Object.keys(formattedExtensions).length > 0) {
202 response.extensions = formattedExtensions;
203 }
204 if (config.formatResponse) {
205 const formattedResponse = config.formatResponse(response, requestContext);
206 if (formattedResponse != null) {
207 response = formattedResponse;
208 }
209 }
210 return sendResponse(response);
211 }
212 finally {
213 requestDidEnd();
214 }
215 function parse(query, parseOptions) {
216 const parsingDidEnd = extensionStack.parsingDidStart({
217 queryString: query,
218 });
219 try {
220 return graphql_1.parse(query, parseOptions);
221 }
222 finally {
223 parsingDidEnd();
224 }
225 }
226 function validate(document) {
227 let rules = graphql_1.specifiedRules;
228 if (config.validationRules) {
229 rules = rules.concat(config.validationRules);
230 }
231 const validationDidEnd = extensionStack.validationDidStart();
232 try {
233 return graphql_1.validate(config.schema, document, rules);
234 }
235 finally {
236 validationDidEnd();
237 }
238 }
239 function execute(requestContext) {
240 return __awaiter(this, void 0, void 0, function* () {
241 const { request, document } = requestContext;
242 const executionArgs = {
243 schema: config.schema,
244 document,
245 rootValue: typeof config.rootValue === 'function'
246 ? config.rootValue(document)
247 : config.rootValue,
248 contextValue: requestContext.context,
249 variableValues: request.variables,
250 operationName: request.operationName,
251 fieldResolver: config.fieldResolver,
252 };
253 const executionDidEnd = extensionStack.executionDidStart({
254 executionArgs,
255 });
256 try {
257 if (config.executor) {
258 return yield config.executor(requestContext);
259 }
260 else {
261 return yield graphql_1.execute(executionArgs);
262 }
263 }
264 finally {
265 executionDidEnd();
266 }
267 });
268 }
269 function sendResponse(response) {
270 return __awaiter(this, void 0, void 0, function* () {
271 requestContext.response = extensionStack.willSendResponse({
272 graphqlResponse: Object.assign(Object.assign({}, requestContext.response), { errors: response.errors, data: response.data, extensions: response.extensions }),
273 context: requestContext.context,
274 }).graphqlResponse;
275 yield dispatcher.invokeHookAsync('willSendResponse', requestContext);
276 return requestContext.response;
277 });
278 }
279 function emitErrorAndThrow(error) {
280 return __awaiter(this, void 0, void 0, function* () {
281 yield didEncounterErrors([error]);
282 throw error;
283 });
284 }
285 function didEncounterErrors(errors) {
286 return __awaiter(this, void 0, void 0, function* () {
287 requestContext.errors = errors;
288 extensionStack.didEncounterErrors(errors);
289 return yield dispatcher.invokeHookAsync('didEncounterErrors', requestContext);
290 });
291 }
292 function sendErrorResponse(errorOrErrors, errorClass) {
293 return __awaiter(this, void 0, void 0, function* () {
294 const errors = Array.isArray(errorOrErrors)
295 ? errorOrErrors
296 : [errorOrErrors];
297 yield didEncounterErrors(errors);
298 return sendResponse({
299 errors: formatErrors(errors.map(err => apollo_server_errors_1.fromGraphQLError(err, errorClass && {
300 errorClass,
301 }))),
302 });
303 });
304 }
305 function formatErrors(errors) {
306 return apollo_server_errors_1.formatApolloErrors(errors, {
307 formatter: config.formatError,
308 debug: requestContext.debug,
309 });
310 }
311 function initializeRequestListenerDispatcher() {
312 const requestListeners = [];
313 if (config.plugins) {
314 for (const plugin of config.plugins) {
315 if (!plugin.requestDidStart)
316 continue;
317 const listener = plugin.requestDidStart(requestContext);
318 if (listener) {
319 requestListeners.push(listener);
320 }
321 }
322 }
323 return new dispatcher_1.Dispatcher(requestListeners);
324 }
325 function initializeExtensionStack() {
326 var _a;
327 if ((_a = config.extensions) === null || _a === void 0 ? void 0 : _a.length) {
328 graphql_extensions_1.enableGraphQLExtensions(config.schema);
329 }
330 const extensions = config.extensions ? config.extensions.map(f => f()) : [];
331 const hasOwn = Object.prototype.hasOwnProperty;
332 extensions.forEach((extension) => {
333 if (!extension.constructor ||
334 hasOwn.call(extension.constructor, symbolExtensionDeprecationDone)) {
335 return;
336 }
337 Object.defineProperty(extension.constructor, symbolExtensionDeprecationDone, { value: true });
338 const extensionName = extension.constructor.name;
339 logger.warn('[deprecated] ' +
340 (extensionName
341 ? 'A "' + extensionName + '" '
342 : 'An anonymous extension ') +
343 'was defined within the "extensions" configuration for ' +
344 'Apollo Server. The API on which this extension is built ' +
345 '("graphql-extensions") is being deprecated in the next major ' +
346 'version of Apollo Server in favor of the new plugin API. See ' +
347 'https://go.apollo.dev/s/plugins for the documentation on how ' +
348 'these plugins are to be defined and used.');
349 });
350 return new graphql_extensions_1.GraphQLExtensionStack(extensions);
351 }
352 function initializeDataSources() {
353 return __awaiter(this, void 0, void 0, function* () {
354 if (config.dataSources) {
355 const context = requestContext.context;
356 const dataSources = config.dataSources();
357 const initializers = [];
358 for (const dataSource of Object.values(dataSources)) {
359 if (dataSource.initialize) {
360 initializers.push(dataSource.initialize({
361 context,
362 cache: requestContext.cache,
363 }));
364 }
365 }
366 yield Promise.all(initializers);
367 if ('dataSources' in context) {
368 throw new Error('Please use the dataSources config option instead of putting dataSources on the context yourself.');
369 }
370 context.dataSources = dataSources;
371 }
372 });
373 }
374 });
375}
376exports.processGraphQLRequest = processGraphQLRequest;
377//# sourceMappingURL=requestPipeline.js.map
\No newline at end of file