"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.updateArgsToSoUpdateOptionsDefault = exports.searchArgsToSOFindOptionsDefault = exports.createArgsToSoCreateOptionsDefault = exports.SOContentStorage = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _boom = _interopRequireDefault(require("@hapi/boom"));
var _lodash = require("lodash");
var _utils = require("./utils");
/*
 * 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.
 */

const savedObjectClientFromRequest = async ctx => {
  if (!ctx.requestHandlerContext) {
    throw new Error('Storage context.requestHandlerContext missing.');
  }
  const {
    savedObjects
  } = await ctx.requestHandlerContext.core;
  return savedObjects.client;
};
function savedObjectToItem(savedObject, allowedSavedObjectAttributes) {
  const {
    id,
    type,
    updated_at: updatedAt,
    created_at: createdAt,
    attributes,
    references,
    error,
    namespaces,
    version,
    managed
  } = savedObject;
  return {
    id,
    type,
    managed,
    updatedAt,
    createdAt,
    attributes: (0, _lodash.pick)(attributes, allowedSavedObjectAttributes),
    references,
    error,
    namespaces,
    version
  };
}
const searchArgsToSOFindOptionsDefault = params => {
  var _options$searchFields, _options$fields;
  const {
    query,
    contentTypeId,
    options
  } = params;
  return {
    type: contentTypeId,
    search: query.text,
    perPage: query.limit,
    page: query.cursor ? +query.cursor : undefined,
    defaultSearchOperator: 'AND',
    searchFields: (_options$searchFields = options === null || options === void 0 ? void 0 : options.searchFields) !== null && _options$searchFields !== void 0 ? _options$searchFields : ['description', 'title'],
    fields: (_options$fields = options === null || options === void 0 ? void 0 : options.fields) !== null && _options$fields !== void 0 ? _options$fields : ['description', 'title'],
    ...(0, _utils.tagsToFindOptions)(query.tags)
  };
};
exports.searchArgsToSOFindOptionsDefault = searchArgsToSOFindOptionsDefault;
const createArgsToSoCreateOptionsDefault = params => params;
exports.createArgsToSoCreateOptionsDefault = createArgsToSoCreateOptionsDefault;
const updateArgsToSoUpdateOptionsDefault = params => params;
exports.updateArgsToSoUpdateOptionsDefault = updateArgsToSoUpdateOptionsDefault;
class SOContentStorage {
  constructor({
    savedObjectType,
    cmServicesDefinition,
    createArgsToSoCreateOptions,
    updateArgsToSoUpdateOptions,
    searchArgsToSOFindOptions,
    enableMSearch,
    allowedSavedObjectAttributes,
    mSearchAdditionalSearchFields,
    logger,
    throwOnResultValidationError
  }) {
    (0, _defineProperty2.default)(this, "throwOnResultValidationError", void 0);
    (0, _defineProperty2.default)(this, "logger", void 0);
    (0, _defineProperty2.default)(this, "savedObjectType", void 0);
    (0, _defineProperty2.default)(this, "cmServicesDefinition", void 0);
    (0, _defineProperty2.default)(this, "createArgsToSoCreateOptions", void 0);
    (0, _defineProperty2.default)(this, "updateArgsToSoUpdateOptions", void 0);
    (0, _defineProperty2.default)(this, "searchArgsToSOFindOptions", void 0);
    (0, _defineProperty2.default)(this, "allowedSavedObjectAttributes", void 0);
    (0, _defineProperty2.default)(this, "mSearch", void 0);
    this.logger = logger;
    this.throwOnResultValidationError = throwOnResultValidationError !== null && throwOnResultValidationError !== void 0 ? throwOnResultValidationError : false;
    this.savedObjectType = savedObjectType;
    this.cmServicesDefinition = cmServicesDefinition;
    this.createArgsToSoCreateOptions = createArgsToSoCreateOptions || createArgsToSoCreateOptionsDefault;
    this.updateArgsToSoUpdateOptions = updateArgsToSoUpdateOptions || updateArgsToSoUpdateOptionsDefault;
    this.searchArgsToSOFindOptions = searchArgsToSOFindOptions || searchArgsToSOFindOptionsDefault;
    this.allowedSavedObjectAttributes = allowedSavedObjectAttributes;
    if (enableMSearch) {
      this.mSearch = {
        savedObjectType: this.savedObjectType,
        additionalSearchFields: mSearchAdditionalSearchFields,
        toItemResult: (ctx, savedObject) => {
          const transforms = ctx.utils.getTransforms(this.cmServicesDefinition);
          const contentItem = savedObjectToItem(savedObject, this.allowedSavedObjectAttributes, false);
          const validationError = transforms.mSearch.out.result.validate(contentItem);
          if (validationError) {
            if (this.throwOnResultValidationError) {
              throw _boom.default.badRequest(`Invalid response. ${validationError.message}`);
            } else {
              this.logger.warn(`Invalid response. ${validationError.message}`);
            }
          }

          // Validate DB response and DOWN transform to the request version
          const {
            value,
            error: resultError
          } = transforms.mSearch.out.result.down(contentItem, undefined,
          // do not override version
          {
            validate: false
          } // validation is done above
          );

          if (resultError) {
            throw _boom.default.badRequest(`Invalid response. ${resultError.message}`);
          }
          return value;
        }
      };
    }
  }
  async get(ctx, id) {
    const transforms = ctx.utils.getTransforms(this.cmServicesDefinition);
    const soClient = await savedObjectClientFromRequest(ctx);

    // Save data in DB
    const {
      saved_object: savedObject,
      alias_purpose: aliasPurpose,
      alias_target_id: aliasTargetId,
      outcome
    } = await soClient.resolve(this.savedObjectType, id);
    const response = {
      item: savedObjectToItem(savedObject, this.allowedSavedObjectAttributes, false),
      meta: {
        aliasPurpose,
        aliasTargetId,
        outcome
      }
    };
    const validationError = transforms.get.out.result.validate(response);
    if (validationError) {
      if (this.throwOnResultValidationError) {
        throw _boom.default.badRequest(`Invalid response. ${validationError.message}`);
      } else {
        this.logger.warn(`Invalid response. ${validationError.message}`);
      }
    }

    // Validate DB response and DOWN transform to the request version
    const {
      value,
      error: resultError
    } = transforms.get.out.result.down(response, undefined,
    // do not override version
    {
      validate: false
    } // validation is done above
    );

    if (resultError) {
      throw _boom.default.badRequest(`Invalid response. ${resultError.message}`);
    }
    return value;
  }
  async bulkGet() {
    // Not implemented
    throw new Error(`[bulkGet] has not been implemented. See ${this.constructor.name} class.`);
  }
  async create(ctx, data, options) {
    const transforms = ctx.utils.getTransforms(this.cmServicesDefinition);
    const soClient = await savedObjectClientFromRequest(ctx);

    // Validate input (data & options) & UP transform them to the latest version
    const {
      value: dataToLatest,
      error: dataError
    } = transforms.create.in.data.up(data);
    if (dataError) {
      throw _boom.default.badRequest(`Invalid data. ${dataError.message}`);
    }
    const {
      value: optionsToLatest,
      error: optionsError
    } = transforms.create.in.options.up(options);
    if (optionsError) {
      throw _boom.default.badRequest(`Invalid options. ${optionsError.message}`);
    }
    const createOptions = this.createArgsToSoCreateOptions(optionsToLatest);

    // Save data in DB
    const savedObject = await soClient.create(this.savedObjectType, dataToLatest, createOptions);
    const result = {
      item: savedObjectToItem(savedObject, this.allowedSavedObjectAttributes, false)
    };
    const validationError = transforms.create.out.result.validate(result);
    if (validationError) {
      if (this.throwOnResultValidationError) {
        throw _boom.default.badRequest(`Invalid response. ${validationError.message}`);
      } else {
        this.logger.warn(`Invalid response. ${validationError.message}`);
      }
    }

    // Validate DB response and DOWN transform to the request version
    const {
      value,
      error: resultError
    } = transforms.create.out.result.down(result, undefined,
    // do not override version
    {
      validate: false
    } // validation is done above
    );

    if (resultError) {
      throw _boom.default.badRequest(`Invalid response. ${resultError.message}`);
    }
    return value;
  }
  async update(ctx, id, data, options) {
    const transforms = ctx.utils.getTransforms(this.cmServicesDefinition);
    const soClient = await savedObjectClientFromRequest(ctx);

    // Validate input (data & options) & UP transform them to the latest version
    const {
      value: dataToLatest,
      error: dataError
    } = transforms.update.in.data.up(data);
    if (dataError) {
      throw _boom.default.badRequest(`Invalid data. ${dataError.message}`);
    }
    const {
      value: optionsToLatest,
      error: optionsError
    } = transforms.update.in.options.up(options);
    if (optionsError) {
      throw _boom.default.badRequest(`Invalid options. ${optionsError.message}`);
    }
    const updateOptions = this.updateArgsToSoUpdateOptions(optionsToLatest);

    // Save data in DB
    const partialSavedObject = await soClient.update(this.savedObjectType, id, dataToLatest, updateOptions);
    const result = {
      item: savedObjectToItem(partialSavedObject, this.allowedSavedObjectAttributes, true)
    };
    const validationError = transforms.update.out.result.validate(result);
    if (validationError) {
      if (this.throwOnResultValidationError) {
        throw _boom.default.badRequest(`Invalid response. ${validationError.message}`);
      } else {
        this.logger.warn(`Invalid response. ${validationError.message}`);
      }
    }

    // Validate DB response and DOWN transform to the request version
    const {
      value,
      error: resultError
    } = transforms.update.out.result.down(result, undefined,
    // do not override version
    {
      validate: false
    } // validation is done above
    );

    if (resultError) {
      throw _boom.default.badRequest(`Invalid response. ${resultError.message}`);
    }
    return value;
  }
  async delete(ctx, id,
  // force is necessary to delete saved objects that exist in multiple namespaces
  options) {
    var _options$force;
    const soClient = await savedObjectClientFromRequest(ctx);
    await soClient.delete(this.savedObjectType, id, {
      force: (_options$force = options === null || options === void 0 ? void 0 : options.force) !== null && _options$force !== void 0 ? _options$force : false
    });
    return {
      success: true
    };
  }
  async search(ctx, query, options = {}) {
    const transforms = ctx.utils.getTransforms(this.cmServicesDefinition);
    const soClient = await savedObjectClientFromRequest(ctx);

    // Validate and UP transform the options
    const {
      value: optionsToLatest,
      error: optionsError
    } = transforms.search.in.options.up(options);
    if (optionsError) {
      throw _boom.default.badRequest(`Invalid payload. ${optionsError.message}`);
    }
    const soQuery = this.searchArgsToSOFindOptions({
      contentTypeId: this.savedObjectType,
      query,
      options: optionsToLatest
    });
    // Execute the query in the DB
    const soResponse = await soClient.find(soQuery);
    const response = {
      hits: soResponse.saved_objects.map(so => savedObjectToItem(so, this.allowedSavedObjectAttributes, false)),
      pagination: {
        total: soResponse.total
      }
    };
    const validationError = transforms.search.out.result.validate(response);
    if (validationError) {
      if (this.throwOnResultValidationError) {
        throw _boom.default.badRequest(`Invalid response. ${validationError.message}`);
      } else {
        this.logger.warn(`Invalid response. ${validationError.message}`);
      }
    }

    // Validate the response and DOWN transform to the request version
    const {
      value,
      error: resultError
    } = transforms.search.out.result.down(response, undefined,
    // do not override version
    {
      validate: false
    } // validation is done above
    );

    if (resultError) {
      throw _boom.default.badRequest(`Invalid response. ${resultError.message}`);
    }
    return value;
  }
}
exports.SOContentStorage = SOContentStorage;