From 9f2b115721391cb9392c7b4030f6183e94afca99 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Sun, 12 Jul 2026 01:42:49 +0200 Subject: [PATCH] feat(preview): POST /api/discover/want (want a release-group by metadata) Co-Authored-By: Claude Opus 4.8 (1M context) --- web/src/app/api/discover/want/route.test.ts | 29 ++++++++++++++++++++ web/src/app/api/discover/want/route.ts | 30 +++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 web/src/app/api/discover/want/route.test.ts create mode 100644 web/src/app/api/discover/want/route.ts diff --git a/web/src/app/api/discover/want/route.test.ts b/web/src/app/api/discover/want/route.test.ts new file mode 100644 index 0000000..8acc408 --- /dev/null +++ b/web/src/app/api/discover/want/route.test.ts @@ -0,0 +1,29 @@ +import { describe, it, expect } from "vitest"; +import { prisma } from "@/lib/db"; +import { POST } from "./route"; + +function post(body: unknown) { + return POST(new Request("http://localhost/api/discover/want", { + method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body), + })); +} + +describe("discover want API", () => { + it("wants a release-group from metadata (201), idempotent re-want flips monitored", async () => { + const res = await post({ rgMbid: "rg1", artistMbid: "a1", artistName: "Band", album: "Rec", + primaryType: "Album", secondaryTypes: [], firstReleaseDate: "2020" }); + expect(res.status).toBe(201); + expect(await prisma.monitoredRelease.findUnique({ where: { rgMbid: "rg1" } })) + .toMatchObject({ album: "Rec", monitored: true, artistName: "Band" }); + + await prisma.monitoredRelease.update({ where: { rgMbid: "rg1" }, data: { monitored: false } }); + await post({ rgMbid: "rg1", artistMbid: "a1", artistName: "Band", album: "Rec" }); + expect((await prisma.monitoredRelease.findUnique({ where: { rgMbid: "rg1" } }))!.monitored).toBe(true); + expect(await prisma.monitoredRelease.count({ where: { rgMbid: "rg1" } })).toBe(1); + }); + + it("400s on a bad body", async () => { + expect((await post({ rgMbid: "rg1" })).status).toBe(400); + expect((await post({})).status).toBe(400); + }); +}); diff --git a/web/src/app/api/discover/want/route.ts b/web/src/app/api/discover/want/route.ts new file mode 100644 index 0000000..3a9ce13 --- /dev/null +++ b/web/src/app/api/discover/want/route.ts @@ -0,0 +1,30 @@ +import { prisma } from "@/lib/db"; + +export async function POST(request: Request) { + let body: unknown; + try { + body = await request.json(); + } catch { + return Response.json({ error: "invalid JSON" }, { status: 400 }); + } + const { rgMbid, artistMbid, artistName, album, primaryType, secondaryTypes, firstReleaseDate } = + (body ?? {}) as Record; + if ([rgMbid, artistMbid, artistName, album].some((v) => typeof v !== "string" || !v.trim())) { + return Response.json({ error: "rgMbid, artistMbid, artistName, album are required" }, { status: 400 }); + } + const rel = await prisma.monitoredRelease.upsert({ + where: { rgMbid: rgMbid as string }, + create: { + artistMbid: artistMbid as string, + artistName: artistName as string, + rgMbid: rgMbid as string, + album: album as string, + primaryType: typeof primaryType === "string" ? primaryType : null, + secondaryTypes: Array.isArray(secondaryTypes) ? (secondaryTypes as string[]) : [], + firstReleaseDate: typeof firstReleaseDate === "string" ? firstReleaseDate : null, + monitored: true, + }, + update: { monitored: true }, + }); + return Response.json({ id: rel.id, album: rel.album, monitored: rel.monitored }, { status: 201 }); +}