text: "use strict";
  var __importDefault = (this && this.__importDefault) || function (mod) {
      return (mod && mod.__esModule) ? mod : { "default": mod };
  };
  Object.defineProperty(exports, "__esModule", { value: true });
  exports.processGraphQLRequest = exports.APQ_CACHE_PREFIX = void 0;
  const graphql_1 = require("graphql");
  const schemaInstrumentation_1 = require("./utils/schemaInstrumentation");
  const apollo_server_errors_1 = require("apollo-server-errors");
  const dispatcher_1 = require("./utils/dispatcher");
  const apollo_server_caching_1 = require("apollo-server-caching");
  const createSHA_1 = __importDefault(require("./utils/createSHA"));
  const runHttpQuery_1 = require("./runHttpQuery");
  const apollo_server_env_1 = require("apollo-server-env");
  exports.APQ_CACHE_PREFIX = 'apq:';
  function computeQueryHash(query) {
      return createSHA_1.default('sha256').update(query).digest('hex');
  }
  async function processGraphQLRequest(config, requestContext) {
      var _a;
      const logger = requestContext.logger || console;
      const metrics = (requestContext.metrics =
          requestContext.metrics || Object.create(null));
      const dispatcher = await initializeRequestListenerDispatcher();
      await initializeDataSources();
      const request = requestContext.request;
      let { query, extensions } = request;
      let queryHash;
      let persistedQueryCache;
      metrics.persistedQueryHit = false;
      metrics.persistedQueryRegister = false;
      if (extensions && extensions.persistedQuery) {
          if (!config.persistedQueries || !config.persistedQueries.cache) {
              return await sendErrorResponse(new apollo_server_errors_1.PersistedQueryNotSupportedError());
          }
          else if (extensions.persistedQuery.version !== 1) {
              return await sendErrorResponse(new graphql_1.GraphQLError('Unsupported persisted query version'));
          }
          persistedQueryCache = config.persistedQueries.cache;
          if (!(persistedQueryCache instanceof apollo_server_caching_1.PrefixingKeyValueCache)) {
              persistedQueryCache = new apollo_server_caching_1.PrefixingKeyValueCache(persistedQueryCache, exports.APQ_CACHE_PREFIX);
          }
          queryHash = extensions.persistedQuery.sha256Hash;
          if (query === undefined) {
              query = await persistedQueryCache.get(queryHash);
              if (query) {
                  metrics.persistedQueryHit = true;
              }
              else {
                  return await sendErrorResponse(new apollo_server_errors_1.PersistedQueryNotFoundError());
              }
          }
          else {
              const computedQueryHash = computeQueryHash(query);
              if (queryHash !== computedQueryHash) {
                  return await sendErrorResponse(new graphql_1.GraphQLError('provided sha does not match query'));
              }
              metrics.persistedQueryRegister = true;
          }
      }
      else if (query) {
          queryHash = computeQueryHash(query);
      }
      else {
          return await sendErrorResponse(new graphql_1.GraphQLError('GraphQL operations must contain a non-empty `query` or a `persistedQuery` extension.'));
      }
      requestContext.queryHash = queryHash;
      requestContext.source = query;
      await dispatcher.invokeHook('didResolveSource', requestContext);
      if (config.documentStore) {
          try {
              requestContext.document = await config.documentStore.get(queryHash);
          }
          catch (err) {
              logger.warn('An error occurred while attempting to read from the documentStore. ' +
                  (err && err.message) || err);
          }
      }
      if (!requestContext.document) {
          const parsingDidEnd = await dispatcher.invokeDidStartHook('parsingDidStart', requestContext);
          try {
              requestContext.document = parse(query, config.parseOptions);
              parsingDidEnd();
          }
          catch (syntaxError) {
              await parsingDidEnd(syntaxError);
              return await sendErrorResponse(syntaxError, apollo_server_errors_1.SyntaxError);
          }
          const validationDidEnd = await dispatcher.invokeDidStartHook('validationDidStart', requestContext);
          const validationErrors = validate(requestContext.document);
          if (validationErrors.length === 0) {
              await validationDidEnd();
          }
          else {
              await validationDidEnd(validationErrors);
              return await sendErrorResponse(validationErrors, apollo_server_errors_1.ValidationError);
          }
          if (config.documentStore) {
              Promise.resolve(config.documentStore.set(queryHash, requestContext.document)).catch((err) => logger.warn('Could not store validated document. ' + (err && err.message) || err));
          }
      }
      const operation = graphql_1.getOperationAST(requestContext.document, request.operationName);
      requestContext.operation = operation || undefined;
      requestContext.operationName =
          (operation && operation.name && operation.name.value) || null;
      try {
          await dispatcher.invokeHook('didResolveOperation', requestContext);
      }
      catch (err) {
          return await sendErrorResponse(err);
      }
      if (metrics.persistedQueryRegister && persistedQueryCache) {
          Promise.resolve(persistedQueryCache.set(queryHash, query, config.persistedQueries &&
              typeof config.persistedQueries.ttl !== 'undefined'
              ? {
                  ttl: config.persistedQueries.ttl,
              }
              : Object.create(null))).catch(logger.warn);
      }
      let response = await dispatcher.invokeHooksUntilNonNull('responseForOperation', requestContext);
      if (response == null) {
          const executionListeners = [];
          (await dispatcher.invokeHook('executionDidStart', requestContext)).forEach((executionListener) => {
              if (executionListener) {
                  executionListeners.push(executionListener);
              }
          });
          executionListeners.reverse();
          const executionDispatcher = new dispatcher_1.Dispatcher(executionListeners);
          const invokeWillResolveField = (...args) => executionDispatcher.invokeSyncDidStartHook('willResolveField', ...args);
          Object.defineProperty(requestContext.context, schemaInstrumentation_1.symbolExecutionDispatcherWillResolveField, { value: invokeWillResolveField });
          if (config.fieldResolver) {
              Object.defineProperty(requestContext.context, schemaInstrumentation_1.symbolUserFieldResolver, {
                  value: config.fieldResolver,
              });
          }
          schemaInstrumentation_1.enablePluginsForSchemaResolvers(config.schema);
          try {
              const result = await execute(requestContext);
              const resultErrors = (_a = result.errors) === null || _a === void 0 ? void 0 : _a.map((e) => {
                  var _a;
                  if (((_a = e.nodes) === null || _a === void 0 ? void 0 : _a.length) === 1 &&
                      e.nodes[0].kind === graphql_1.Kind.VARIABLE_DEFINITION &&
                      (e.message.startsWith(`Variable "$${e.nodes[0].variable.name.value}" got invalid value `) ||
                          (e.nodes[0].type.kind === graphql_1.Kind.NON_NULL_TYPE &&
                              e.nodes[0].type.type.kind === graphql_1.Kind.NAMED_TYPE &&
                              (e.message.startsWith(`Variable "$${e.nodes[0].variable.name.value}" of required ` +
                                  `type "${e.nodes[0].type.type.name.value}!" was not provided.`) ||
                                  e.message.startsWith(`Variable "$${e.nodes[0].variable.name.value}" of non-null ` +
                                      `type "${e.nodes[0].type.type.name.value}!" must not be null.`))))) {
                      return apollo_server_errors_1.fromGraphQLError(e, {
                          errorClass: apollo_server_errors_1.UserInputError,
                      });
                  }
                  return e;
              });
              if (resultErrors) {
                  await didEncounterErrors(resultErrors);
              }
              response = {
                  ...result,
                  errors: resultErrors ? formatErrors(resultErrors) : undefined,
              };
              await executionDispatcher.invokeHook('executionDidEnd');
          }
          catch (executionError) {
              await executionDispatcher.invokeHook('executionDidEnd', executionError);
              return await sendErrorResponse(executionError);
          }
      }
      if (config.formatResponse) {
          const formattedResponse = config.formatResponse(response, requestContext);
          if (formattedResponse != null) {
              response = formattedResponse;
          }
      }
      return sendResponse(response);
      function parse(query, parseOptions) {
          return graphql_1.parse(query, parseOptions);
      }
      function validate(document) {
          let rules = graphql_1.specifiedRules;
          if (config.validationRules) {
              rules = rules.concat(config.validationRules);
          }
          return graphql_1.validate(config.schema, document, rules);
      }
      async function execute(requestContext) {
          const { request, document } = requestContext;
          const executionArgs = {
              schema: config.schema,
              document,
              rootValue: typeof config.rootValue === 'function'
                  ? config.rootValue(document)
                  : config.rootValue,
              contextValue: requestContext.context,
              variableValues: request.variables,
              operationName: request.operationName,
              fieldResolver: config.fieldResolver,
          };
          if (config.executor) {
              return await config.executor(requestContext);
          }
          else {
              return await graphql_1.execute(executionArgs);
          }
      }
      async function sendResponse(response) {
          requestContext.response = {
              ...requestContext.response,
              errors: response.errors,
              data: response.data,
              extensions: response.extensions,
          };
          if (response.http) {
              if (!requestContext.response.http) {
                  requestContext.response.http = {
                      headers: new apollo_server_env_1.Headers(),
                  };
              }
              if (response.http.status) {
                  requestContext.response.http.status = response.http.status;
              }
              for (const [name, value] of response.http.headers) {
                  requestContext.response.http.headers.set(name, value);
              }
          }
          await dispatcher.invokeHook('willSendResponse', requestContext);
          return requestContext.response;
      }
      async function didEncounterErrors(errors) {
          requestContext.errors = errors;
          return await dispatcher.invokeHook('didEncounterErrors', requestContext);
      }
      async function sendErrorResponse(errorOrErrors, errorClass) {
          const errors = Array.isArray(errorOrErrors)
              ? errorOrErrors
              : [errorOrErrors];
          await didEncounterErrors(errors);
          const response = {
              errors: formatErrors(errors.map((err) => err instanceof apollo_server_errors_1.ApolloError && !errorClass
                  ? err
                  : apollo_server_errors_1.fromGraphQLError(err, errorClass && {
                      errorClass,
                  }))),
          };
          if (errors.every((err) => err instanceof apollo_server_errors_1.PersistedQueryNotSupportedError ||
              err instanceof apollo_server_errors_1.PersistedQueryNotFoundError)) {
              response.http = {
                  status: 200,
                  headers: new apollo_server_env_1.Headers({
                      'Cache-Control': 'private, no-cache, must-revalidate',
                  }),
              };
          }
          else if (errors.length === 1 && errors[0] instanceof runHttpQuery_1.HttpQueryError) {
              response.http = {
                  status: errors[0].statusCode,
                  headers: new apollo_server_env_1.Headers(errors[0].headers),
              };
          }
          return sendResponse(response);
      }
      function formatErrors(errors) {
          return apollo_server_errors_1.formatApolloErrors(errors, {
              formatter: config.formatError,
              debug: requestContext.debug,
          });
      }
      async function initializeRequestListenerDispatcher() {
          const requestListeners = [];
          if (config.plugins) {
              for (const plugin of config.plugins) {
                  if (!plugin.requestDidStart)
                      continue;
                  const listener = await plugin.requestDidStart(requestContext);
                  if (listener) {
                      requestListeners.push(listener);
                  }
              }
          }
          return new dispatcher_1.Dispatcher(requestListeners);
      }
      async function initializeDataSources() {
          if (config.dataSources) {
              const context = requestContext.context;
              const dataSources = config.dataSources();
              const initializers = [];
              for (const dataSource of Object.values(dataSources)) {
                  if (dataSource.initialize) {
                      initializers.push(dataSource.initialize({
                          context,
                          cache: requestContext.cache,
                      }));
                  }
              }
              await Promise.all(initializers);
              if ('dataSources' in context) {
                  throw new Error('Please use the dataSources config option instead of putting dataSources on the context yourself.');
              }
              context.dataSources = dataSources;
          }
      }
  }
  exports.processGraphQLRequest = processGraphQLRequest;
  //# sourceMappingURL=requestPipeline.js.map