Skip to content

Slice 2 (Module 02): Delete document with atomic cascade #10

Closed

Description

@mGasiorek998

What to build

Add the ability for a patient to permanently delete one of their documents. The deletion must be atomic: document_shares rows are removed and the documents row is deleted within a single transaction, then the file is removed from disk. If the disk delete fails after the transaction commits, return a 500 with a descriptive body (EH-002 partial-failure path) — the committed DB state is the source of truth. Add a delete button to the document list on /patient/documents.

Acceptance criteria

  • DELETE /documents/:id by the owning patient returns 204; the documents row, all associated document_shares rows, and the UUID file on disk are all gone.
  • The DB removal (documents row + shares rows) occurs inside a single transaction — no observable intermediate state where one is absent and the other is not.
  • Deleting a document that is currently shared with one or more doctors removes the sharing records atomically along with the document row.
  • If the file cannot be deleted from disk after the transaction commits, the endpoint returns 500 with { error: "DELETE_FAILED", message: "Document metadata removed but file could not be deleted from storage" }.
  • DELETE /documents/:id for a document owned by a different patient returns 403.
  • DELETE /documents/:id for a non-existent id returns 404 { error: "DOCUMENT_NOT_FOUND", ... }.
  • The delete button on the /patient/documents UI removes the entry from the list on success.
  • pnpm verify exits 0.

User stories covered

Blocked by

Activity

  1. added
    afkEligible for the main agent loop
    sliceVertical tracer-bullet slice
    in-progressCurrently being worked on by an iteration (semaphore)
    on May 7, 2026
  2. mGasiorek998 commented on May 7, 2026

    @mGasiorek998
    Author

    Plan: Add deleteDocument use case in apps/api/src/use-cases/documents/ that runs a single Drizzle transaction deleting from document_shares then documents (cascade ensures atomicity at the SQL level too — but explicit txn pattern documents intent). After commit, attempt unlink via the storage service; on failure return 500 DELETE_FAILED with body matching AC. Wire DELETE /documents/:id route behind requireAuth() + requireRole(['patient']), with 404 for unknown id, 403 when caller doesn't own the document. Fill packages/contracts/src/documents.ts with the new contract endpoint. On the web side: add useDeleteDocument mutation hook in apps/web/src/features/documents/ and wire a delete button on the patient documents list. Tests planned: HTTP integration tests (204 happy path + cascade verification, 403 ownership, 404 unknown, 500 disk-failure path), use-case integration test against real Postgres for transaction atomicity, web component test for delete button. Rough order: contract → DB transaction use case → HTTP route → UI button. Seven red→green cycles.

  3. added
    qa-readyPR opened, awaiting human QA
    and removed
    in-progressCurrently being worked on by an iteration (semaphore)
    on May 7, 2026
  4. mGasiorek998 commented on May 7, 2026

    @mGasiorek998
    Author

    Implementation PR opened: #16. One red commit (7 failing integration tests covering all backend ACs), one green commit (repo + use case + route + contract + UI). All 75 tests pass; pnpm verify clean. Note: stacked on #15 — needs to merge after Slice 1 lands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    afkEligible for the main agent looppriority:2Priority 2qa-readyPR opened, awaiting human QAsliceVertical tracer-bullet slice

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions