api: REST endpoints for ideas (M12 T-500)
- app/api/ideas/route.ts: GET (list with archived/product_id/status filters, user_id-scope), POST (creates DRAFT with auto IDEA-NNN code, 201) - app/api/ideas/[id]/route.ts: GET (idea + recent logs), PATCH (ideaUpdateSchema, isIdeaEditable guard) - lib/idea-dto.ts: API projection — converts Prisma row → DTO with lowercase status + has_grill_md/has_plan_md flags (md content excluded from list payloads, fetch via dedicated download action) Auth: session OR API-token via authenticateApiRequest. Strict user_id scope (no productAccessFilter — Idee is privé per Q8). 404 (not 403) for foreign-user reads to prevent enumeration. Tests: 13 cases (auth-401, demo-403, validation-422, malformed-400, not-found-404, status-mismatch-422, filter param round-trip, DTO shape). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6904de9f2b
commit
4b234dc300
4 changed files with 428 additions and 0 deletions
49
lib/idea-dto.ts
Normal file
49
lib/idea-dto.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
// API-projection voor Idea — converteert Prisma-row naar het externe contract.
|
||||
// Belangrijk: status wordt naar lowercase API-string vertaald (zelfde patroon
|
||||
// als TaskStatus / StoryStatus / PbiStatus elders in de codebase).
|
||||
|
||||
import { ideaStatusToApi } from '@/lib/idea-status'
|
||||
|
||||
import type { Idea, IdeaStatus, Product } from '@prisma/client'
|
||||
|
||||
type IdeaWithProduct = Idea & {
|
||||
product: Pick<Product, 'id' | 'name' | 'repo_url'> | null
|
||||
pbi?: { id: string; code: string; title: string } | null
|
||||
}
|
||||
|
||||
export interface IdeaDto {
|
||||
id: string
|
||||
code: string
|
||||
title: string
|
||||
description: string | null
|
||||
status: ReturnType<typeof ideaStatusToApi>
|
||||
product_id: string | null
|
||||
product: { id: string; name: string; repo_url: string | null } | null
|
||||
pbi_id: string | null
|
||||
pbi?: { id: string; code: string; title: string } | null
|
||||
archived: boolean
|
||||
has_grill_md: boolean
|
||||
has_plan_md: boolean
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export function ideaToDto(idea: IdeaWithProduct & { status: IdeaStatus }): IdeaDto {
|
||||
return {
|
||||
id: idea.id,
|
||||
code: idea.code,
|
||||
title: idea.title,
|
||||
description: idea.description,
|
||||
status: ideaStatusToApi(idea.status),
|
||||
product_id: idea.product_id,
|
||||
product: idea.product,
|
||||
pbi_id: idea.pbi_id,
|
||||
pbi: idea.pbi ?? null,
|
||||
archived: idea.archived,
|
||||
// Geen md-content in lijst-payloads (kan groot zijn) — enkel een vlag.
|
||||
has_grill_md: idea.grill_md !== null,
|
||||
has_plan_md: idea.plan_md !== null,
|
||||
created_at: idea.created_at.toISOString(),
|
||||
updated_at: idea.updated_at.toISOString(),
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue