From 76067d76acdc08c41aff96440ff02590922e8ceb Mon Sep 17 00:00:00 2001 From: quirinecker Date: Tue, 3 Mar 2026 15:15:06 +0100 Subject: [PATCH] added http endpoint to update alarms. For now it will not update the schedules automatically --- snooze-pal.db | Bin 20480 -> 20480 bytes src/dao/alarm.rs | 9 +++++ src/resources/alarm/mod.rs | 7 +++- src/resources/alarm/patch.rs | 73 +++++++++++++++++++++++++++++++++++ src/resources/alarm/post.rs | 8 +++- src/scheduler.rs | 38 +++++++++++++++--- src/types.rs | 5 ++- 7 files changed, 132 insertions(+), 8 deletions(-) create mode 100644 src/resources/alarm/patch.rs diff --git a/snooze-pal.db b/snooze-pal.db index 276a41851efca8c2c09cdca5e262f1de0d09c82c..5f318151e0bea4b3fe4e71f75a6a26a94ed10d9e 100644 GIT binary patch delta 93 zcmZozz}T>Wae_3X>_i!7M%j%COZ0hI_%j&zAMo$t@8QqbEGXc^Z=fg6%3#fDXk=hy rrmF;Eb4Y^O2FAJu#tMceR)$7ahUR*v7DmM-MVWc&n|thw1sKHvDODCL delta 42 ycmZozz}T>Wae_3X)I=F)MyZVnOZ2&z`Fj}nAMo$t@7XLUP{+Ty$G%vAQ49bQK@4sH diff --git a/src/dao/alarm.rs b/src/dao/alarm.rs index 0ee9aaf..92c64c0 100644 --- a/src/dao/alarm.rs +++ b/src/dao/alarm.rs @@ -1,3 +1,5 @@ +use std::i32; + use chrono::{DateTime, Utc}; use sea_orm::{ ActiveModelTrait, ActiveValue::Set, ColumnTrait, ConnectionTrait, DbErr, EntityTrait, QueryFilter @@ -34,3 +36,10 @@ pub async fn get_alarms(db: &C, enabled: Option) -> Re query.all(db).await } + +pub async fn get_alarm(db: &C, id: i32) -> Result, DbErr> { + let result = model::alarm::Entity::find_by_id(id) + .one(db).await; + + return result +} diff --git a/src/resources/alarm/mod.rs b/src/resources/alarm/mod.rs index b89a363..278af04 100644 --- a/src/resources/alarm/mod.rs +++ b/src/resources/alarm/mod.rs @@ -1,4 +1,5 @@ mod get; +mod patch; mod post; use utoipa_axum::{router::OpenApiRouter, routes}; @@ -6,5 +7,9 @@ use utoipa_axum::{router::OpenApiRouter, routes}; use crate::AppState; pub fn router() -> OpenApiRouter { - OpenApiRouter::new().routes(routes!(post::post_handler, get::get_handler)) + OpenApiRouter::new().routes(routes!( + post::post_handler, + get::get_handler, + patch::patch_handler + )) } diff --git a/src/resources/alarm/patch.rs b/src/resources/alarm/patch.rs new file mode 100644 index 0000000..dcd26d4 --- /dev/null +++ b/src/resources/alarm/patch.rs @@ -0,0 +1,73 @@ +use axum::Json; +use axum::http::StatusCode; +use axum::{debug_handler, response::IntoResponse}; +use axum::extract::{Path, State}; +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +use utoipa::{IntoResponses, ToSchema}; + +use crate::AppState; + +#[derive(ToSchema, Serialize, Deserialize)] +pub struct OkResponse { + id: i32, + title: String, + enabled: bool, + time: DateTime, +} + +#[derive(ToSchema, Deserialize)] +pub struct PatchRequestBody { + title: Option, + enabled: Option, + time: Option>, +} + +#[derive(IntoResponses)] +pub enum Responses { + #[response(status = 200)] + Ok(#[to_schema] OkResponse), + #[response(status = 500, description = "Something failed in the Database when trying to update the alarm")] + DbError(String), + #[response(status = 404, description = "The alarm you want to update was not found")] + NotFound, +} + +impl IntoResponse for Responses { + fn into_response(self) -> axum::response::Response { + match self { + Responses::Ok(body) => (StatusCode::OK, Json(body)).into_response(), + Responses::DbError(message) => (StatusCode::INTERNAL_SERVER_ERROR, Json(message)).into_response(), + Responses::NotFound => (StatusCode::NOT_FOUND).into_response(), + } + } +} + +impl From for OkResponse { + fn from(value: crate::types::Alarm) -> Self { + OkResponse { + id: value.id, + title: value.title, + enabled: value.enabled, + time: value.time, + } + } +} + +#[utoipa::path(patch, path = "/{id}", responses(Responses))] +#[debug_handler] +pub async fn patch_handler(State(AppState { scheduler }): State, Path(id): Path, Json(body): Json) -> Responses { + let result = scheduler.update_alarm(id, body.title, body.enabled, body.time).await; + + match result { + Ok(Some(alarm)) => { + Responses::Ok(alarm.into()) + }, + Ok(None) => { + Responses::NotFound + }, + Err(error) => { + Responses::DbError(error.to_string()) + } + } +} diff --git a/src/resources/alarm/post.rs b/src/resources/alarm/post.rs index 9f4ec1a..7c8f16c 100644 --- a/src/resources/alarm/post.rs +++ b/src/resources/alarm/post.rs @@ -18,6 +18,7 @@ pub struct RequestBody { #[derive(ToSchema, Serialize)] pub struct OkResponseBody { + id: i32, time: DateTime, title: String, enabled: bool, @@ -43,7 +44,12 @@ impl IntoResponse for Responses { impl From for OkResponseBody { fn from(value: Alarm) -> Self { - OkResponseBody { time: value.time, enabled: value.enabled, title: value.title } + OkResponseBody { + time: value.time, + enabled: value.enabled, + title: value.title, + id: value.id + } } } diff --git a/src/scheduler.rs b/src/scheduler.rs index 0908567..2fa04fb 100644 --- a/src/scheduler.rs +++ b/src/scheduler.rs @@ -1,10 +1,13 @@ +use std::i32; use std::sync::{Arc, Mutex}; use chrono::{DateTime, Local, Timelike, Utc}; use cron_tab::Cron; -use sea_orm::{DatabaseConnection, DbErr}; +use sea_orm::ActiveValue::Set; +use sea_orm::{ActiveModelTrait, DatabaseConnection, DbErr}; -use crate::dao::alarm::{create_alarm, get_alarms}; +use crate::dao::alarm::{create_alarm, get_alarm, get_alarms}; +use crate::model; use crate::ringer::Ringer; use crate::types::Alarm; @@ -50,16 +53,15 @@ impl Scheduler { pub async fn add_alarm(&self, time: DateTime, title: String) -> Result { let cron_schedule = self.construct_cron_schedule(time); - let alarm = Alarm::new(true, time, title.clone()); - create_alarm(&*self.db, title, time) + let created_alarm = create_alarm(&*self.db, title, time) .await .map_err(|e| e.to_string())?; self.schedule(&cron_schedule).map_err(|e| e.to_string())?; println!("Added alarm {}", cron_schedule); - Ok(alarm) + Ok(created_alarm.into()) } pub async fn get_alarms(&self, enabled: Option) -> Result, DbErr> { @@ -71,6 +73,32 @@ impl Scheduler { ) } + pub async fn update_alarm(&self, id: i32, title: Option, enabled: Option, time: Option>) -> Result, DbErr> { + let alarm = get_alarm(&*self.db, id).await?; + + let Some(alarm) = alarm else { + return Ok(None) + }; + + let mut active_alarm: model::alarm::ActiveModel = alarm.into(); + + if let Some(title) = title { + active_alarm.title = Set(title.to_owned()); + } + + if let Some(enabled) = enabled { + active_alarm.enabled = Set(enabled); + } + + if let Some(time) = time { + active_alarm.time = Set(time.naive_utc()); + } + + let updated_alarm = active_alarm.update(&*self.db).await?; + + return Ok(Some(updated_alarm.into())); + } + pub async fn start(&self) { self.cron.lock().expect("Failed to lock cron").start(); self.register_existing_alarms().await; diff --git a/src/types.rs b/src/types.rs index f1e0aa7..aa58da8 100644 --- a/src/types.rs +++ b/src/types.rs @@ -5,14 +5,16 @@ use crate::model; #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Alarm { + pub id: i32, pub enabled: bool, pub time: DateTime, pub title: String, } impl Alarm { - pub fn new(enabled: bool, time: DateTime, title: String) -> Self { + pub fn new(id: i32, enabled: bool, time: DateTime, title: String) -> Self { Self { + id, enabled, time, title, @@ -23,6 +25,7 @@ impl Alarm { impl From for Alarm { fn from(value: model::alarm::Model) -> Self { Self { + id: value.id, enabled: value.enabled, time: DateTime::from_utc(value.time, Utc), title: value.title,