Get schedule message endpoint (#16)
textsender_api PR / Rustfmt (pull_request) Successful in 38s
textsender_api PR / Check (pull_request) Successful in 1m38s
textsender_api PR / Clippy (pull_request) Successful in 2m3s

Reviewed-on: phoenix/textsender_api#16
This commit was merged in pull request #16.
This commit is contained in:
2026-06-18 14:19:21 -04:00
parent d1bc5eb830
commit 825575930e
7 changed files with 222 additions and 2 deletions
Generated
+1 -1
View File
@@ -2293,7 +2293,7 @@ dependencies = [
[[package]]
name = "textsender_api"
version = "0.1.10"
version = "0.1.11"
dependencies = [
"axum",
"axum-extra",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "textsender_api"
version = "0.1.10"
version = "0.1.11"
edition = "2024"
rust-version = "1.96"
+73
View File
@@ -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<uuid::Uuid>,
pub user_id: Option<uuid::Uuid>,
}
}
pub mod response {
@@ -26,6 +32,12 @@ pub mod response {
pub message: String,
pub data: Vec<textsender_models::message::scheduling::ScheduledMessage>,
}
#[derive(Debug, Default, Deserialize, Serialize, ToSchema)]
pub struct GetScheduledMessageResponse {
pub message: String,
pub data: Vec<textsender_models::message::scheduling::ScheduledMessage>,
}
}
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<sqlx::PgPool>,
axum::extract::Query(params): axum::extract::Query<
super::request::GetScheduledMessageParams,
>,
) -> (
axum::http::StatusCode,
axum::Json<super::response::GetScheduledMessageResponse>,
) {
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<time::OffsetDateTime>) -> bool {
scheduled_time
.is_some_and(|t| t - time::OffsetDateTime::now_utc() >= time::Duration::minutes(10))
+2
View File
@@ -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 {
+6
View File
@@ -130,6 +130,12 @@ pub mod init {
axum::middleware::from_fn(crate::auth::auth::<axum::body::Body>),
),
)
.route(
crate::caller::endpoints::GET_SCHEDULE_MESSAGE,
get(scheduling_endpoints::get_scheduled_messages).route_layer(
axum::middleware::from_fn(crate::auth::auth::<axum::body::Body>),
),
)
.layer(cors::configure_cors().await)
}
+76
View File
@@ -25,3 +25,79 @@ pub async fn insert(
Err(_) => Err(sqlx::Error::RowNotFound),
}
}
pub async fn get(
pool: &sqlx::PgPool,
id: &uuid::Uuid,
) -> Result<textsender_models::message::scheduling::ScheduledMessage, sqlx::Error> {
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<Vec<textsender_models::message::scheduling::ScheduledMessage>, 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<textsender_models::message::scheduling::ScheduledMessage> =
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<textsender_models::message::scheduling::ScheduledMessage, sqlx::Error> {
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,
})
}
+63
View File
@@ -354,6 +354,25 @@ mod request {
Err(err) => Err(err),
}
}
pub async fn get_scheduled_messages(
app: &axum::Router,
id: &uuid::Uuid,
) -> Result<axum::response::Response, axum::http::Error> {
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<uuid::Uuid> = 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;
}