From 1c7595ea5d284d1ddebea8a0d28b121582fab56f Mon Sep 17 00:00:00 2001 From: phoenix Date: Sat, 20 Jun 2026 11:05:42 -0400 Subject: [PATCH 1/6] Adding constant for endpoint --- src/caller/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/caller/mod.rs b/src/caller/mod.rs index b8ef7dd..db43e41 100644 --- a/src/caller/mod.rs +++ b/src/caller/mod.rs @@ -26,6 +26,8 @@ pub mod endpoints { pub const GET_SCHEDULED_MESSAGE_EVENT: &str = "/api/v1/schedule/message/event"; /// Constant for deleting Scheduled Message Event endpoint pub const DELETE_SCHEDULED_MESSAGE_EVENT: &str = "/api/v1/schedule/message/event/{id}"; + /// Constant for fetching Scheduled Message endpoint + pub const FETCH_SCHEDULED_MESSAGE: &str = "/api/v1/schedule/message/fetch"; } pub mod response { -- 2.47.3 From fe2d0ddffd996248fbf49a7719af572678f18ed5 Mon Sep 17 00:00:00 2001 From: phoenix Date: Sat, 20 Jun 2026 11:21:20 -0400 Subject: [PATCH 2/6] Making endpoint available --- src/caller/message/scheduling.rs | 46 ++++++++++++++++++++++++++++++++ src/config/mod.rs | 6 +++++ src/repo/message/scheduling.rs | 34 +++++++++++++++++++++++ 3 files changed, 86 insertions(+) diff --git a/src/caller/message/scheduling.rs b/src/caller/message/scheduling.rs index af1eef7..249dd73 100644 --- a/src/caller/message/scheduling.rs +++ b/src/caller/message/scheduling.rs @@ -104,6 +104,8 @@ pub mod response { pub struct ChangedStatus { pub old_status: String, } + + pub use ScheduleMessageResponse as FetchScheduledMessageResponse; } pub mod endpoint { @@ -243,6 +245,50 @@ pub mod endpoint { .is_some_and(|t| t - time::OffsetDateTime::now_utc() >= time::Duration::minutes(10)) } + /// Endpoint to fetch Scheduled Message that is ready + #[utoipa::path( + get, + path = crate::caller::endpoints::FETCH_SCHEDULED_MESSAGE, + responses( + (status = 200, description = "Scheduled Message found", body = super::response::FetchScheduledMessageResponse), + (status = 400, description = "Error getting Scheduled Message", body = super::response::FetchScheduledMessageResponse) + ) + )] + pub async fn fetch_scheduled_message( + axum::Extension(pool): axum::Extension, + ) -> ( + axum::http::StatusCode, + axum::Json, + ) { + let mut response = super::response::FetchScheduledMessageResponse::default(); + + match scheduling_repo::fetch(&pool).await { + Ok(scheduled_message) => { + response.data.push(scheduled_message); + response.message = String::from(super::super::super::response::SUCCESSFUL); + + (axum::http::StatusCode::OK, axum::Json(response)) + } + Err(err) => { + match err { + sqlx::Error::RowNotFound => { + response.message = String::from("Nothing to fetch"); + + (axum::http::StatusCode::NO_CONTENT, axum::Json(response)) + } + _ => { + eprintln!("Error: {err:?}"); + response.message = String::from("Error"); + ( + axum::http::StatusCode::INTERNAL_SERVER_ERROR, + axum::Json(response), + ) + } + } + } + } + } + /// Endpoint to update status of Scheduled Message #[utoipa::path( patch, diff --git a/src/config/mod.rs b/src/config/mod.rs index d09b4e0..17d8485 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -160,6 +160,12 @@ pub mod init { axum::middleware::from_fn(crate::auth::auth::), ), ) + .route( + crate::caller::endpoints::FETCH_SCHEDULED_MESSAGE, + get(scheduling_endpoints::fetch_scheduled_message).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), + ) .layer(cors::configure_cors().await) } diff --git a/src/repo/message/scheduling.rs b/src/repo/message/scheduling.rs index 36141a8..4a21621 100644 --- a/src/repo/message/scheduling.rs +++ b/src/repo/message/scheduling.rs @@ -84,6 +84,40 @@ pub async fn get_with_user_id( } } +pub async fn fetch( + pool: &sqlx::PgPool, +) -> Result { + match sqlx::query( + r#" + UPDATE "scheduled_messages" + SET status = $1 + WHERE id = ( + SELECT id FROM "scheduled_messages" + WHERE status = $2 + ORDER BY id + FOR UPDATE SKIP LOCKED + LIMIT 1 + ) + RETURNING id, scheduled, created, status, user_id + "#, + ) + .bind(textsender_models::message::scheduling::PROCESSING) + .bind(textsender_models::message::scheduling::READY) + .fetch_one(pool) + .await + { + Ok(row) => { + match parse_row(&row).await { + Ok(scheduled_message) => { + Ok(scheduled_message) + } + Err(err) => Err(err) + } + } + Err(err) => Err(err), + } +} + pub async fn update_status( pool: &sqlx::PgPool, id: &uuid::Uuid, -- 2.47.3 From ce48ea1eed3678107d18a0943bf4bcfd7dcd29a4 Mon Sep 17 00:00:00 2001 From: phoenix Date: Sat, 20 Jun 2026 11:26:49 -0400 Subject: [PATCH 3/6] cargo fmt --- src/caller/message/scheduling.rs | 16 +++++++--------- src/repo/message/scheduling.rs | 12 ++++-------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/caller/message/scheduling.rs b/src/caller/message/scheduling.rs index 249dd73..ca136b6 100644 --- a/src/caller/message/scheduling.rs +++ b/src/caller/message/scheduling.rs @@ -269,23 +269,21 @@ pub mod endpoint { (axum::http::StatusCode::OK, axum::Json(response)) } - Err(err) => { - match err { - sqlx::Error::RowNotFound => { - response.message = String::from("Nothing to fetch"); + Err(err) => match err { + sqlx::Error::RowNotFound => { + response.message = String::from("Nothing to fetch"); - (axum::http::StatusCode::NO_CONTENT, axum::Json(response)) - } - _ => { + (axum::http::StatusCode::NO_CONTENT, axum::Json(response)) + } + _ => { eprintln!("Error: {err:?}"); response.message = String::from("Error"); ( axum::http::StatusCode::INTERNAL_SERVER_ERROR, axum::Json(response), ) - } } - } + }, } } diff --git a/src/repo/message/scheduling.rs b/src/repo/message/scheduling.rs index 4a21621..1604350 100644 --- a/src/repo/message/scheduling.rs +++ b/src/repo/message/scheduling.rs @@ -106,14 +106,10 @@ pub async fn fetch( .fetch_one(pool) .await { - Ok(row) => { - match parse_row(&row).await { - Ok(scheduled_message) => { - Ok(scheduled_message) - } - Err(err) => Err(err) - } - } + Ok(row) => match parse_row(&row).await { + Ok(scheduled_message) => Ok(scheduled_message), + Err(err) => Err(err), + }, Err(err) => Err(err), } } -- 2.47.3 From 05df9e7887cfff489e2947876da66965998e29f2 Mon Sep 17 00:00:00 2001 From: phoenix Date: Sat, 20 Jun 2026 11:37:18 -0400 Subject: [PATCH 4/6] Warning fix --- src/caller/message/scheduling.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/caller/message/scheduling.rs b/src/caller/message/scheduling.rs index ca136b6..d146889 100644 --- a/src/caller/message/scheduling.rs +++ b/src/caller/message/scheduling.rs @@ -52,15 +52,10 @@ pub mod request { 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::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 } -- 2.47.3 From d9b6164587bdd330ed0fa32c8a884ed6710a7b7b Mon Sep 17 00:00:00 2001 From: phoenix Date: Sat, 20 Jun 2026 11:37:25 -0400 Subject: [PATCH 5/6] Adding test --- tests/test.rs | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/tests/test.rs b/tests/test.rs index 1519c09..53e2aa0 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -377,6 +377,25 @@ mod request { } } + pub async fn fetch_scheduled_message( + app: &axum::Router, + ) -> Result { + match super::super::run_post( + None, + textsender_api::caller::endpoints::FETCH_SCHEDULED_MESSAGE, + 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), + } + } + pub async fn update_scheduled_message_status( app: &axum::Router, id: &uuid::Uuid, @@ -1143,3 +1162,110 @@ async fn test_update_scheduled_message_status() { let _ = db_mgr::drop_database(&tm_pool, &db_name).await; } + +#[tokio::test] +async fn test_fetch_scheduled_message() { + let (tm_pool, db_name, pool) = db_mgr::get_database_ready().await; + let app = init::app(pool).await; + + let mut contact_id: uuid::Uuid = uuid::Uuid::nil(); + let mut message_id: uuid::Uuid = uuid::Uuid::nil(); + let mut scheduled_message_id: uuid::Uuid = uuid::Uuid::nil(); + + match request::create_contact(&app).await { + Ok(response) => { + let resp = util::get_resp_data::< + textsender_api::caller::contact::response::AddContactResponse, + >(response) + .await; + assert_eq!(false, resp.data.is_empty(), "Should not be empty"); + contact_id = resp.data[0].id.unwrap(); + } + Err(err) => { + assert!(false, "Error: {:?}", err); + } + }; + + match request::message::create_message(&app).await { + Ok(response) => { + let resp = util::get_resp_data::< + textsender_api::caller::message::response::AddMessageResponse, + >(response) + .await; + assert_eq!(false, resp.data.is_empty(), "Should not be empty"); + let message = &resp.data[0]; + message_id = message.id.unwrap(); + } + Err(err) => { + assert!(false, "Error: {:?}", err); + } + }; + + match request::message::scheduling::create_scheduled_message(&app).await { + Ok(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"); + scheduled_message_id = resp.data[0].id; + } + Err(err) => { + assert!(false, "Error: {err:?}"); + } + } + + match request::message::scheduling::create_scheduled_message_event( + &app, + &contact_id, + &message_id, + &scheduled_message_id, + ) + .await + { + Ok(response) => { + let resp = util::get_resp_data::< + textsender_api::caller::message::scheduling::response::CreateScheduleMessageEventResponse, + >(response) + .await; + assert_eq!(false, resp.data.is_empty(), "Should not be empty"); + } + Err(err) => { + assert!(false, "Error: {err:?}"); + } + } + + match request::message::scheduling::update_scheduled_message_status( + &app, + &scheduled_message_id, + textsender_models::message::scheduling::READY, + ) + .await + { + Ok(response) => { + let resp = util::get_resp_data::< + textsender_api::caller::message::scheduling::response::UpdateScheduledMessageStatusResponse, + >(response) + .await; + assert_eq!(false, resp.data.is_empty(), "Should not be empty"); + } + Err(err) => { + assert!(false, "Error: {err:?}"); + } + } + + match request::message::scheduling::fetch_scheduled_message(&app).await { + Ok(response) => { + let resp = util::get_resp_data::< + textsender_api::caller::message::scheduling::response::FetchScheduledMessageResponse, + >(response) + .await; + assert_eq!(false, resp.data.is_empty(), "Should not be empty"); + } + Err(err) => { + assert!(false, "Error: {err:?}"); + } + } + + let _ = db_mgr::drop_database(&tm_pool, &db_name).await; +} -- 2.47.3 From 358f4932e805dbc0cf35a3188847e08d4c73ee2c Mon Sep 17 00:00:00 2001 From: phoenix Date: Sat, 20 Jun 2026 11:39:55 -0400 Subject: [PATCH 6/6] bump: textsender_api --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eae15e8..2eb269f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2277,7 +2277,7 @@ dependencies = [ [[package]] name = "textsender_api" -version = "0.1.14" +version = "0.1.15" dependencies = [ "axum", "axum-extra", diff --git a/Cargo.toml b/Cargo.toml index 80587bf..1de4543 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "textsender_api" -version = "0.1.14" +version = "0.1.15" edition = "2024" rust-version = "1.96" -- 2.47.3