"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.retrieveFilesIgnoringNotFound = exports.bulkDeleteFileAttachments = void 0; var _boom = _interopRequireDefault(require("@hapi/boom")); var _pSettle = _interopRequireDefault(require("p-settle")); var _lodash = require("lodash"); var _errors = require("@kbn/files-plugin/server/file_service/errors"); var _api = require("../../../common/types/api"); var _api2 = require("../../../common/api"); var _constants = require("../../../common/constants"); var _error = require("../../common/error"); var _authorization = require("../../authorization"); var _files = require("../../../common/files"); var _files2 = require("../files"); /* * 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 bulkDeleteFileAttachments = async ({ caseId, fileIds }, clientArgs, casesClient) => { const { user, services: { attachmentService, userActionService }, logger, authorization, fileService } = clientArgs; try { const request = (0, _api2.decodeWithExcessOrThrow)(_api.BulkDeleteFileAttachmentsRequestRt)({ ids: fileIds }); await casesClient.cases.resolve({ id: caseId, includeComments: false }); const fileEntities = await getFileEntities({ caseId, fileIds: request.ids, fileService, logger }); // It's possible for this to return an empty array if there was an error creating file attachments in which case the // file would be present but the case attachment would not const fileAttachments = await attachmentService.getter.getFileAttachments({ caseId, fileIds: request.ids }); await authorization.ensureAuthorized({ entities: [...fileAttachments.map(attachment => ({ id: attachment.id, owner: attachment.attributes.owner })), ...fileEntities], operation: _authorization.Operations.deleteComment }); await Promise.all([(0, _files2.deleteFiles)(fileEntities.map(entity => entity.id), fileService), attachmentService.bulkDelete({ attachmentIds: fileAttachments.map(so => so.id), refresh: false })]); await userActionService.creator.bulkCreateAttachmentDeletion({ caseId, attachments: fileAttachments.map(attachment => ({ id: attachment.id, owner: attachment.attributes.owner, attachment: attachment.attributes })), user }); } catch (error) { throw (0, _error.createCaseError)({ message: `Failed to delete file attachments for case: ${caseId}: ${error}`, error, logger }); } }; exports.bulkDeleteFileAttachments = bulkDeleteFileAttachments; const getFileEntities = async ({ caseId, fileIds, fileService, logger }) => { const files = await getFiles({ caseId, fileIds, fileService, logger }); const fileEntities = (0, _files2.createFileEntities)(files); return fileEntities; }; const getFiles = async ({ caseId, fileIds, fileService, logger }) => { // it's possible that we're trying to delete a file when an attachment wasn't created (for example if the create // attachment request failed) const fileSettleResults = await (0, _pSettle.default)(fileIds.map(async fileId => fileService.getById({ id: fileId })), { concurrency: _constants.MAX_CONCURRENT_SEARCHES }); const files = retrieveFilesIgnoringNotFound(fileSettleResults, fileIds, logger); const [validFiles, invalidFiles] = (0, _lodash.partition)(files, file => { return _files.CaseFileMetadataForDeletionRt.is(file.data.meta) && file.data.meta.caseIds.length === 1 && file.data.meta.caseIds.includes(caseId); }); if (invalidFiles.length > 0) { const invalidIds = invalidFiles.map(fileInfo => fileInfo.id); // I'm intentionally being vague here because it's possible an unauthorized user could attempt to delete files throw _boom.default.badRequest(`Failed to delete files because filed ids were invalid: ${invalidIds}`); } return validFiles.map(fileInfo => fileInfo.data); }; const retrieveFilesIgnoringNotFound = (results, fileIds, logger) => { const files = []; results.forEach((result, index) => { if (result.isFulfilled) { files.push(result.value); } else if (result.reason instanceof _errors.FileNotFoundError) { const warningMessage = getFileNotFoundErrorMessage({ resultsLength: results.length, fileIds, index, result }); logger.warn(warningMessage); } else if (result.reason instanceof Error) { throw result.reason; } else { throw new Error(`Failed to retrieve file id: ${fileIds[index]}: ${result.reason}`); } }); return files; }; exports.retrieveFilesIgnoringNotFound = retrieveFilesIgnoringNotFound; const getFileNotFoundErrorMessage = ({ resultsLength, fileIds, index, result }) => { if (resultsLength === fileIds.length) { return `Failed to find file id: ${fileIds[index]}: ${result.reason}`; } return `Failed to find file: ${result.reason}`; };