Skip to content

fix(patients): repair main's broken pipeline wiring and SSE auth - #51

Open
Ndifreke000 wants to merge 1 commit into
NexusHealthAll:mainfrom
Ndifreke000:fix/patient-pipeline-build-and-sse-auth
Open

Ndifreke000 wants to merge 1 commit into
NexusHealthAll:mainfrom
Ndifreke000:fix/patient-pipeline-build-and-sse-auth

Conversation

@Ndifreke000

Copy link
Copy Markdown
Collaborator

Context

main is currently broken. #38 (fix/ml-pipeline-wiring-and-migrations) fixed the migration version collision and re-exported the patient module, but left the router in a state that doesn't build, and has an auth regression once it does.

What's broken on main right now

  1. Doesn't compile. app_routes.rs calls pipeline::pipeline_events but never adds pipeline to the use crate::handlers::{...} import, and AppState is missing the pipeline_events field that pipeline.rs's handler reads (state.pipeline_events.subscribe()). cargo build fails with unresolved module or unlinked crate 'pipeline' and no field 'pipeline_events' on type 'AppState'.
  2. Panics on startup once that's fixed. POST /api/v1/ingest/patient and GET /api/v1/patients/{id} are registered twice — once in a new route block fix(pipeline): resolve migration version collision, export patient mo… #38 added, once in the original block from an earlier partial restore (ef2454e) that was never removed. Axum panics at router-build time: Overlapping method route. Handler for 'POST /api/v1/ingest/patient' already exists.
  3. SSE auth regression. GET /api/v1/pipeline/events only accepts a header-based JWT (extract_claims). The browser's native EventSource API (used by the nexusApp frontend for live triage updates) can't set an Authorization header, so every real SSE connection from the deployed frontend 401s.

What this PR does

  • Adds pipeline to the handler import list in app_routes.rs and adds the pipeline_events: Arc<broadcast::Sender<PipelineEvent>> field to AppState, wired to the broadcast channel that's already constructed for the prediction worker (it just wasn't exposed to the route before).
  • Removes the duplicate ingest_patient/get_patient route registration, keeping list_patients and pipeline_events alongside the original two in one block.
  • Adds extract_claims_with_query_fallback in utils/jwt.rs (header first, falling back to a ?token= query param) and wires it into pipeline.rs's handler and require_role middleware, so SSE and any require_role-gated route accept the fallback.

Testing

  • cargo build — clean (previously failed to compile on current main).
  • Started the server locally — previously panicked on startup, now starts and stays up.
  • cargo test --test patient_ingest_tests — 2 passed, 0 failed.
  • Manually verified against a real Postgres + trained ml-service, with a minted JWT:
    • GET /api/v1/patients200 with real data (was unreachable — server wouldn't start).
    • GET /api/v1/pipeline/events?token=...200 (was 401).
    • GET /api/v1/pipeline/events with no token → 401 (auth still enforced).
    • Submitted a patient via POST /api/v1/ingest/patient → worker resolved a real prediction via ml-service in under a second, visible on GET /api/v1/patients/{id}.

Test plan for reviewers

  • Pull this branch, confirm cargo build succeeds and the server starts without panicking.
  • Run against a real Postgres + ml-service, confirm GET /api/v1/patients and GET /api/v1/pipeline/events?token=<jwt> both return 200.
  • Confirm CI actually gates this before merge.

PR NexusHealthAll#38 (fix/ml-pipeline-wiring-and-migrations) left main in a state that
doesn't compile and, once fixed to compile, panics on startup:

- `pipeline` was never added to the `use crate::handlers::{...}` import in
  app_routes.rs, and `AppState` was missing the `pipeline_events` field
  pipeline.rs's handler needs -- so `cargo build` fails outright with
  "unresolved module `pipeline`" and "no field `pipeline_events`".
- Once that's fixed, the router panics at startup: POST /api/v1/ingest/patient
  and GET /api/v1/patients/{id} were registered twice -- once in a new block
  PR NexusHealthAll#38 added, once in the original block from an earlier partial restore
  that was never removed. Axum's router refuses overlapping routes.
- Separately, GET /api/v1/pipeline/events only accepted a header-based JWT.
  The browser's native EventSource API (used for the frontend's live triage
  updates) can't set an Authorization header, so every real SSE connection
  401'd. Restored extract_claims_with_query_fallback (header first, falling
  back to a ?token= query param) and wired it into both the SSE handler and
  require_role, so routes gated by that middleware accept the fallback too.

Verified locally end to end: backend builds and starts cleanly,
GET /api/v1/patients returns real data, GET /api/v1/pipeline/events returns
200 with ?token= and 401 with neither, and a submitted patient resolves a
real prediction via ml-service in under a second.

cargo test --test patient_ingest_tests: 2 passed, 0 failed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant