chore: Permanent team deletion (#2493)
This commit is contained in:
@ -4,7 +4,7 @@ import { Document, Attachment } from "../models";
|
||||
import { sequelize } from "../sequelize";
|
||||
import parseAttachmentIds from "../utils/parseAttachmentIds";
|
||||
|
||||
export async function documentPermanentDeleter(documents: Document[]) {
|
||||
export default async function documentPermanentDeleter(documents: Document[]) {
|
||||
const activeDocument = documents.find((doc) => !doc.deletedAt);
|
||||
|
||||
if (activeDocument) {
|
||||
|
@ -3,7 +3,7 @@ import { subDays } from "date-fns";
|
||||
import { Attachment, Document } from "../models";
|
||||
import { buildAttachment, buildDocument } from "../test/factories";
|
||||
import { flushdb } from "../test/support";
|
||||
import { documentPermanentDeleter } from "./documentPermanentDeleter";
|
||||
import documentPermanentDeleter from "./documentPermanentDeleter";
|
||||
|
||||
jest.mock("aws-sdk", () => {
|
||||
const mS3 = { deleteObject: jest.fn().mockReturnThis(), promise: jest.fn() };
|
||||
|
177
server/commands/teamPermanentDeleter.js
Normal file
177
server/commands/teamPermanentDeleter.js
Normal file
@ -0,0 +1,177 @@
|
||||
// @flow
|
||||
import Logger from "../logging/logger";
|
||||
import {
|
||||
ApiKey,
|
||||
Attachment,
|
||||
AuthenticationProvider,
|
||||
Collection,
|
||||
Document,
|
||||
Event,
|
||||
FileOperation,
|
||||
Group,
|
||||
Team,
|
||||
NotificationSetting,
|
||||
User,
|
||||
UserAuthentication,
|
||||
Integration,
|
||||
SearchQuery,
|
||||
Share,
|
||||
} from "../models";
|
||||
import { sequelize } from "../sequelize";
|
||||
|
||||
export default async function teamPermanentDeleter(team: Team) {
|
||||
if (!team.deletedAt) {
|
||||
throw new Error(
|
||||
`Cannot permanently delete ${team.id} team. Please delete it and try again.`
|
||||
);
|
||||
}
|
||||
|
||||
Logger.info(
|
||||
"commands",
|
||||
`Permanently deleting team ${team.name} (${team.id})`
|
||||
);
|
||||
|
||||
const teamId = team.id;
|
||||
let transaction;
|
||||
|
||||
try {
|
||||
transaction = await sequelize.transaction();
|
||||
|
||||
await Attachment.findAllInBatches(
|
||||
{
|
||||
where: {
|
||||
teamId,
|
||||
},
|
||||
limit: 100,
|
||||
offset: 0,
|
||||
},
|
||||
async (attachments, options) => {
|
||||
Logger.info(
|
||||
"commands",
|
||||
`Deleting attachments ${options.offset} – ${
|
||||
options.offset + options.limit
|
||||
}…`
|
||||
);
|
||||
|
||||
await Promise.all(
|
||||
attachments.map((attachment) => attachment.destroy({ transaction }))
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
// Destroy user-relation models
|
||||
await User.findAllInBatches(
|
||||
{
|
||||
attributes: ["id"],
|
||||
where: {
|
||||
teamId,
|
||||
},
|
||||
limit: 100,
|
||||
offset: 0,
|
||||
},
|
||||
async (users) => {
|
||||
const userIds = users.map((user) => user.id);
|
||||
|
||||
await UserAuthentication.destroy({
|
||||
where: { userId: userIds },
|
||||
force: true,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await ApiKey.destroy({
|
||||
where: { userId: userIds },
|
||||
force: true,
|
||||
transaction,
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
// Destory team-relation models
|
||||
await AuthenticationProvider.destroy({
|
||||
where: { teamId },
|
||||
force: true,
|
||||
transaction,
|
||||
});
|
||||
|
||||
// events must be first due to db constraints
|
||||
await Event.destroy({
|
||||
where: { teamId },
|
||||
force: true,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await Collection.destroy({
|
||||
where: { teamId },
|
||||
force: true,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await Document.unscoped().destroy({
|
||||
where: { teamId },
|
||||
force: true,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await FileOperation.destroy({
|
||||
where: { teamId },
|
||||
force: true,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await Group.unscoped().destroy({
|
||||
where: { teamId },
|
||||
force: true,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await Integration.destroy({
|
||||
where: { teamId },
|
||||
force: true,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await NotificationSetting.destroy({
|
||||
where: { teamId },
|
||||
force: true,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await SearchQuery.destroy({
|
||||
where: { teamId },
|
||||
force: true,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await Share.destroy({
|
||||
where: { teamId },
|
||||
force: true,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await User.destroy({
|
||||
where: { teamId },
|
||||
force: true,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await team.destroy({
|
||||
force: true,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await Event.create(
|
||||
{
|
||||
name: "teams.destroy",
|
||||
modelId: teamId,
|
||||
},
|
||||
{ transaction }
|
||||
);
|
||||
|
||||
await transaction.commit();
|
||||
} catch (err) {
|
||||
if (transaction) {
|
||||
await transaction.rollback();
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
100
server/commands/teamPermanentDeleter.test.js
Normal file
100
server/commands/teamPermanentDeleter.test.js
Normal file
@ -0,0 +1,100 @@
|
||||
// @flow
|
||||
import { subDays } from "date-fns";
|
||||
import { Attachment, User, Document, Collection, Team } from "../models";
|
||||
import {
|
||||
buildAttachment,
|
||||
buildUser,
|
||||
buildTeam,
|
||||
buildDocument,
|
||||
} from "../test/factories";
|
||||
import { flushdb } from "../test/support";
|
||||
import teamPermanentDeleter from "./teamPermanentDeleter";
|
||||
|
||||
jest.mock("aws-sdk", () => {
|
||||
const mS3 = { deleteObject: jest.fn().mockReturnThis(), promise: jest.fn() };
|
||||
return {
|
||||
S3: jest.fn(() => mS3),
|
||||
Endpoint: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
beforeEach(() => flushdb());
|
||||
|
||||
describe("teamPermanentDeleter", () => {
|
||||
it("should destroy related data", async () => {
|
||||
const team = await buildTeam({
|
||||
deletedAt: subDays(new Date(), 90),
|
||||
});
|
||||
const user = await buildUser({
|
||||
teamId: team.id,
|
||||
});
|
||||
await buildDocument({
|
||||
teamId: team.id,
|
||||
userId: user.id,
|
||||
});
|
||||
|
||||
await teamPermanentDeleter(team);
|
||||
|
||||
expect(await Team.count()).toEqual(0);
|
||||
expect(await User.count()).toEqual(0);
|
||||
expect(await Document.unscoped().count({ paranoid: false })).toEqual(0);
|
||||
expect(await Collection.unscoped().count({ paranoid: false })).toEqual(0);
|
||||
});
|
||||
|
||||
it("should not destroy unrelated data", async () => {
|
||||
const team = await buildTeam({
|
||||
deletedAt: subDays(new Date(), 90),
|
||||
});
|
||||
|
||||
await buildUser();
|
||||
await buildTeam();
|
||||
await buildDocument();
|
||||
|
||||
await teamPermanentDeleter(team);
|
||||
|
||||
expect(await Team.count()).toEqual(4); // each build command creates a team
|
||||
expect(await User.count()).toEqual(2);
|
||||
expect(await Document.unscoped().count({ paranoid: false })).toEqual(1);
|
||||
expect(await Collection.unscoped().count({ paranoid: false })).toEqual(1);
|
||||
});
|
||||
|
||||
it("should destroy attachments", async () => {
|
||||
const team = await buildTeam({
|
||||
deletedAt: subDays(new Date(), 90),
|
||||
});
|
||||
const user = await buildUser({
|
||||
teamId: team.id,
|
||||
});
|
||||
const document = await buildDocument({
|
||||
teamId: team.id,
|
||||
userId: user.id,
|
||||
});
|
||||
await buildAttachment({
|
||||
teamId: document.teamId,
|
||||
documentId: document.id,
|
||||
});
|
||||
|
||||
await teamPermanentDeleter(team);
|
||||
|
||||
expect(await Team.count()).toEqual(0);
|
||||
expect(await User.count()).toEqual(0);
|
||||
expect(await Attachment.count()).toEqual(0);
|
||||
expect(await Document.unscoped().count({ paranoid: false })).toEqual(0);
|
||||
expect(await Collection.unscoped().count({ paranoid: false })).toEqual(0);
|
||||
});
|
||||
|
||||
it("should error when trying to destroy undeleted team", async () => {
|
||||
const team = await buildTeam();
|
||||
|
||||
let error;
|
||||
try {
|
||||
await teamPermanentDeleter(team);
|
||||
} catch (err) {
|
||||
error = err.message;
|
||||
}
|
||||
|
||||
expect(error).toEqual(
|
||||
`Cannot permanently delete ${team.id} team. Please delete it and try again.`
|
||||
);
|
||||
});
|
||||
});
|
Reference in New Issue
Block a user