added http endpoint to update alarms. For now it will not update the schedules automatically

This commit is contained in:
2026-03-03 15:15:06 +01:00
parent 37b8fe5e56
commit 76067d76ac
7 changed files with 132 additions and 8 deletions

Binary file not shown.

View File

@@ -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<C: ConnectionTrait>(db: &C, enabled: Option<bool>) -> Re
query.all(db).await
}
pub async fn get_alarm<C: ConnectionTrait>(db: &C, id: i32) -> Result<Option<model::alarm::Model>, DbErr> {
let result = model::alarm::Entity::find_by_id(id)
.one(db).await;
return result
}

View File

@@ -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<AppState> {
OpenApiRouter::new().routes(routes!(post::post_handler, get::get_handler))
OpenApiRouter::new().routes(routes!(
post::post_handler,
get::get_handler,
patch::patch_handler
))
}

View File

@@ -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<Utc>,
}
#[derive(ToSchema, Deserialize)]
pub struct PatchRequestBody {
title: Option<String>,
enabled: Option<bool>,
time: Option<DateTime<Utc>>,
}
#[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<crate::types::Alarm> 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<AppState>, Path(id): Path<i32>, Json(body): Json<PatchRequestBody>) -> 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())
}
}
}

View File

@@ -18,6 +18,7 @@ pub struct RequestBody {
#[derive(ToSchema, Serialize)]
pub struct OkResponseBody {
id: i32,
time: DateTime<Utc>,
title: String,
enabled: bool,
@@ -43,7 +44,12 @@ impl IntoResponse for Responses {
impl From<Alarm> 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
}
}
}

View File

@@ -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<Utc>, title: String) -> Result<Alarm, String> {
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<bool>) -> Result<Vec<Alarm>, DbErr> {
@@ -71,6 +73,32 @@ impl Scheduler {
)
}
pub async fn update_alarm(&self, id: i32, title: Option<String>, enabled: Option<bool>, time: Option<DateTime<Utc>>) -> Result<Option<Alarm>, 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;

View File

@@ -5,14 +5,16 @@ use crate::model;
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Alarm {
pub id: i32,
pub enabled: bool,
pub time: DateTime<Utc>,
pub title: String,
}
impl Alarm {
pub fn new(enabled: bool, time: DateTime<Utc>, title: String) -> Self {
pub fn new(id: i32, enabled: bool, time: DateTime<Utc>, title: String) -> Self {
Self {
id,
enabled,
time,
title,
@@ -23,6 +25,7 @@ impl Alarm {
impl From<model::alarm::Model> 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,