"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.passThroughValidation = exports.CoreVersionedRoute = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _configSchema = require("@kbn/config-schema");
var _coreHttpCommon = require("@kbn/core-http-common");
var _validate = require("./validate");
var _route_version_utils = require("./route_version_utils");
var _inject_response_headers = require("./inject_response_headers");
var _handler_resolvers = require("./handler_resolvers");
/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the Elastic License
 * 2.0 and the Server Side Public License, v 1; you may not use this file except
 * in compliance with, at your election, the Elastic License 2.0 or the Server
 * Side Public License, v 1.
 */

// This validation is a pass-through so that we can apply our version-specific validation later
const passThroughValidation = {
  body: _configSchema.schema.nullable(_configSchema.schema.any()),
  params: _configSchema.schema.nullable(_configSchema.schema.any()),
  query: _configSchema.schema.nullable(_configSchema.schema.any())
};
exports.passThroughValidation = passThroughValidation;
class CoreVersionedRoute {
  static from({
    router,
    method,
    path,
    options
  }) {
    return new CoreVersionedRoute(router, method, path, options);
  }
  constructor(router, method, path, options) {
    (0, _defineProperty2.default)(this, "handlers", new Map());
    (0, _defineProperty2.default)(this, "isPublic", void 0);
    (0, _defineProperty2.default)(this, "enableQueryVersion", void 0);
    (0, _defineProperty2.default)(this, "requestHandler", async (ctx, originalReq, res) => {
      var _validation$response;
      if (this.handlers.size <= 0) {
        return res.custom({
          statusCode: 500,
          body: `No handlers registered for [${this.method}] [${this.path}].`
        });
      }
      const req = originalReq;
      let version;
      const maybeVersion = (0, _route_version_utils.readVersion)(req, this.enableQueryVersion);
      if (!maybeVersion && this.isPublic) {
        version = this.getDefaultVersion();
      } else {
        version = maybeVersion;
      }
      if (!version) {
        return res.badRequest({
          body: `Please specify a version via ${_coreHttpCommon.ELASTIC_HTTP_VERSION_HEADER} header. Available versions: ${this.versionsToString()}`
        });
      }
      if ((0, _route_version_utils.hasQueryVersion)(req)) {
        if (this.enableQueryVersion) {
          // This endpoint has opted-in to query versioning, so we remove the query parameter as it is reserved
          (0, _route_version_utils.removeQueryVersion)(req);
        } else return res.badRequest({
          body: `Use of query parameter "${_coreHttpCommon.ELASTIC_HTTP_VERSION_QUERY_PARAM}" is not allowed. Please specify the API version using the "${_coreHttpCommon.ELASTIC_HTTP_VERSION_HEADER}" header.`
        });
      }
      const invalidVersionMessage = (0, _route_version_utils.isValidRouteVersion)(this.isPublic, version);
      if (invalidVersionMessage) {
        return res.badRequest({
          body: invalidVersionMessage
        });
      }
      const handler = this.handlers.get(version);
      if (!handler) {
        return res.badRequest({
          body: `No version "${version}" available for [${this.method}] [${this.path}]. Available versions are: ${this.versionsToString()}`
        });
      }
      const validation = handler.options.validate || undefined;
      if (validation !== null && validation !== void 0 && validation.request && Boolean(validation.request.body || validation.request.params || validation.request.query)) {
        try {
          const {
            body,
            params,
            query
          } = (0, _validate.validate)(req, validation.request, handler.options.version);
          req.body = body;
          req.params = params;
          req.query = query;
        } catch (e) {
          return res.badRequest({
            body: e.message
          });
        }
      } else {
        // Preserve behavior of not passing through unvalidated data
        req.body = {};
        req.params = {};
        req.query = {};
      }
      const response = await handler.fn(ctx, req, res);
      if (this.router.isDev && validation !== null && validation !== void 0 && (_validation$response = validation.response) !== null && _validation$response !== void 0 && _validation$response[response.status]) {
        const responseValidation = validation.response[response.status];
        try {
          var _validation$response$;
          (0, _validate.validate)({
            body: response.payload
          }, {
            body: responseValidation.body,
            unsafe: {
              body: (_validation$response$ = validation.response.unsafe) === null || _validation$response$ === void 0 ? void 0 : _validation$response$.body
            }
          }, handler.options.version);
        } catch (e) {
          return res.custom({
            statusCode: 500,
            body: `Failed output validation: ${e.message}`
          });
        }
      }
      return (0, _inject_response_headers.injectResponseHeaders)({
        [_coreHttpCommon.ELASTIC_HTTP_VERSION_HEADER]: version
      }, response);
    });
    this.router = router;
    this.method = method;
    this.path = path;
    this.options = options;
    this.isPublic = this.options.access === 'public';
    this.enableQueryVersion = this.options.enableQueryVersion === true;
    this.router.router[this.method]({
      path: this.path,
      validate: passThroughValidation,
      options: this.getRouteConfigOptions()
    }, this.requestHandler);
  }
  getRouteConfigOptions() {
    return {
      access: this.options.access,
      ...this.options.options
    };
  }

  /** This method assumes that one or more versions handlers are registered  */
  getDefaultVersion() {
    return _handler_resolvers.resolvers[this.router.defaultHandlerResolutionStrategy]([...this.handlers.keys()]);
  }
  versionsToString() {
    return this.handlers.size ? '[' + [...this.handlers.keys()].join(', ') + ']' : '<none>';
  }
  validateVersion(version) {
    // We do an additional check here while we only have a single allowed public version
    // for all public Kibana HTTP APIs
    if (this.router.isDev && this.isPublic) {
      const message = (0, _route_version_utils.isAllowedPublicVersion)(version);
      if (message) {
        throw new Error(message);
      }
    }
    const message = (0, _route_version_utils.isValidRouteVersion)(this.isPublic, version);
    if (message) {
      throw new Error(message);
    }
    if (this.handlers.has(version)) {
      throw new Error(`Version "${version}" handler has already been registered for the route [${this.method.toLowerCase()}] [${this.path}]"`);
    }
  }
  addVersion(options, handler) {
    this.validateVersion(options.version);
    this.handlers.set(options.version, {
      fn: handler,
      options
    });
    return this;
  }
  getHandlers() {
    return [...this.handlers.values()];
  }
}
exports.CoreVersionedRoute = CoreVersionedRoute;