"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

exports.__esModule = true;
exports.queryRunner = queryRunner;

var _isEmpty2 = _interopRequireDefault(require("lodash/isEmpty"));

var _fsExtra = _interopRequireDefault(require("fs-extra"));

var _reporter = _interopRequireDefault(require("gatsby-cli/lib/reporter"));

var _crypto = _interopRequireDefault(require("crypto"));

var _path = _interopRequireDefault(require("path"));

var _redux = require("../redux");

var _actions = require("../redux/actions");

var _graphqlErrorsCodeframe = require("./graphql-errors-codeframe");

var _errorParser = _interopRequireDefault(require("./error-parser"));

var _pageData = require("../utils/page-data");

var _cacheLmdb = _interopRequireDefault(require("../utils/cache-lmdb"));

let resultHashCache;

function getResultHashCache() {
  if (!resultHashCache) {
    resultHashCache = new _cacheLmdb.default({
      name: `query-result-hashes`,
      encoding: `string`
    }).init();
  }

  return resultHashCache;
}

function reportLongRunningQueryJob(queryJob) {
  const messageParts = [`This query took more than 15s to run — which is unusually long and might indicate you're querying too much or have some unoptimized code:`, `File path: ${queryJob.componentPath}`];

  if (queryJob.isPage) {
    const {
      path,
      context
    } = queryJob.context;
    messageParts.push(`URL path: ${path}`);

    if (!(0, _isEmpty2.default)(context)) {
      messageParts.push(`Context: ${JSON.stringify(context, null, 4)}`);
    }
  }

  _reporter.default.warn(messageParts.join(`\n`));
}

function panicQueryJobError(queryJob, errors) {
  let urlPath = undefined;
  let queryContext = {};
  const plugin = queryJob.pluginCreatorId || `none`;

  if (queryJob.queryType === `page`) {
    urlPath = queryJob.context.path;
    queryContext = queryJob.context.context;
  }

  const structuredErrors = errors.map(e => {
    const structuredError = (0, _errorParser.default)({
      message: e.message,
      filePath: undefined,
      location: undefined,
      error: e
    });
    structuredError.context = { ...structuredError.context,
      codeFrame: (0, _graphqlErrorsCodeframe.getCodeFrame)(queryJob.query, e.locations && e.locations[0].line, e.locations && e.locations[0].column),
      filePath: queryJob.componentPath,
      ...(urlPath ? {
        urlPath
      } : {}),
      ...queryContext,
      plugin
    };
    return structuredError;
  });

  _reporter.default.panicOnBuild(structuredErrors);
}

async function startQueryJob(graphqlRunner, queryJob, parentSpan) {
  let isPending = true; // Print out warning when query takes too long

  const timeoutId = setTimeout(() => {
    if (isPending) {
      reportLongRunningQueryJob(queryJob);
    }
  }, 15000);
  return graphqlRunner.query(queryJob.query, queryJob.context, {
    parentSpan,
    queryName: queryJob.id,
    componentPath: queryJob.componentPath
  }).finally(() => {
    isPending = false;
    clearTimeout(timeoutId);
  });
}

async function queryRunner(graphqlRunner, queryJob, parentSpan) {
  const {
    program
  } = _redux.store.getState();

  _redux.store.dispatch(_actions.actions.queryStart({
    path: queryJob.id,
    componentPath: queryJob.componentPath,
    isPage: queryJob.queryType === `page`
  })); // Run query


  let result; // Nothing to do if the query doesn't exist.

  if (!queryJob.query || queryJob.query === ``) {
    result = {};
  } else {
    result = await startQueryJob(graphqlRunner, queryJob, parentSpan);
  }

  if (result.errors) {
    // If there's a graphql error then log the error and exit
    panicQueryJobError(queryJob, result.errors);
  } // Add the page context onto the results.


  if (queryJob && queryJob.queryType === `page`) {
    result[`pageContext`] = Object.assign({}, queryJob.context);
  } // Delete internal data from pageContext


  if (result.pageContext) {
    delete result.pageContext.path;
    delete result.pageContext.internalComponentName;
    delete result.pageContext.component;
    delete result.pageContext.componentChunkName;
    delete result.pageContext.updatedAt;
    delete result.pageContext.pluginCreator___NODE;
    delete result.pageContext.pluginCreatorId;
    delete result.pageContext.componentPath;
    delete result.pageContext.context;
    delete result.pageContext.isCreatedByStatefulCreatePages;
    delete result.pageContext.matchPath;
    delete result.pageContext.mode;
    delete result.pageContext.slices;
  }

  const resultJSON = JSON.stringify(result);

  const resultHash = _crypto.default.createHash(`sha1`).update(resultJSON).digest(`base64`);

  const resultHashCache = getResultHashCache();

  if (resultHash !== (await resultHashCache.get(queryJob.id)) || queryJob.queryType === `page` && !(0, _pageData.pageDataExists)(_path.default.join(program.directory, `public`), queryJob.id)) {
    await resultHashCache.set(queryJob.id, resultHash);

    if (queryJob.queryType === `page` || queryJob.queryType === `slice`) {
      // We need to save this temporarily in cache because
      // this might be incomplete at the moment
      await (0, _pageData.savePageQueryResult)(program.directory, queryJob.id, resultJSON);

      if (queryJob.queryType === `page`) {
        _redux.store.dispatch({
          type: `ADD_PENDING_PAGE_DATA_WRITE`,
          payload: {
            path: queryJob.id
          }
        });
      } else if (queryJob.queryType === `slice`) {
        _redux.store.dispatch({
          type: `ADD_PENDING_SLICE_DATA_WRITE`,
          payload: {
            name: queryJob.id.substring(4)
          }
        });
      }
    } else if (queryJob.queryType === `static`) {
      const resultPath = _path.default.join(program.directory, `public`, `page-data`, `sq`, `d`, `${queryJob.hash}.json`);

      await _fsExtra.default.outputFile(resultPath, resultJSON);
    }
  } // Broadcast that a page's query has run.


  _redux.store.dispatch(_actions.actions.pageQueryRun({
    path: queryJob.id,
    componentPath: queryJob.componentPath,
    queryType: queryJob.queryType,
    resultHash,
    queryHash: queryJob.hash
  }));

  return result;
}
//# sourceMappingURL=query-runner.js.map