mirror of
https://github.com/versia-pub/server.git
synced 2025-12-07 08:48:19 +01:00
feat(api): ✨ Implement glitch-soc /v1/notifications/destroy_multiple route
This commit is contained in:
parent
6dff872822
commit
5030d03404
112
server/api/api/v1/notifications/destroy_multiple/index.test.ts
Normal file
112
server/api/api/v1/notifications/destroy_multiple/index.test.ts
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
||||||
|
import { config } from "config-manager";
|
||||||
|
import {
|
||||||
|
deleteOldTestUsers,
|
||||||
|
getTestStatuses,
|
||||||
|
getTestUsers,
|
||||||
|
sendTestRequest,
|
||||||
|
} from "~tests/utils";
|
||||||
|
import type { Notification as APINotification } from "~types/mastodon/notification";
|
||||||
|
import { meta } from "./index";
|
||||||
|
|
||||||
|
await deleteOldTestUsers();
|
||||||
|
|
||||||
|
const { users, tokens, deleteUsers } = await getTestUsers(2);
|
||||||
|
const statuses = await getTestStatuses(40, users[0]);
|
||||||
|
let notifications: APINotification[] = [];
|
||||||
|
|
||||||
|
// Create some test notifications
|
||||||
|
beforeAll(async () => {
|
||||||
|
await fetch(
|
||||||
|
new URL(`/api/v1/accounts/${users[0].id}/follow`, config.http.base_url),
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${tokens[1].accessToken}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const i of [0, 1, 2, 3]) {
|
||||||
|
await fetch(
|
||||||
|
new URL(
|
||||||
|
`/api/v1/statuses/${statuses[i].id}/favourite`,
|
||||||
|
config.http.base_url,
|
||||||
|
),
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${tokens[1].accessToken}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
notifications = await fetch(
|
||||||
|
new URL("/api/v1/notifications", config.http.base_url),
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${tokens[0].accessToken}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
).then((r) => r.json());
|
||||||
|
|
||||||
|
expect(notifications.length).toBe(5);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await deleteUsers();
|
||||||
|
});
|
||||||
|
|
||||||
|
// /api/v1/notifications/destroy_multiple
|
||||||
|
describe(meta.route, () => {
|
||||||
|
test("should return 401 if not authenticated", async () => {
|
||||||
|
const response = await sendTestRequest(
|
||||||
|
new Request(new URL(meta.route, config.http.base_url), {
|
||||||
|
method: "DELETE",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(response.status).toBe(401);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should dismiss notifications", async () => {
|
||||||
|
const response = await sendTestRequest(
|
||||||
|
new Request(
|
||||||
|
new URL(
|
||||||
|
`${meta.route}?${new URLSearchParams(
|
||||||
|
notifications.slice(1).map((n) => ["ids[]", n.id]),
|
||||||
|
).toString()}`,
|
||||||
|
config.http.base_url,
|
||||||
|
),
|
||||||
|
{
|
||||||
|
method: "DELETE",
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${tokens[0].accessToken}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should not display dismissed notification", async () => {
|
||||||
|
const response = await sendTestRequest(
|
||||||
|
new Request(
|
||||||
|
new URL("/api/v1/notifications", config.http.base_url),
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${tokens[0].accessToken}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
|
||||||
|
const output = await response.json();
|
||||||
|
|
||||||
|
expect(output.length).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
41
server/api/api/v1/notifications/destroy_multiple/index.ts
Normal file
41
server/api/api/v1/notifications/destroy_multiple/index.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { apiRoute, applyConfig, idValidator } from "@api";
|
||||||
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
|
import { inArray } from "drizzle-orm";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { db } from "~drizzle/db";
|
||||||
|
import { notification } from "~drizzle/schema";
|
||||||
|
|
||||||
|
export const meta = applyConfig({
|
||||||
|
allowedMethods: ["DELETE"],
|
||||||
|
route: "/api/v1/notifications/destroy_multiple",
|
||||||
|
ratelimits: {
|
||||||
|
max: 100,
|
||||||
|
duration: 60,
|
||||||
|
},
|
||||||
|
auth: {
|
||||||
|
required: true,
|
||||||
|
oauthPermissions: ["write:notifications"],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const schema = z.object({
|
||||||
|
ids: z.array(z.string().regex(idValidator)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default apiRoute<typeof meta, typeof schema>(
|
||||||
|
async (req, matchedRoute, extraData) => {
|
||||||
|
const { user } = extraData.auth;
|
||||||
|
if (!user) return errorResponse("Unauthorized", 401);
|
||||||
|
|
||||||
|
const { ids } = extraData.parsedRequest;
|
||||||
|
|
||||||
|
await db
|
||||||
|
.update(notification)
|
||||||
|
.set({
|
||||||
|
dismissed: true,
|
||||||
|
})
|
||||||
|
.where(inArray(notification.id, ids));
|
||||||
|
|
||||||
|
return jsonResponse({});
|
||||||
|
},
|
||||||
|
);
|
||||||
Loading…
Reference in a new issue