ALL-1871 PR-4: Backend PATCH /v1/bulk-upload/preview/validate endpoint + import wire-up
queued## ALL-1871 PR-4 - Backend: re-validate endpoint + import edits wire-up
Linear: ALL-1871 ("In-line editing of CSV data in bulk upload preview")
Branch: wilbo/all-1871-backend-edits
Depends on: PR-3 merged (frontend complete)
### What to build
Two backend changes:
1. A new PATCH endpoint so the frontend can validate a set of edited rows server-side without re-uploading the full file
2. Modify the import endpoint to accept an optional editedRows payload that gets merged into the CSV data before creating staged records
### 1. New endpoint: PATCH /v1/bulk-upload/preview/validate
File: domains/identity/src/routers/v1/bulk-upload/preview.ts (add handler) OR new file previewValidate.ts
Request body (JSON, not multipart):
```json
{
"token": "<preview token from original POST /preview response if implemented, or sessionId>",
"rows": [
{ "rowIndex": 2, "referenceId": "CUST001", "firstName": "...", ... }
]
}
```
NOTE: The current preview endpoint does NOT persist a session token. Two approaches:
Option A (simpler): Accept the full edited rows array directly in the request body, re-run validation only on submitted rows, return errors. No token needed. Frontend sends only the edited rows.
Option B: Store preview results keyed by a short-lived token (Redis/in-memory) and accept partial row patches.
RECOMMEND Option A for this PR -- avoids new state management.
Response:
```json
{
"validationErrors": [{ "row": 2, "field": "email", "message": "...", "value": "..." }],
"validRows": 1,
"invalidRows": 0
}
```
Validation to run:
- Same rules as parseCSV validation (use the existing validateRow function from domains/identity/src/services/bulk-upload/common/validation.ts)
- Address validation: call validateAddressesBatch (from addressValidation.ts) -- same as preview does
- Profile-aware (pass same profile as original upload)
Auth: same middleware stack as preview (validateAuthentication + validateToken, see index.ts)
### 2. Modify POST /v1/bulk-upload/import to accept editedRows
File: domains/identity/src/routers/v1/bulk-upload/import.ts
Add optional multipart field: `editedRows` (JSON string of Record<number, Record<string, string>>)
- If present, after parseCSV, iterate over editedRows and overwrite row fields before importRow processing
- editedRows keys are 0-indexed row array positions (not 1-indexed row numbers)
- Only overwrite keys present in editedRows[i] (sparse patch, not full row replacement)
File: apps/dashboard/lib/bulk-upload/client.ts
- Update import() method to accept editedRows option and append as JSON string to FormData
File: apps/dashboard/hooks/useBulkUpload.ts
- Update useImportUpload to accept editedRows in options
File: apps/dashboard/components/pages/ConnectPage/BulkUploadTab.tsx
- Pass editedCells (from useState in BulkUploadTab) as the editedRows option to importFile in handleStartImport
### Acceptance test
- User uploads CSV with 2 address errors
- User corrects both addresses in the preview table (PRs 1-3)
- User clicks Import
- Network tab: import POST includes editedRows field with the two corrected rows
- Import job succeeds; created entities reflect the corrected addresses, not the original CSV values
- (Optional smoke test): PATCH /v1/bulk-upload/preview/validate with a row containing an invalid email returns a 200 with validationErrors containing that row
### Dependencies
- Depends on PR-3 merged (full frontend stack complete, editedCells available in BulkUploadTab)
### Tests
- New test in domains/identity/src/routers/v1/bulk-upload/ for the PATCH endpoint
- Update import.ts integration test to cover editedRows merging
Event Timeline
created