Don’t delete polls with options in the future (#273)

This commit is contained in:
Luke Vella 2022-08-05 16:47:10 +01:00 committed by GitHub
parent dac1041361
commit 2648be9b0a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 90 additions and 9 deletions

View file

@ -4,6 +4,8 @@ import { NextApiRequest, NextApiResponse } from "next";
import { prisma } from "~/prisma/db";
import { parseValue } from "../../utils/date-time-utils";
/**
* DANGER: This endpoint will permanently delete polls.
*/
@ -24,12 +26,33 @@ export default async function handler(
return;
}
// soft delete polls that have not been accessed for over 30 days
const inactivePolls = await prisma.poll.deleteMany({
// get polls that have not been accessed for over 30 days
const inactivePolls = await prisma.$queryRaw<
Array<{ id: string; max: string }>
>`
SELECT polls.id, MAX(options.value) FROM polls
JOIN options ON options.poll_id = polls.id
WHERE touched_at <= ${dayjs().add(-30, "days").toDate()} AND deleted = false
GROUP BY polls.id;
`;
const pollsToSoftDelete: string[] = [];
// keep polls that have options that are in the future
inactivePolls.forEach(({ id, max: value }) => {
const parsedValue = parseValue(value);
const date =
parsedValue.type === "date" ? parsedValue.date : parsedValue.end;
if (dayjs(date).isBefore(dayjs())) {
pollsToSoftDelete.push(id);
}
});
const softDeletedPolls = await prisma.poll.deleteMany({
where: {
deleted: false,
touchedAt: {
lte: dayjs().add(-30, "days").toDate(),
id: {
in: pollsToSoftDelete,
},
},
});
@ -111,7 +134,7 @@ export default async function handler(
}
res.status(200).json({
inactive: inactivePolls.count,
softDeleted: softDeletedPolls.count,
deleted: pollIdsToDelete.length,
});
}

View file

@ -155,3 +155,19 @@ export const expectTimeOption = (d: DateTimeOption): TimeOption => {
}
return d;
};
export const parseValue = (value: string): DateTimeOption => {
if (isTimeSlot(value)) {
const [start, end] = value.split("/");
return {
type: "timeSlot",
start,
end,
};
} else {
return {
type: "date",
date: value,
};
}
};

View file

@ -75,10 +75,9 @@ test.beforeAll(async ({ request, baseURL }) => {
participantUrlId: "p6",
adminUrlId: "a6",
},
// Old demo poll
{
demo: true,
title: "Demo poll",
title: "Old demo poll",
id: "demo-poll-old",
type: "date",
userId: "user1",
@ -86,6 +85,15 @@ test.beforeAll(async ({ request, baseURL }) => {
participantUrlId: "p7",
adminUrlId: "a7",
},
{
title: "Inactive poll with future option",
id: "inactive-poll-future-option",
type: "date",
userId: "user1",
touchedAt: dayjs().add(-30, "days").toDate(),
participantUrlId: "p8",
adminUrlId: "a8",
},
],
});
@ -106,6 +114,21 @@ test.beforeAll(async ({ request, baseURL }) => {
value: "2022-02-24",
pollId: "deleted-poll-7d",
},
{
id: "option-4",
value: `${dayjs()
.add(10, "days")
.format("YYYY-MM-DDTHH:mm:ss")}/${dayjs()
.add(10, "days")
.add(1, "hour")
.format("YYYY-MM-DDTHH:mm:ss")}`,
pollId: "inactive-poll-future-option",
},
{
id: "option-5",
value: dayjs().add(-1, "days").format("YYYY-MM-DD"),
pollId: "inactive-poll",
},
],
});
@ -144,7 +167,7 @@ test.beforeAll(async ({ request, baseURL }) => {
});
expect(await res.json()).toMatchObject({
inactive: 1,
softDeleted: 1,
deleted: 2,
});
});
@ -252,6 +275,16 @@ test("should delete old demo poll", async () => {
expect(oldDemoPoll).toBeNull();
});
test("should not delete poll that has options in the future", async () => {
const futureOptionPoll = await prisma.poll.findFirst({
where: {
id: "inactive-poll-future-option",
},
});
expect(futureOptionPoll).not.toBeNull();
});
// Teardown
test.afterAll(async () => {
await prisma.$executeRaw`DELETE FROM polls WHERE id IN (${Prisma.join([
@ -263,4 +296,13 @@ test.afterAll(async () => {
"demo-poll-new",
"demo-poll-old",
])})`;
await prisma.$executeRaw`DELETE FROM options WHERE id IN (${Prisma.join([
"active-poll",
"deleted-poll-6d",
"deleted-poll-7d",
"still-active-poll",
"inactive-poll",
"demo-poll-new",
"demo-poll-old",
])})`;
});