feat: releases API — monitor toggle + search-now enqueue

This commit is contained in:
Jonathan
2026-07-11 14:00:49 +02:00
parent f5ceea8fde
commit 6615ecf3a7
4 changed files with 99 additions and 0 deletions
@@ -0,0 +1,30 @@
import { describe, it, expect } from "vitest";
import { prisma } from "@/lib/db";
import { PATCH } from "./route";
const ctx = (id: string) => ({ params: Promise.resolve({ id }) });
async function seedRelease(monitored = false) {
return prisma.monitoredRelease.create({
data: { artistMbid: "a1", artistName: "John Mayer", rgMbid: "rg1", album: "Continuum", secondaryTypes: [], monitored },
});
}
function patchReq(body: unknown) {
return new Request("http://localhost/x", { method: "PATCH", headers: { "content-type": "application/json" }, body: JSON.stringify(body) });
}
describe("PATCH /api/releases/[id]", () => {
it("toggles monitored", async () => {
const r = await seedRelease(false);
const res = await PATCH(patchReq({ monitored: true }), ctx(r.id));
expect(res.status).toBe(200);
expect((await res.json()).monitored).toBe(true);
expect((await prisma.monitoredRelease.findUnique({ where: { id: r.id } }))!.monitored).toBe(true);
});
it("400s a non-boolean and 404s an unknown release", async () => {
const r = await seedRelease();
expect((await PATCH(patchReq({ monitored: 1 }), ctx(r.id))).status).toBe(400);
expect((await PATCH(patchReq({ monitored: true }), ctx("nope"))).status).toBe(404);
});
});
+25
View File
@@ -0,0 +1,25 @@
import { prisma } from "@/lib/db";
import { Prisma } from "@prisma/client";
export async function PATCH(request: Request, { params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
let body: unknown;
try {
body = await request.json();
} catch {
return Response.json({ error: "invalid JSON" }, { status: 400 });
}
const { monitored } = (body ?? {}) as { monitored?: unknown };
if (typeof monitored !== "boolean") {
return Response.json({ error: "monitored (boolean) is required" }, { status: 400 });
}
try {
const updated = await prisma.monitoredRelease.update({ where: { id }, data: { monitored } });
return Response.json({ id: updated.id, monitored: updated.monitored });
} catch (e) {
if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === "P2025") {
return Response.json({ error: "not found" }, { status: 404 });
}
throw e;
}
}
@@ -0,0 +1,26 @@
import { describe, it, expect } from "vitest";
import { prisma } from "@/lib/db";
import { POST } from "./route";
const ctx = (id: string) => ({ params: Promise.resolve({ id }) });
describe("POST /api/releases/[id]/search", () => {
it("enqueues a Request+Job linked to the release and stamps lastSearchedAt", async () => {
const r = await prisma.monitoredRelease.create({
data: { artistMbid: "a1", artistName: "John Mayer", rgMbid: "rg1", album: "Continuum", secondaryTypes: [], monitored: true },
});
const res = await POST(new Request("http://localhost/x", { method: "POST" }), ctx(r.id));
expect(res.status).toBe(202);
expect((await res.json()).enqueued).toBe(true);
const reqs = await prisma.request.findMany({ where: { monitoredReleaseId: r.id }, include: { job: true } });
expect(reqs).toHaveLength(1);
expect(reqs[0]).toMatchObject({ artist: "John Mayer", album: "Continuum" });
expect(reqs[0].job!.state).toBe("requested");
expect((await prisma.monitoredRelease.findUnique({ where: { id: r.id } }))!.lastSearchedAt).not.toBeNull();
});
it("404s an unknown release", async () => {
expect((await POST(new Request("http://localhost/x", { method: "POST" }), ctx("nope"))).status).toBe(404);
});
});
@@ -0,0 +1,18 @@
import { prisma } from "@/lib/db";
export async function POST(_request: Request, { params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
const release = await prisma.monitoredRelease.findUnique({ where: { id } });
if (!release) return Response.json({ error: "not found" }, { status: 404 });
await prisma.request.create({
data: {
artist: release.artistName,
album: release.album,
monitoredReleaseId: release.id,
job: { create: {} },
},
});
await prisma.monitoredRelease.update({ where: { id }, data: { lastSearchedAt: new Date() } });
return Response.json({ enqueued: true }, { status: 202 });
}