fix: Delete collection exports (#2595)
This commit is contained in:
31
server/commands/fileOperationDeleter.js
Normal file
31
server/commands/fileOperationDeleter.js
Normal file
@@ -0,0 +1,31 @@
|
||||
// @flow
|
||||
import { FileOperation, Event, User } from "../models";
|
||||
import { sequelize } from "../sequelize";
|
||||
|
||||
export default async function fileOperationDeleter(
|
||||
fileOp: FileOperation,
|
||||
user: User,
|
||||
ip: string
|
||||
) {
|
||||
let transaction = await sequelize.transaction();
|
||||
|
||||
try {
|
||||
await fileOp.destroy({ transaction });
|
||||
|
||||
await Event.create(
|
||||
{
|
||||
name: "fileOperations.delete",
|
||||
teamId: user.teamId,
|
||||
actorId: user.id,
|
||||
data: fileOp.dataValues,
|
||||
ip,
|
||||
},
|
||||
{ transaction }
|
||||
);
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
31
server/commands/fileOperationDeleter.test.js
Normal file
31
server/commands/fileOperationDeleter.test.js
Normal file
@@ -0,0 +1,31 @@
|
||||
// @flow
|
||||
import { FileOperation } from "../models";
|
||||
import { buildAdmin, buildFileOperation } from "../test/factories";
|
||||
import { flushdb } from "../test/support";
|
||||
import fileOperationDeleter from "./fileOperationDeleter";
|
||||
|
||||
jest.mock("aws-sdk", () => {
|
||||
const mS3 = { deleteObject: jest.fn().mockReturnThis(), promise: jest.fn() };
|
||||
return {
|
||||
S3: jest.fn(() => mS3),
|
||||
Endpoint: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
beforeEach(() => flushdb());
|
||||
|
||||
describe("fileOperationDeleter", () => {
|
||||
const ip = "127.0.0.1";
|
||||
|
||||
it("should destroy file operation", async () => {
|
||||
const admin = await buildAdmin();
|
||||
const fileOp = await buildFileOperation({
|
||||
userId: admin.id,
|
||||
teamId: admin.teamId,
|
||||
});
|
||||
|
||||
await fileOperationDeleter(fileOp, admin, ip);
|
||||
|
||||
expect(await FileOperation.count()).toEqual(0);
|
||||
});
|
||||
});
|
||||
@@ -34,10 +34,14 @@ const FileOperation = sequelize.define("file_operations", {
|
||||
},
|
||||
});
|
||||
|
||||
FileOperation.beforeDestroy(async (model) => {
|
||||
await deleteFromS3(model.key);
|
||||
});
|
||||
|
||||
FileOperation.prototype.expire = async function () {
|
||||
this.state = "expired";
|
||||
await deleteFromS3(this.key);
|
||||
this.save();
|
||||
await this.save();
|
||||
};
|
||||
|
||||
FileOperation.associate = (models) => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// @flow
|
||||
import Router from "koa-router";
|
||||
import fileOperationDeleter from "../../commands/fileOperationDeleter";
|
||||
import { NotFoundError, ValidationError } from "../../errors";
|
||||
import auth from "../../middlewares/authentication";
|
||||
import { FileOperation, Team } from "../../models";
|
||||
@@ -88,7 +89,7 @@ router.post("fileOperations.redirect", auth(), async (ctx) => {
|
||||
authorize(user, fileOp.type, team);
|
||||
|
||||
if (fileOp.state !== "complete") {
|
||||
throw new ValidationError("file operation is not complete yet");
|
||||
throw new ValidationError(`${fileOp.type} is not complete yet`);
|
||||
}
|
||||
|
||||
const accessUrl = await getSignedUrl(fileOp.key);
|
||||
@@ -96,4 +97,24 @@ router.post("fileOperations.redirect", auth(), async (ctx) => {
|
||||
ctx.redirect(accessUrl);
|
||||
});
|
||||
|
||||
router.post("fileOperations.delete", auth(), async (ctx) => {
|
||||
const { id } = ctx.body;
|
||||
ctx.assertUuid(id, "id is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
const team = await Team.findByPk(user.teamId);
|
||||
const fileOp = await FileOperation.findByPk(id);
|
||||
|
||||
if (!fileOp) {
|
||||
throw new NotFoundError();
|
||||
}
|
||||
|
||||
authorize(user, fileOp.type, team);
|
||||
|
||||
await fileOperationDeleter(fileOp, user, ctx.request.ip);
|
||||
|
||||
ctx.body = {
|
||||
success: true,
|
||||
};
|
||||
});
|
||||
export default router;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable flowtype/require-valid-file-annotation */
|
||||
import TestServer from "fetch-test-server";
|
||||
|
||||
import { Collection, User } from "../../models";
|
||||
import { Collection, User, Event, FileOperation } from "../../models";
|
||||
import webService from "../../services/web";
|
||||
import {
|
||||
buildAdmin,
|
||||
@@ -15,6 +15,14 @@ import { flushdb } from "../../test/support";
|
||||
const app = webService();
|
||||
const server = new TestServer(app.callback());
|
||||
|
||||
jest.mock("aws-sdk", () => {
|
||||
const mS3 = { deleteObject: jest.fn().mockReturnThis(), promise: jest.fn() };
|
||||
return {
|
||||
S3: jest.fn(() => mS3),
|
||||
Endpoint: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
beforeEach(() => flushdb());
|
||||
afterAll(() => server.close());
|
||||
|
||||
@@ -234,7 +242,7 @@ describe("#fileOperations.redirect", () => {
|
||||
|
||||
const body = await res.json();
|
||||
expect(res.status).toEqual(400);
|
||||
expect(body.message).toEqual("file operation is not complete yet");
|
||||
expect(body.message).toEqual("export is not complete yet");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -281,3 +289,27 @@ describe("#fileOperations.info", () => {
|
||||
expect(res.status).toBe(403);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#fileOperations.delete", () => {
|
||||
it("should delete file operation", async () => {
|
||||
const team = await buildTeam();
|
||||
const admin = await buildAdmin({ teamId: team.id });
|
||||
const exportData = await buildFileOperation({
|
||||
type: "export",
|
||||
teamId: team.id,
|
||||
userId: admin.id,
|
||||
state: "complete",
|
||||
});
|
||||
|
||||
const deleteResponse = await server.post("/api/fileOperations.delete", {
|
||||
body: {
|
||||
token: admin.getJwtToken(),
|
||||
id: exportData.id,
|
||||
},
|
||||
});
|
||||
|
||||
expect(deleteResponse.status).toBe(200);
|
||||
expect(await Event.count()).toBe(1);
|
||||
expect(await FileOperation.count()).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -268,15 +268,12 @@ export async function buildFileOperation(overrides: Object = {}) {
|
||||
overrides.userId = user.id;
|
||||
}
|
||||
|
||||
if (!overrides.collectionId) {
|
||||
const collection = await buildCollection(overrides);
|
||||
overrides.collectionId = collection.id;
|
||||
}
|
||||
|
||||
return FileOperation.create({
|
||||
state: "creating",
|
||||
size: 0,
|
||||
key: "key/to/aws/file.zip",
|
||||
key: "uploads/key/to/file.zip",
|
||||
collectionId: null,
|
||||
type: "export",
|
||||
url: "https://www.urltos3file.com/file.zip",
|
||||
...overrides,
|
||||
});
|
||||
|
||||
@@ -132,7 +132,7 @@ export type CollectionExportAllEvent = {
|
||||
};
|
||||
|
||||
export type FileOperationEvent = {
|
||||
name: "fileOperations.update",
|
||||
name: "fileOperations.update" | "fileOperation.delete",
|
||||
teamId: string,
|
||||
actorId: string,
|
||||
data: {
|
||||
|
||||
Reference in New Issue
Block a user