Files
plant-disease-id/tasks/multi-image-user-feedback/08-feedback-api-endpoint.md

108 lines
3.9 KiB
Markdown

# 08. Feedback API Endpoint for Accuracy Ratings and Storage Consent
meta:
id: multi-image-user-feedback-08
feature: multi-image-user-feedback
priority: P1
depends_on: [multi-image-user-feedback-01]
tags: [api, backend, database]
objective:
- Create a POST endpoint at `/api/feedback` that accepts diagnosis feedback submissions (accuracy rating, notes, image storage consent) and persists them to the database.
deliverables:
- `src/app/api/feedback/route.ts` — new API route
- `src/app/api/feedback/feedback.test.ts` — test file
steps:
1. Create `src/app/api/feedback/route.ts`:
Route: `POST /api/feedback`
Accepts JSON body matching `FeedbackRequest`:
```typescript
{
sessionId: string;
imageIds: string[];
userSpecies?: string;
predictedDiseaseId: string;
accuracyRating: "correct" | "incorrect" | "unsure";
consentToStoreImages: boolean;
userCorrectedSpecies?: string;
notes?: string;
}
```
Handler logic:
- Parse and validate request body
- Generate UUID for `id`
- Get `modelVersion` from model loader's `getStatus()`
- Set `createdAt` to current timestamp
- Insert into `diagnosisFeedback` table via Drizzle
- Return `FeedbackResponse`: `{ success: true, id: string }`
- Handle validation errors with 400 status
- Handle DB errors with 500 status
Validation rules:
- `sessionId` — required, non-empty string
- `imageIds` — required, array of non-empty strings, min length 1
- `accuracyRating` — required, must be one of "correct", "incorrect", "unsure"
- `consentToStoreImages` — required, boolean
- `userSpecies` — optional string
- `userCorrectedSpecies` — optional string, only meaningful when accuracy is not "correct"
- `notes` — optional string, max 500 characters (with error message if exceeded)
2. Create `src/lib/api/feedback.ts` — client-side helper:
- `submitFeedback(data: FeedbackRequest): Promise<FeedbackResponse>`
- POST to `/api/feedback` with JSON body
- 15-second timeout
- Handle network errors gracefully
3. Handle edge cases:
- Invalid JSON body → 400 with descriptive error
- Missing required fields → 400 listing missing fields
- Invalid accuracyRating value → 400 with allowed values
- Database unreachable → 500 with error message
- Duplicate sessionId → allowed (user can submit multiple times for different predictions)
4. CORS and caching:
- Add `Cache-Control: no-store` header
- No authentication required (public endpoint for feedback)
tests:
- Unit: valid feedback submission returns 200 with success
- Unit: missing required fields return 400
- Unit: invalid accuracyRating returns 400
- Unit: notes over 500 chars returns 400
- Unit: empty imageIds array returns 400
- Unit: client helper `submitFeedback()` makes correct fetch call
- Unit: client helper handles network error gracefully
- Integration: submit feedback and verify it exists in database
acceptance_criteria:
- POST /api/feedback accepts valid feedback and stores it
- Invalid requests return appropriate 400 errors with descriptive messages
- Database stores all fields correctly
- Client helper function is usable from any feedback component
- Endpoint returns `{ success: true, id }` on success
validation:
- `npx tsc --noEmit` passes
- Unit tests pass: `npx vitest run src/app/api/feedback/`
- Manual test: `curl -X POST http://localhost:3000/api/feedback -H 'Content-Type: application/json' -d '{"sessionId":"test","imageIds":["img1"],"predictedDiseaseId":"early-blight","accuracyRating":"correct","consentToStoreImages":false}'`
- Verify stored data with direct DB query
notes:
- No auth needed for MVP — feedback is public and anonymous
- imageIds reference images in the uploads directory; no automatic cleanup
- A future task could add a review/admin dashboard for browsing feedback entries
- Rate limiting could be added later if needed (by sessionId or IP)