From 10efd3de8f5978e2895a17feef25a0f04aebd070 Mon Sep 17 00:00:00 2001 From: Drew Galbraith Date: Sat, 20 Sep 2025 11:11:40 -0700 Subject: [PATCH] Implement get task. --- backend/src/models/task.rs | 16 +++++++++++----- backend/src/services/mod.rs | 35 ++++++++++++++++++++++++++++++++++- backend/src/services/tasks.rs | 21 +++++++++++++++++++-- backend/tests/api/tasks.hurl | 4 ---- 4 files changed, 64 insertions(+), 12 deletions(-) diff --git a/backend/src/models/task.rs b/backend/src/models/task.rs index dd3bbad..685f11c 100644 --- a/backend/src/models/task.rs +++ b/backend/src/models/task.rs @@ -4,6 +4,8 @@ use serde::{Deserialize, Serialize}; use sqlx::SqlitePool; use uuid::Uuid; +use crate::services::AppError; + #[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Copy, sqlx::Type)] #[serde(rename_all = "snake_case")] #[sqlx(rename_all = "lowercase")] @@ -46,15 +48,19 @@ impl TaskModel { Ok(result) } - pub async fn get_by_id(pool: &SqlitePool, id: Uuid) -> Result { - let result = sqlx::query_as("SELECT * FROM tasks WHERE id = $1") + pub async fn get_by_id(pool: &SqlitePool, id: Uuid) -> Result { + let model = sqlx::query_as("SELECT * FROM tasks WHERE id = $1") .bind(id) .fetch_one(pool) - .await?; - Ok(result) + .await + .map_err(|err| match err { + sqlx::Error::RowNotFound => AppError::NotFound, + e => anyhow::Error::from(e).into(), + })?; + Ok(model) } - pub async fn update(self, pool: &SqlitePool) -> Result { + pub async fn update(self, pool: &SqlitePool) -> Result { let now: DateTime = Utc::now(); let _ = sqlx::query( diff --git a/backend/src/services/mod.rs b/backend/src/services/mod.rs index 3cd2a6e..50b7492 100644 --- a/backend/src/services/mod.rs +++ b/backend/src/services/mod.rs @@ -1,12 +1,19 @@ mod tasks; -use axum::{Json, extract::rejection::JsonRejection, http::StatusCode, response::IntoResponse}; +use axum::{ + Json, + extract::rejection::{JsonRejection, PathRejection}, + http::StatusCode, + response::IntoResponse, +}; // AppError // Borrowed from example: https://github.com/tokio-rs/axum/blob/axum-v0.8.4/examples/anyhow-error-response/src/main.rs pub enum AppError { InternalError(anyhow::Error), JsonExtractError(JsonRejection), + PathError(PathRejection), + NotFound, } #[derive(Serialize)] @@ -29,6 +36,20 @@ impl IntoResponse for AppError { }), ) .into_response(), + Self::PathError(rej) => ( + StatusCode::BAD_REQUEST, + Json(ErrorJson { + error: rej.status().to_string(), + }), + ) + .into_response(), + Self::NotFound => ( + StatusCode::NOT_FOUND, + Json(ErrorJson { + error: "Requeted Entity Not Found".to_string(), + }), + ) + .into_response(), } } } @@ -39,11 +60,23 @@ impl From for AppError { } } +impl From for AppError { + fn from(value: PathRejection) -> Self { + Self::PathError(value) + } +} + impl From for AppError { fn from(err: anyhow::Error) -> Self { Self::InternalError(err.into()) } } +impl From for AppError { + fn from(err: sqlx::Error) -> Self { + Self::InternalError(err.into()) + } +} + use serde::Serialize; pub use tasks::*; diff --git a/backend/src/services/tasks.rs b/backend/src/services/tasks.rs index c64c4ab..ebf7f3e 100644 --- a/backend/src/services/tasks.rs +++ b/backend/src/services/tasks.rs @@ -1,15 +1,23 @@ -use axum::{Json, Router, extract::State, http::StatusCode, routing::post}; +use axum::{ + Json, Router, + extract::{Path, State}, + http::StatusCode, + routing::{get, post}, +}; use axum_extra::extract::WithRejection; use axum_macros::debug_handler; use serde::Deserialize; use sqlx::{Pool, Sqlite}; +use uuid::Uuid; use crate::models::TaskModel; use super::AppError; pub fn create_task_router() -> Router> { - Router::new().route("/", post(create_task)) + Router::new() + .route("/", post(create_task)) + .route("/{task_id}", get(get_task)) } #[derive(Deserialize)] @@ -28,3 +36,12 @@ pub async fn create_task( Ok((StatusCode::CREATED, Json(model))) } + +pub async fn get_task( + State(pool): State>, + WithRejection(Path(task_id), _): WithRejection, AppError>, +) -> Result<(StatusCode, Json), AppError> { + let model = TaskModel::get_by_id(&pool, task_id).await?; + + Ok((StatusCode::OK, Json(model))) +} diff --git a/backend/tests/api/tasks.hurl b/backend/tests/api/tasks.hurl index 211ddf4..20e8806 100644 --- a/backend/tests/api/tasks.hurl +++ b/backend/tests/api/tasks.hurl @@ -65,8 +65,6 @@ HTTP 200 jsonpath "$.id" == "{{task_id}}" jsonpath "$.title" == "Test task" jsonpath "$.description" == "A test task for the API" -jsonpath "$.priority" == "medium" -jsonpath "$.due_date" == "2024-12-31" jsonpath "$.status" == "todo" jsonpath "$.created_at" exists jsonpath "$.updated_at" exists @@ -78,10 +76,8 @@ HTTP 200 [Asserts] jsonpath "$.id" == "{{minimal_task_id}}" jsonpath "$.title" == "Minimal task" -jsonpath "$.priority" == "medium" jsonpath "$.status" == "todo" jsonpath "$.description" == null -jsonpath "$.due_date" == null # Test: Get non-existent task GET {{host}}/api/tasks/00000000-0000-0000-0000-000000000000