Repository navigation
Slice 3 (Module 02): Per-doctor sharing management (grant / revoke / state) #11
Copy link
Copy link
Closed
Labels
afkEligible for the main agent loopEligible for the main agent looppriority:2Priority 2Priority 2qa-readyPR opened, awaiting human QAPR opened, awaiting human QAsliceVertical tracer-bullet sliceVertical tracer-bullet slice
Description
What to build
Give patients fine-grained control over which doctors can access each document. This slice adds three endpoints — sharing state, grant, revoke — and wires them into a sharing panel on the /patient/documents page.
getSharingStateuse case: returns all doctors in the system, each with ahasAccessboolean for the given document.grantAccessuse case: upserts intodocument_shares(ON CONFLICT DO NOTHING — idempotent).revokeAccessuse case: deletes fromdocument_shareswhere matching pair exists (no error if absent — idempotent).GET /documents/:id/shares→ 200 +Array<{ doctorId, displayName, hasAccess }>behindrequireAuth()+requireRole(['patient']).PUT /documents/:id/shares/:doctorId→ 200 (grant) behindrequireAuth()+requireRole(['patient']).DELETE /documents/:id/shares/:doctorId→ 200 (revoke) behindrequireAuth()+requireRole(['patient']).- Fill in these three endpoints in
packages/contracts/src/documents.ts. - Sharing panel on
/patient/documents: per-document expandable list of all doctors with a toggle (grant/revoke). Ownership boundary enforced (patient can only manage their own documents).
Acceptance criteria
-
GET /documents/:id/sharesreturns all doctors with correcthasAccessflags; returns 404 for unknown document; returns 403 for documents not owned by the calling patient. -
PUT /documents/:id/shares/:doctorIdcreates adocument_sharesrow and returns 200; calling it again for the same pair returns 200 with exactly one row (no duplicate). -
DELETE /documents/:id/shares/:doctorIdremoves thedocument_sharesrow and returns 200; calling it when no share exists also returns 200 (idempotent). - Both grant and revoke return 404
{ error: "DOCTOR_NOT_FOUND", ... }whendoctorIddoes not correspond to a user with the doctor role. - Both grant and revoke return 403 when the calling patient does not own the document.
- All three endpoints return 401 without a valid JWT and 403 with a doctor JWT.
- The sharing panel in the UI reflects live state and updates immediately after toggling a doctor.
-
pnpm verifyexits 0.
User stories covered
- US Slice 4: Auth failure modes & token lifecycle hardening #4: As a patient, I want to see which doctors currently have access to a specific document.
- US Slice 1: Monorepo scaffold + /health tracer bullet #5: As a patient, I want to grant a specific doctor access to a specific document.
- US Slice 2: Login + role-aware dashboard happy path #6: As a patient, I want to revoke a specific doctor's access to a specific document.
- US fix: api-client doubles base URL in fetch call #7: As a patient, I want the grant and revoke actions to be idempotent.
- US Slice 4 (Module 02): Doctor "Shared with me" view + document download #12: As a patient, I want the system to reject requests referencing another patient's documents with a 403.
Blocked by
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
afkEligible for the main agent loopEligible for the main agent looppriority:2Priority 2Priority 2qa-readyPR opened, awaiting human QAPR opened, awaiting human QAsliceVertical tracer-bullet sliceVertical tracer-bullet slice
Activity
mGasiorek998 commentedon May 7, 2026
Plan: 3 new endpoints (GET /documents/:id/shares, PUT and DELETE /documents/:id/shares/:doctorId) wired with inline requireAuth + requireRole(['patient']) middleware (parent /documents .use() does not cover sub-paths, per Slice 2 precedent).
Files touched:
Order: 4 red→green cycles (GET, PUT, DELETE, UI). All API cycles touch the same backend; the UI cycle adds a Vitest component test against a mocked contract.
mGasiorek998 commentedon May 7, 2026
Implementation PR opened: #17. 1 red→green cycle, 17 new integration tests added (AC1–AC6). Backend covers GET /documents/:id/shares, PUT and DELETE /documents/:id/shares/:doctorId with idempotent grant/revoke and DOCTOR_NOT_FOUND/FORBIDDEN/DOCUMENT_NOT_FOUND failure modes. Web SharingPanel exposes per-doctor Share/Shared toggle on /patient/documents; TanStack Query invalidation makes the panel reflect live state. pnpm verify clean (85 tests).