diff --git a/Cargo.lock b/Cargo.lock index 3989e6f..b600799 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2293,7 +2293,7 @@ dependencies = [ [[package]] name = "textsender_api" -version = "0.1.10" +version = "0.1.11" dependencies = [ "axum", "axum-extra", diff --git a/Cargo.toml b/Cargo.toml index c5beac2..c11c93b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "textsender_api" -version = "0.1.10" +version = "0.1.11" edition = "2024" rust-version = "1.96" diff --git a/src/caller/message/scheduling.rs b/src/caller/message/scheduling.rs index de9f85e..29f310a 100644 --- a/src/caller/message/scheduling.rs +++ b/src/caller/message/scheduling.rs @@ -15,6 +15,12 @@ pub mod request { 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, + } } pub mod response { @@ -26,6 +32,12 @@ pub mod response { pub message: String, pub data: Vec, } + + #[derive(Debug, Default, Deserialize, Serialize, ToSchema)] + pub struct GetScheduledMessageResponse { + pub message: String, + pub data: Vec, + } } pub mod endpoint { @@ -97,6 +109,67 @@ pub mod endpoint { } } + /// 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)) diff --git a/src/caller/mod.rs b/src/caller/mod.rs index d436f86..11dff9c 100644 --- a/src/caller/mod.rs +++ b/src/caller/mod.rs @@ -16,6 +16,8 @@ pub mod endpoints { pub const GET_MESSAGE: &str = "/api/v1/message"; /// Constant for scheduling message endpoint pub const SCHEDULE_MESSAGE: &str = "/api/v1/schedule/message"; + /// Constant for getting scheduled message endpoint + pub const GET_SCHEDULE_MESSAGE: &str = "/api/v1/schedule/message"; } pub mod response { diff --git a/src/config/mod.rs b/src/config/mod.rs index 7199ff2..ce2e588 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -130,6 +130,12 @@ pub mod init { axum::middleware::from_fn(crate::auth::auth::), ), ) + .route( + crate::caller::endpoints::GET_SCHEDULE_MESSAGE, + get(scheduling_endpoints::get_scheduled_messages).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), + ) .layer(cors::configure_cors().await) } diff --git a/src/repo/scheduling.rs b/src/repo/scheduling.rs index b03e93c..97af52c 100644 --- a/src/repo/scheduling.rs +++ b/src/repo/scheduling.rs @@ -25,3 +25,79 @@ pub async fn insert( Err(_) => Err(sqlx::Error::RowNotFound), } } + +pub async fn get( + pool: &sqlx::PgPool, + id: &uuid::Uuid, +) -> Result { + match sqlx::query( + r#" + SELECT id, scheduled, created, status, user_id FROM "scheduled_messages" + WHERE + id = $1; + "#, + ) + .bind(id) + .fetch_one(pool) + .await + { + Ok(row) => match parse_row(&row).await { + Ok(scheduled_message) => Ok(scheduled_message), + Err(err) => Err(err), + }, + Err(_) => Err(sqlx::Error::RowNotFound), + } +} + +pub async fn get_with_user_id( + pool: &sqlx::PgPool, + user_id: &uuid::Uuid, +) -> Result, sqlx::Error> { + match sqlx::query( + r#" + SELECT id, scheduled, created, status, user_id FROM "scheduled_messages" + WHERE + user_id = $1; + "#, + ) + .bind(user_id) + .fetch_all(pool) + .await + { + Ok(rows) => { + let mut messages: Vec = + Vec::new(); + for row in rows { + match parse_row(&row).await { + Ok(scheduled_message) => { + messages.push(scheduled_message); + } + Err(err) => { + return Err(err); + } + } + } + + Ok(messages) + } + Err(_) => Err(sqlx::Error::RowNotFound), + } +} + +async fn parse_row( + row: &sqlx::postgres::PgRow, +) -> Result { + let id: uuid::Uuid = row.try_get("id")?; + let scheduled: time::OffsetDateTime = row.try_get("scheduled")?; + let status: String = row.try_get("status")?; + let created: time::OffsetDateTime = row.try_get("created")?; + let user_id: uuid::Uuid = row.try_get("user_id")?; + + Ok(textsender_models::message::scheduling::ScheduledMessage { + id, + scheduled: Some(scheduled), + created: Some(created), + status, + user_id, + }) +} diff --git a/tests/test.rs b/tests/test.rs index 90e8749..0660fee 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -354,6 +354,25 @@ mod request { Err(err) => Err(err), } } + + pub async fn get_scheduled_messages( + app: &axum::Router, + id: &uuid::Uuid, + ) -> Result { + let uri = format!( + "{}?id={}", + textsender_api::caller::endpoints::GET_SCHEDULE_MESSAGE, + id + ); + + match super::super::run_post(None, &uri, axum::http::Method::GET, false).await { + Ok(req) => match app.clone().oneshot(req).await { + Ok(response) => Ok(response), + Err(err) => Err(axum::http::Error::from(err)), + }, + Err(err) => Err(err), + } + } } } @@ -624,3 +643,47 @@ async fn test_schedule_message() { let _ = db_mgr::drop_database(&tm_pool, &db_name).await; } + +#[tokio::test] +async fn test_get_scheduled_message() { + let (tm_pool, db_name, pool) = db_mgr::get_database_ready().await; + let app = init::app(pool).await; + + let mut id: Option = None; + + match request::message::scheduling::create_scheduled_message(&app).await { + Ok(response) => { + eprintln!("Response: {response:?}"); + println!("Response: {response:?}"); + let resp = util::get_resp_data::< + textsender_api::caller::message::scheduling::response::ScheduleMessageResponse, + >(response) + .await; + assert_eq!(false, resp.data.is_empty(), "Should not be empty"); + let scheduled_message = &resp.data[0]; + id = Some(scheduled_message.id); + } + Err(err) => { + assert!(false, "Error: {err:?}"); + } + } + + if let Some(parsed_id) = id { + match request::message::scheduling::get_scheduled_messages(&app, &parsed_id).await { + Ok(response) => { + let resp = util::get_resp_data::< + textsender_api::caller::message::scheduling::response::GetScheduledMessageResponse, + >(response) + .await; + assert_eq!(false, resp.data.is_empty(), "Should not be empty"); + } + Err(err) => { + assert!(false, "Error: {err:?}"); + } + } + } else { + assert!(false, "Scheduled Message Id could not be retrieved"); + } + + let _ = db_mgr::drop_database(&tm_pool, &db_name).await; +}