feat: artist item API — detail, auto-monitor toggle, unfollow
This commit is contained in:
@@ -0,0 +1,60 @@
|
|||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import { prisma } from "@/lib/db";
|
||||||
|
import { GET, PATCH, DELETE } from "./route";
|
||||||
|
|
||||||
|
const ctx = (id: string) => ({ params: Promise.resolve({ id }) });
|
||||||
|
|
||||||
|
async function seedArtist() {
|
||||||
|
return prisma.watchedArtist.create({
|
||||||
|
data: {
|
||||||
|
mbid: "a1",
|
||||||
|
name: "John Mayer",
|
||||||
|
releases: {
|
||||||
|
create: [
|
||||||
|
{ artistMbid: "a1", artistName: "John Mayer", rgMbid: "rg1", album: "Continuum", primaryType: "Album", secondaryTypes: [], firstReleaseDate: "2006-09-12", monitored: true, state: "grabbed", currentQualityClass: 1 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function patchReq(body: unknown) {
|
||||||
|
return new Request("http://localhost/x", { method: "PATCH", headers: { "content-type": "application/json" }, body: JSON.stringify(body) });
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("artist item API", () => {
|
||||||
|
it("returns detail with releases", async () => {
|
||||||
|
const a = await seedArtist();
|
||||||
|
const res = await GET(new Request("http://localhost/x"), ctx(a.id));
|
||||||
|
expect(res.status).toBe(200);
|
||||||
|
const body = await res.json();
|
||||||
|
expect(body.name).toBe("John Mayer");
|
||||||
|
expect(body.releases[0]).toMatchObject({ rgMbid: "rg1", album: "Continuum", monitored: true, state: "grabbed", currentQualityClass: 1 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("404s an unknown artist on GET", async () => {
|
||||||
|
expect((await GET(new Request("http://localhost/x"), ctx("nope"))).status).toBe(404);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("toggles autoMonitorFuture", async () => {
|
||||||
|
const a = await seedArtist();
|
||||||
|
const res = await PATCH(patchReq({ autoMonitorFuture: true }), ctx(a.id));
|
||||||
|
expect(res.status).toBe(200);
|
||||||
|
expect((await res.json()).autoMonitorFuture).toBe(true);
|
||||||
|
expect((await prisma.watchedArtist.findUnique({ where: { id: a.id } }))!.autoMonitorFuture).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("400s a non-boolean toggle and 404s an unknown artist", async () => {
|
||||||
|
const a = await seedArtist();
|
||||||
|
expect((await PATCH(patchReq({ autoMonitorFuture: "yes" }), ctx(a.id))).status).toBe(400);
|
||||||
|
expect((await PATCH(patchReq({ autoMonitorFuture: true }), ctx("nope"))).status).toBe(404);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("unfollows and cascades releases", async () => {
|
||||||
|
const a = await seedArtist();
|
||||||
|
const res = await DELETE(new Request("http://localhost/x"), ctx(a.id));
|
||||||
|
expect(res.status).toBe(204);
|
||||||
|
expect(await prisma.watchedArtist.findUnique({ where: { id: a.id } })).toBeNull();
|
||||||
|
expect(await prisma.monitoredRelease.count({ where: { rgMbid: "rg1" } })).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import { prisma } from "@/lib/db";
|
||||||
|
|
||||||
|
export async function GET(_request: Request, { params }: { params: Promise<{ id: string }> }) {
|
||||||
|
const { id } = await params;
|
||||||
|
const artist = await prisma.watchedArtist.findUnique({
|
||||||
|
where: { id },
|
||||||
|
include: { releases: { orderBy: [{ firstReleaseDate: "desc" }, { album: "asc" }] } },
|
||||||
|
});
|
||||||
|
if (!artist) return Response.json({ error: "not found" }, { status: 404 });
|
||||||
|
return Response.json({
|
||||||
|
id: artist.id,
|
||||||
|
mbid: artist.mbid,
|
||||||
|
name: artist.name,
|
||||||
|
autoMonitorFuture: artist.autoMonitorFuture,
|
||||||
|
releases: artist.releases.map((r) => ({
|
||||||
|
id: r.id,
|
||||||
|
rgMbid: r.rgMbid,
|
||||||
|
album: r.album,
|
||||||
|
primaryType: r.primaryType,
|
||||||
|
secondaryTypes: r.secondaryTypes,
|
||||||
|
firstReleaseDate: r.firstReleaseDate,
|
||||||
|
monitored: r.monitored,
|
||||||
|
state: r.state,
|
||||||
|
currentQualityClass: r.currentQualityClass,
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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 { autoMonitorFuture } = (body ?? {}) as { autoMonitorFuture?: unknown };
|
||||||
|
if (typeof autoMonitorFuture !== "boolean") {
|
||||||
|
return Response.json({ error: "autoMonitorFuture (boolean) is required" }, { status: 400 });
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const updated = await prisma.watchedArtist.update({ where: { id }, data: { autoMonitorFuture } });
|
||||||
|
return Response.json({ id: updated.id, autoMonitorFuture: updated.autoMonitorFuture });
|
||||||
|
} catch {
|
||||||
|
return Response.json({ error: "not found" }, { status: 404 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function DELETE(_request: Request, { params }: { params: Promise<{ id: string }> }) {
|
||||||
|
const { id } = await params;
|
||||||
|
try {
|
||||||
|
await prisma.watchedArtist.delete({ where: { id } });
|
||||||
|
return new Response(null, { status: 204 });
|
||||||
|
} catch {
|
||||||
|
return Response.json({ error: "not found" }, { status: 404 });
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user