diff --git a/backend/src/handlers/medications.rs b/backend/src/handlers/medications.rs index 7683932..1aed58b 100644 --- a/backend/src/handlers/medications.rs +++ b/backend/src/handlers/medications.rs @@ -2,7 +2,6 @@ use axum::{ extract::{Extension, Json, Path, Query, State}, http::StatusCode, }; -use mongodb::bson::oid::ObjectId; use std::time::SystemTime; use crate::{ @@ -109,13 +108,12 @@ pub async fn get_medication( let database = state.db.get_database(); let repo = MedicationRepository::new(database.collection("medications")); - match ObjectId::parse_str(&id) { - Ok(oid) => match repo.find_by_id(&oid).await { - Ok(Some(medication)) => Ok(Json(medication.into())), - Ok(None) => Err(StatusCode::NOT_FOUND), - Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR), - }, - Err(_) => Err(StatusCode::BAD_REQUEST), + // The path param is the application-level medication_id (a UUID), not the + // Mongo _id, so look it up directly instead of parsing as ObjectId. + match repo.find_by_medication_id(&id).await { + Ok(Some(medication)) => Ok(Json(medication.into())), + Ok(None) => Err(StatusCode::NOT_FOUND), + Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR), } } @@ -128,13 +126,11 @@ pub async fn update_medication( let database = state.db.get_database(); let repo = MedicationRepository::new(database.collection("medications")); - match ObjectId::parse_str(&id) { - Ok(oid) => match repo.update(&oid, req).await { - Ok(Some(medication)) => Ok(Json(medication.into())), - Ok(None) => Err(StatusCode::NOT_FOUND), - Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR), - }, - Err(_) => Err(StatusCode::BAD_REQUEST), + // Look up by medication_id (UUID) — the path param is not a Mongo ObjectId. + match repo.update_by_medication_id(&id, req).await { + Ok(Some(medication)) => Ok(Json(medication.into())), + Ok(None) => Err(StatusCode::NOT_FOUND), + Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR), } } @@ -146,13 +142,11 @@ pub async fn delete_medication( let database = state.db.get_database(); let repo = MedicationRepository::new(database.collection("medications")); - match ObjectId::parse_str(&id) { - Ok(oid) => match repo.delete(&oid).await { - Ok(true) => Ok(StatusCode::NO_CONTENT), - Ok(false) => Err(StatusCode::NOT_FOUND), - Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR), - }, - Err(_) => Err(StatusCode::BAD_REQUEST), + // Look up by medication_id (UUID), not Mongo _id. + match repo.delete_by_medication_id(&id).await { + Ok(true) => Ok(StatusCode::NO_CONTENT), + Ok(false) => Err(StatusCode::NOT_FOUND), + Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR), } } diff --git a/backend/src/models/medication.rs b/backend/src/models/medication.rs index 247cae7..b39f642 100644 --- a/backend/src/models/medication.rs +++ b/backend/src/models/medication.rs @@ -362,6 +362,118 @@ impl MedicationRepository { Ok(medication) } + /// Look up by the application-level medication_id (a UUID string), which is + /// what the API exposes in URLs. (find_by_id filters on the Mongo _id.) + pub async fn find_by_medication_id( + &self, + medication_id: &str, + ) -> Result, Box> { + let filter = doc! { "medicationId": medication_id }; + let medication = self.collection.find_one(filter, None).await?; + Ok(medication) + } + + /// Update by the application-level medication_id (UUID). The API exposes + /// medication_id in URLs, not the Mongo _id. + pub async fn update_by_medication_id( + &self, + medication_id: &str, + updates: UpdateMedicationRequest, + ) -> Result, Box> { + let filter = doc! { "medicationId": medication_id }; + let existing = self.collection.find_one(filter.clone(), None).await?; + let Some(existing) = existing else { + return Ok(None); + }; + + let mut data: MedicationData = + serde_json::from_str(&existing.medication_data.data).unwrap_or_default(); + + if let Some(v) = updates.name { + data.name = v; + } + if let Some(v) = updates.dosage { + data.dosage = v; + } + if let Some(v) = updates.frequency { + data.frequency = v; + } + if let Some(v) = updates.route { + data.route = v; + } + if let Some(v) = updates.reason { + data.reason = Some(v); + } + if let Some(v) = updates.instructions { + data.instructions = Some(v); + } + if let Some(v) = updates.side_effects { + data.side_effects = v; + } + if let Some(v) = updates.prescribed_by { + data.prescribed_by = Some(v); + } + if let Some(v) = updates.prescribed_date { + data.prescribed_date = Some(v); + } + if let Some(v) = updates.start_date { + data.start_date = Some(v); + } + if let Some(v) = updates.end_date { + data.end_date = Some(v); + } + if let Some(v) = updates.notes { + data.notes = Some(v); + } + if let Some(v) = updates.tags { + data.tags = v; + } + // NOTE: updates.active is accepted by the API but not yet persisted. + + let data_json = serde_json::json!({ + "name": data.name, + "dosage": data.dosage, + "frequency": data.frequency, + "route": data.route, + "reason": data.reason, + "instructions": data.instructions, + "sideEffects": data.side_effects, + "prescribedBy": data.prescribed_by, + "prescribedDate": data.prescribed_date, + "startDate": data.start_date, + "endDate": data.end_date, + "notes": data.notes, + "tags": data.tags, + }) + .to_string(); + + let mut update_doc = doc! { + "medicationData.data": data_json, + "updatedAt": DateTime::now(), + }; + if let Some(pill_identification) = updates.pill_identification { + if let Ok(pill_doc) = mongodb::bson::to_document(&pill_identification) { + update_doc.insert("pill_identification", pill_doc); + } + } + + let medication = self + .collection + .find_one_and_update(filter, doc! { "$set": update_doc }, None) + .await?; + Ok(medication) + } + + /// Delete by the application-level medication_id (UUID). + pub async fn delete_by_medication_id( + &self, + medication_id: &str, + ) -> Result> { + let filter = doc! { "medicationId": medication_id }; + let result = self.collection.delete_one(filter, None).await?; + Ok(result.deleted_count > 0) + } + pub async fn update( &self, id: &ObjectId,