108 lines
3.9 KiB
Markdown
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)
|