pub mod request { use serde::{Deserialize, Serialize}; use utoipa::ToSchema; #[derive(Debug, Deserialize, Serialize, ToSchema)] pub struct ScheduleMessageRequest { #[serde(with = "time::serde::rfc3339::option")] pub scheduled: Option, pub status: String, pub user_id: uuid::Uuid, } impl ScheduleMessageRequest { pub fn is_valid(&self) -> bool { self.scheduled.is_some() || !self.status.is_empty() || !self.user_id.is_nil() } } #[derive(Debug, Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)] pub struct GetScheduledMessageParams { pub id: Option, pub user_id: Option, } #[derive(Debug, Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)] pub struct CreateScheduleMessageEventRequest { pub contact_id: uuid::Uuid, pub message_id: uuid::Uuid, pub scheduled_message_id: uuid::Uuid, } impl CreateScheduleMessageEventRequest { pub fn is_valid(&self) -> bool { !self.contact_id.is_nil() || !self.message_id.is_nil() || !self.scheduled_message_id.is_nil() } } #[derive(Debug, Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)] pub struct GetScheduledMessageEventParams { pub id: Option, pub scheduled_message_id: Option, } #[derive(Debug, Default, Deserialize, Serialize, ToSchema)] pub struct UpdateScheduledMessageStatusRequest { pub status: String, pub scheduled_message_id: uuid::Uuid, } impl UpdateScheduledMessageStatusRequest { pub fn is_valid(&self) -> bool { if !self.status.is_empty() || !self.scheduled_message_id.is_nil() { if self.status == textsender_models::message::scheduling::PENDING || self.status == textsender_models::message::scheduling::READY || self.status == textsender_models::message::scheduling::PROCESSING || self.status == textsender_models::message::scheduling::DONE { true } else { false } } else { false } } } } pub mod response { use serde::{Deserialize, Serialize}; use utoipa::ToSchema; #[derive(Debug, Default, Deserialize, Serialize, ToSchema)] pub struct ScheduleMessageResponse { pub message: String, pub data: Vec, } #[derive(Debug, Default, Deserialize, Serialize, ToSchema)] pub struct GetScheduledMessageResponse { pub message: String, pub data: Vec, } #[derive(Debug, Default, Deserialize, Serialize, ToSchema)] pub struct CreateScheduleMessageEventResponse { pub message: String, pub data: Vec, } pub use CreateScheduleMessageEventResponse as GetScheduledMessageEventResponse; pub use CreateScheduleMessageEventResponse as DeleteScheduledMessageEventResponse; #[derive(Debug, Default, Deserialize, Serialize, ToSchema)] pub struct UpdateScheduledMessageStatusResponse { pub message: String, pub data: Vec, } #[derive(Debug, Default, Deserialize, Serialize, ToSchema)] pub struct ChangedStatus { pub old_status: String, } } pub mod endpoint { use crate::repo::contact as contact_repo; use crate::repo::message as message_repo; use message_repo::scheduling as scheduling_repo; /// Endpoint to create a Scheudled Message #[utoipa::path( post, path = crate::caller::endpoints::SCHEDULE_MESSAGE, request_body( content = super::request::ScheduleMessageRequest, description = "Data needed to create a Scheduled Message", content_type = "application/json" ), responses( (status = 201, description = "Scheduled Message created", body = super::response::ScheduleMessageResponse), (status = 400, description = "Error", body = super::response::ScheduleMessageResponse), (status = 500, description = "Error creating Scheduled Message", body = super::response::ScheduleMessageResponse) ) )] pub async fn schedule_message( axum::Extension(pool): axum::Extension, axum::Json(payload): axum::Json, ) -> ( axum::http::StatusCode, axum::Json, ) { let mut response = super::response::ScheduleMessageResponse::default(); if payload.is_valid() { if payload.status != textsender_models::message::scheduling::PENDING { response.message = String::from("scheduled message must be pending"); (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) } else { if can_schedule(&payload.scheduled) { let mut scheduled_message = textsender_models::message::scheduling::ScheduledMessage { scheduled: payload.scheduled, status: payload.status, ..Default::default() }; match scheduling_repo::insert(&pool, &scheduled_message, &payload.user_id).await { Ok((id, created)) => { scheduled_message.id = id; scheduled_message.created = Some(created); scheduled_message.user_id = payload.user_id; response.message = String::from("Message scheduled"); response.data.push(scheduled_message); (axum::http::StatusCode::CREATED, axum::Json(response)) } Err(err) => { eprintln!("Error: {err:?}"); response.message = String::from("Error inserting"); ( axum::http::StatusCode::INTERNAL_SERVER_ERROR, axum::Json(response), ) } } } else { response.message = String::from("Cannot schedule message"); (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) } } } else { response.message = String::from("Request body is not valid"); (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) } } /// Endpoint to get Scheduled Message #[utoipa::path( get, path = crate::caller::endpoints::GET_SCHEDULE_MESSAGE, params( ("id" = uuid::Uuid, Path, description = "Id of Scheduled Message"), ("user_id" = uuid::Uuid, Path, description = "User Id associated with the Scheduled Message") ), responses( (status = 200, description = "Scheduled Message found", body = super::response::GetScheduledMessageResponse), (status = 400, description = "Error getting Scheduled Message", body = super::response::GetScheduledMessageResponse) ) )] pub async fn get_scheduled_messages( axum::Extension(pool): axum::Extension, axum::extract::Query(params): axum::extract::Query< super::request::GetScheduledMessageParams, >, ) -> ( axum::http::StatusCode, axum::Json, ) { let mut response = super::response::GetScheduledMessageResponse::default(); let scheduled_messages = match params.id { Some(id) => match scheduling_repo::get(&pool, &id).await { Ok(scheduled_message) => { vec![scheduled_message] } Err(err) => { eprintln!("Error: {err:?}"); return ( axum::http::StatusCode::INTERNAL_SERVER_ERROR, axum::Json(response), ); } }, None => match params.user_id { Some(user_id) => match scheduling_repo::get_with_user_id(&pool, &user_id).await { Ok(scheduled_messages) => scheduled_messages, Err(err) => { eprintln!("Error: {err:?}"); return ( axum::http::StatusCode::INTERNAL_SERVER_ERROR, axum::Json(response), ); } }, None => { response.message = String::from("Invalid parameter"); return (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)); } }, }; response.data = scheduled_messages; response.message = String::from(super::super::super::response::SUCCESSFUL); (axum::http::StatusCode::OK, axum::Json(response)) } fn can_schedule(scheduled_time: &Option) -> bool { scheduled_time .is_some_and(|t| t - time::OffsetDateTime::now_utc() >= time::Duration::minutes(10)) } /// Endpoint to update status of Scheduled Message #[utoipa::path( patch, path = crate::caller::endpoints::UPDATE_SCHEDULED_MESSAGE_STATUS, request_body( content = super::request::UpdateScheduledMessageStatusRequest, description = "Data needed to update status of Scheduled Message", content_type = "application/json" ), responses( (status = 200, description = "Status updated", body = super::response::UpdateScheduledMessageStatusResponse), (status = 400, description = "Error", body = super::response::UpdateScheduledMessageStatusResponse), (status = 500, description = "Error updating", body = super::response::UpdateScheduledMessageStatusResponse) ) )] pub async fn update_scheduled_message_status( axum::Extension(pool): axum::Extension, axum::Json(payload): axum::Json, ) -> ( axum::http::StatusCode, axum::Json, ) { let mut response = super::response::UpdateScheduledMessageStatusResponse::default(); if payload.is_valid() { match scheduling_repo::get(&pool, &payload.scheduled_message_id).await { Ok(scheduled_message) => { let old_status = scheduled_message.status; if old_status == payload.status { response.message = String::from("No change"); (axum::http::StatusCode::NOT_MODIFIED, axum::Json(response)) } else { match scheduling_repo::update_status( &pool, &scheduled_message.id, &payload.status, ) .await { Ok(_) => { response.message = String::from(super::super::super::response::SUCCESSFUL); response.data = vec![super::response::ChangedStatus { old_status }]; (axum::http::StatusCode::OK, axum::Json(response)) } Err(err) => { eprintln!("Error: {err:?}"); response.message = String::from("Invalid"); ( axum::http::StatusCode::INTERNAL_SERVER_ERROR, axum::Json(response), ) } } } } Err(err) => { eprintln!("Error: {err:?}"); response.message = String::from("Invalid"); (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) } } } else { response.message = String::from("Request body is not valid"); (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) } } /// Endpoint to create a Scheudled Message Event #[utoipa::path( post, path = crate::caller::endpoints::CREATE_SCHEDULED_MESSAGE_EVENT, request_body( content = super::request::CreateScheduleMessageEventRequest, description = "Data needed to create a Scheduled Message Event", content_type = "application/json" ), responses( (status = 201, description = "Scheduled Message Event created", body = super::response::CreateScheduleMessageEventResponse), (status = 400, description = "Error", body = super::response::CreateScheduleMessageEventResponse), (status = 500, description = "Error creating Scheduled Message Event", body = super::response::CreateScheduleMessageEventResponse) ) )] pub async fn create_scheduled_message_event( axum::Extension(pool): axum::Extension, axum::Json(payload): axum::Json, ) -> ( axum::http::StatusCode, axum::Json, ) { let mut response = super::response::CreateScheduleMessageEventResponse::default(); if payload.is_valid() { match scheduling_repo::get(&pool, &payload.scheduled_message_id).await { Ok(_) => {} Err(err) => { eprintln!("Error: {err:?}"); response.message = String::from("Scheduled Message does not exist"); return (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)); } } match contact_repo::get(&pool, &payload.contact_id).await { Ok(_) => {} Err(err) => { eprintln!("Error: {err:?}"); response.message = String::from("Contact does not exist"); return (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)); } } match message_repo::get(&pool, &payload.message_id).await { Ok(_) => {} Err(err) => { eprintln!("Error: {err:?}"); response.message = String::from("Contact does not exist"); return (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)); } } let mut scme = textsender_models::message::scheduling::ScheduledMessageEvent { contact_id: payload.contact_id, message_id: payload.message_id, scheduled_message_id: payload.scheduled_message_id, ..Default::default() }; match scheduling_repo::sched_msg_event::insert(&pool, &scme).await { Ok((id, created)) => { scme.id = id; scme.created = Some(created); response.message = String::from(super::super::super::response::SUCCESSFUL); response.data.push(scme); (axum::http::StatusCode::CREATED, axum::Json(response)) } Err(err) => { eprintln!("Error: {err:?}"); response.message = String::from("Error inserting"); ( axum::http::StatusCode::INTERNAL_SERVER_ERROR, axum::Json(response), ) } } } else { response.message = String::from("Request body is not valid"); (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) } } /// Endpoint to get Scheduled Message Event #[utoipa::path( get, path = crate::caller::endpoints::GET_SCHEDULED_MESSAGE_EVENT, params( ("id" = uuid::Uuid, Path, description = "Id of Scheduled Message Event"), ("scheduled_message_id" = uuid::Uuid, Path, description = "Scheduled Message Id associated with the Scheduled Message Event") ), responses( (status = 200, description = "Scheduled Message Event found", body = super::response::GetScheduledMessageEventResponse), (status = 400, description = "Invalid request", body = super::response::GetScheduledMessageEventResponse), (status = 500, description = "Error getting Scheduled Message Event", body = super::response::GetScheduledMessageEventResponse) ) )] pub async fn get_scheduled_message_events( axum::Extension(pool): axum::Extension, axum::extract::Query(params): axum::extract::Query< super::request::GetScheduledMessageEventParams, >, ) -> ( axum::http::StatusCode, axum::Json, ) { let mut response = super::response::GetScheduledMessageEventResponse::default(); let scheduled_message_events = match params.id { Some(id) => match scheduling_repo::sched_msg_event::get(&pool, &id).await { Ok(scheduled_message) => { vec![scheduled_message] } Err(err) => { eprintln!("Error: {err:?}"); return ( axum::http::StatusCode::INTERNAL_SERVER_ERROR, axum::Json(response), ); } }, None => match params.scheduled_message_id { Some(scheduled_message_id) => { match scheduling_repo::sched_msg_event::get_with_scheduled_message_id( &pool, &scheduled_message_id, ) .await { Ok(scheduled_messages) => scheduled_messages, Err(err) => { eprintln!("Error: {err:?}"); return ( axum::http::StatusCode::INTERNAL_SERVER_ERROR, axum::Json(response), ); } } } None => { response.message = String::from("Invalid parameter"); return (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)); } }, }; response.data = scheduled_message_events; response.message = String::from(super::super::super::response::SUCCESSFUL); (axum::http::StatusCode::OK, axum::Json(response)) } /// Endpoint to delete the Scheduled Message Event #[utoipa::path( delete, path = crate::caller::endpoints::DELETE_SCHEDULED_MESSAGE_EVENT, params(("id" = uuid::Uuid, Path, description = "Scheduled Message Event Id")), responses( (status = 200, description = "Deleted", body = super::response::DeleteScheduledMessageEventResponse), (status = 400, description = "Bad request", body = super::response::DeleteScheduledMessageEventResponse), (status = 500, description = "Error deleting", body = super::response::DeleteScheduledMessageEventResponse) ) )] pub async fn delete_scheduled_message_event( axum::Extension(pool): axum::Extension, axum::extract::Path(id): axum::extract::Path, ) -> ( axum::http::StatusCode, axum::Json, ) { let mut response = super::response::DeleteScheduledMessageEventResponse::default(); match scheduling_repo::sched_msg_event::get(&pool, &id).await { Ok(scheduled_message_event) => { match scheduling_repo::sched_msg_event::delete(&pool, &scheduled_message_event) .await { Ok(_) => { response.message = String::from(super::super::super::response::SUCCESSFUL); response.data.push(scheduled_message_event); (axum::http::StatusCode::OK, axum::Json(response)) } Err(err) => { eprintln!("Error: {err:?}"); response.message = String::from("Error deleting"); ( axum::http::StatusCode::INTERNAL_SERVER_ERROR, axum::Json(response), ) } } } Err(err) => { eprintln!("Error: {err:?}"); response.message = String::from("Bad request"); (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) } } } }