"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.bulkDeleteRules = void 0;
var _pMap = _interopRequireDefault(require("p-map"));
var _boom = _interopRequireDefault(require("@hapi/boom"));
var _esQuery = require("@kbn/es-query");
var _apmUtils = require("@kbn/apm-utils");
var _lib = require("../../../../lib");
var _bulk_mark_api_keys_for_invalidation = require("../../../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation");
var _audit_events = require("../../../../rules_client/common/audit_events");
var _common = require("../../../../rules_client/common");
var _constants = require("../../../../rules_client/common/constants");
var _lib2 = require("../../../../rules_client/lib");
var _validation = require("./validation");
var _rule = require("../../../../data/rule");
var _transforms = require("../../transforms");
var _schemas = require("../../schemas");
/*
 * 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; you may not use this file except in compliance with the Elastic License
 * 2.0.
 */

const bulkDeleteRules = async (context, options) => {
  try {
    (0, _validation.validateBulkDeleteRulesBody)(options);
  } catch (error) {
    throw _boom.default.badRequest(`Error validating bulk delete data - ${error.message}`);
  }
  const {
    ids,
    filter
  } = options;
  const kueryNodeFilter = ids ? (0, _lib.convertRuleIdsToKueryNode)(ids) : (0, _common.buildKueryNodeFilter)(filter);
  const authorizationFilter = await (0, _lib2.getAuthorizationFilter)(context, {
    action: 'DELETE'
  });
  const kueryNodeFilterWithAuth = authorizationFilter && kueryNodeFilter ? _esQuery.nodeBuilder.and([kueryNodeFilter, authorizationFilter]) : kueryNodeFilter;
  const {
    total
  } = await (0, _lib2.checkAuthorizationAndGetTotal)(context, {
    filter: kueryNodeFilterWithAuth,
    action: 'DELETE'
  });
  const {
    rules,
    errors,
    accListSpecificForBulkOperation
  } = await (0, _apmUtils.withSpan)({
    name: 'retryIfBulkOperationConflicts',
    type: 'rules'
  }, () => (0, _common.retryIfBulkOperationConflicts)({
    action: 'DELETE',
    logger: context.logger,
    bulkOperation: filterKueryNode => bulkDeleteWithOCC(context, {
      filter: filterKueryNode
    }),
    filter: kueryNodeFilterWithAuth
  }));
  const [apiKeysToInvalidate, taskIdsToDelete] = accListSpecificForBulkOperation;
  const [result] = await Promise.allSettled([(0, _common.tryToRemoveTasks)({
    taskIdsToDelete,
    logger: context.logger,
    taskManager: context.taskManager
  }), (0, _bulk_mark_api_keys_for_invalidation.bulkMarkApiKeysForInvalidation)({
    apiKeys: apiKeysToInvalidate
  }, context.logger, context.unsecuredSavedObjectsClient)]);
  const deletedRules = rules.map(({
    id,
    attributes,
    references
  }) => {
    // TODO (http-versioning): alertTypeId should never be null, but we need to
    // fix the type cast from SavedObjectsBulkUpdateObject to SavedObjectsBulkUpdateObject
    // when we are doing the bulk delete and this should fix itself
    const ruleType = context.ruleTypeRegistry.get(attributes.alertTypeId);
    const ruleDomain = (0, _transforms.transformRuleAttributesToRuleDomain)(attributes, {
      id,
      logger: context.logger,
      ruleType,
      references,
      omitGeneratedValues: false
    });
    try {
      _schemas.ruleDomainSchema.validate(ruleDomain);
    } catch (e) {
      context.logger.warn(`Error validating bulk deleted rule domain object for id: ${id}, ${e}`);
    }
    return ruleDomain;
  });

  // // TODO (http-versioning): This should be of type Rule, change this when all rule types are fixed
  const deletedPublicRules = deletedRules.map(rule => {
    return (0, _transforms.transformRuleDomainToRule)(rule);
  });
  if (result.status === 'fulfilled') {
    return {
      errors,
      total,
      rules: deletedPublicRules,
      taskIdsFailedToBeDeleted: result.value
    };
  } else {
    return {
      errors,
      total,
      rules: deletedPublicRules,
      taskIdsFailedToBeDeleted: []
    };
  }
};
exports.bulkDeleteRules = bulkDeleteRules;
const bulkDeleteWithOCC = async (context, {
  filter
}) => {
  const rulesFinder = await (0, _apmUtils.withSpan)({
    name: 'encryptedSavedObjectsClient.createPointInTimeFinderDecryptedAsInternalUser',
    type: 'rules'
  }, () => context.encryptedSavedObjectsClient.createPointInTimeFinderDecryptedAsInternalUser({
    filter,
    type: 'alert',
    perPage: 100,
    ...(context.namespace ? {
      namespaces: [context.namespace]
    } : undefined)
  }));
  const rulesToDelete = [];
  const apiKeyToRuleIdMapping = {};
  const taskIdToRuleIdMapping = {};
  const ruleNameToRuleIdMapping = {};
  await (0, _apmUtils.withSpan)({
    name: 'Get rules, collect them and their attributes',
    type: 'rules'
  }, async () => {
    for await (const response of rulesFinder.find()) {
      for (const rule of response.saved_objects) {
        var _context$auditLogger;
        if (rule.attributes.apiKey && !rule.attributes.apiKeyCreatedByUser) {
          apiKeyToRuleIdMapping[rule.id] = rule.attributes.apiKey;
        }
        if (rule.attributes.name) {
          ruleNameToRuleIdMapping[rule.id] = rule.attributes.name;
        }
        if (rule.attributes.scheduledTaskId) {
          taskIdToRuleIdMapping[rule.id] = rule.attributes.scheduledTaskId;
        }
        rulesToDelete.push(rule);
        (_context$auditLogger = context.auditLogger) === null || _context$auditLogger === void 0 ? void 0 : _context$auditLogger.log((0, _audit_events.ruleAuditEvent)({
          action: _audit_events.RuleAuditAction.DELETE,
          outcome: 'unknown',
          savedObject: {
            type: 'alert',
            id: rule.id
          }
        }));
      }
    }
    await rulesFinder.close();
  });
  for (const {
    id,
    attributes
  } of rulesToDelete) {
    await (0, _lib2.untrackRuleAlerts)(context, id, attributes);
  }
  const result = await (0, _apmUtils.withSpan)({
    name: 'unsecuredSavedObjectsClient.bulkDelete',
    type: 'rules'
  }, () => (0, _rule.bulkDeleteRulesSo)({
    savedObjectsClient: context.unsecuredSavedObjectsClient,
    ids: rulesToDelete.map(({
      id
    }) => id)
  }));
  const deletedRuleIds = [];
  const apiKeysToInvalidate = [];
  const taskIdsToDelete = [];
  const errors = [];
  result.statuses.forEach(status => {
    if (status.error === undefined) {
      if (apiKeyToRuleIdMapping[status.id]) {
        apiKeysToInvalidate.push(apiKeyToRuleIdMapping[status.id]);
      }
      if (taskIdToRuleIdMapping[status.id]) {
        taskIdsToDelete.push(taskIdToRuleIdMapping[status.id]);
      }
      deletedRuleIds.push(status.id);
    } else {
      var _status$error$message, _ruleNameToRuleIdMapp;
      errors.push({
        message: (_status$error$message = status.error.message) !== null && _status$error$message !== void 0 ? _status$error$message : 'n/a',
        status: status.error.statusCode,
        rule: {
          id: status.id,
          name: (_ruleNameToRuleIdMapp = ruleNameToRuleIdMapping[status.id]) !== null && _ruleNameToRuleIdMapp !== void 0 ? _ruleNameToRuleIdMapp : 'n/a'
        }
      });
    }
  });
  const rules = rulesToDelete.filter(rule => deletedRuleIds.includes(rule.id));

  // migrate legacy actions only for SIEM rules
  // TODO (http-versioning) Remove RawRule casts
  await (0, _pMap.default)(rules, async rule => {
    await (0, _lib2.migrateLegacyActions)(context, {
      ruleId: rule.id,
      attributes: rule.attributes,
      skipActionsValidation: true
    });
  },
  // max concurrency for bulk edit operations, that is limited by api key generations, should be sufficient for bulk migrations
  {
    concurrency: _constants.API_KEY_GENERATE_CONCURRENCY
  });
  return {
    errors,
    rules,
    accListSpecificForBulkOperation: [apiKeysToInvalidate, taskIdsToDelete]
  };
};