Create the remaining task api methods on the server. (#3)
Create the remaining task api methods along with hurl tests for each. Reviewed-on: #3 Co-authored-by: Drew Galbraith <drew@tiramisu.one> Co-committed-by: Drew Galbraith <drew@tiramisu.one>
This commit is contained in:
parent
d32f6be813
commit
ef247e6e29
5 changed files with 457 additions and 3 deletions
|
|
@ -13,6 +13,7 @@ pub enum AppError {
|
|||
InternalError(anyhow::Error),
|
||||
JsonExtractError(JsonRejection),
|
||||
PathError(PathRejection),
|
||||
Unprocessable(String),
|
||||
NotFound,
|
||||
}
|
||||
|
||||
|
|
@ -36,6 +37,11 @@ impl IntoResponse for AppError {
|
|||
}),
|
||||
)
|
||||
.into_response(),
|
||||
Self::Unprocessable(msg) => (
|
||||
StatusCode::UNPROCESSABLE_ENTITY,
|
||||
Json(ErrorJson { error: msg }),
|
||||
)
|
||||
.into_response(),
|
||||
Self::PathError(rej) => (
|
||||
StatusCode::BAD_REQUEST,
|
||||
Json(ErrorJson {
|
||||
|
|
|
|||
|
|
@ -10,14 +10,17 @@ use serde::Deserialize;
|
|||
use sqlx::{Pool, Sqlite};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::models::TaskModel;
|
||||
use crate::models::{TaskModel, TaskStatus};
|
||||
|
||||
use super::AppError;
|
||||
|
||||
pub fn create_task_router() -> Router<Pool<Sqlite>> {
|
||||
Router::new()
|
||||
.route("/", post(create_task))
|
||||
.route("/{task_id}", get(get_task))
|
||||
.route("/", post(create_task).get(list_tasks))
|
||||
.route(
|
||||
"/{task_id}",
|
||||
get(get_task).put(update_task).delete(delete_task),
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
|
@ -37,6 +40,14 @@ pub async fn create_task(
|
|||
Ok((StatusCode::CREATED, Json(model)))
|
||||
}
|
||||
|
||||
pub async fn list_tasks(
|
||||
State(pool): State<Pool<Sqlite>>,
|
||||
) -> Result<(StatusCode, Json<Vec<TaskModel>>), AppError> {
|
||||
let tasks = TaskModel::list_all(&pool).await?;
|
||||
|
||||
Ok((StatusCode::OK, Json(tasks)))
|
||||
}
|
||||
|
||||
pub async fn get_task(
|
||||
State(pool): State<Pool<Sqlite>>,
|
||||
WithRejection(Path(task_id), _): WithRejection<Path<Uuid>, AppError>,
|
||||
|
|
@ -45,3 +56,51 @@ pub async fn get_task(
|
|||
|
||||
Ok((StatusCode::OK, Json(model)))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct UpdateTaskRequest {
|
||||
title: Option<String>,
|
||||
description: Option<String>,
|
||||
status: Option<TaskStatus>,
|
||||
}
|
||||
|
||||
pub async fn update_task(
|
||||
State(pool): State<Pool<Sqlite>>,
|
||||
WithRejection(Path(task_id), _): WithRejection<Path<Uuid>, AppError>,
|
||||
WithRejection(Json(input), _): WithRejection<Json<UpdateTaskRequest>, AppError>,
|
||||
) -> Result<(StatusCode, Json<TaskModel>), AppError> {
|
||||
let mut model = TaskModel::get_by_id(&pool, task_id).await?;
|
||||
|
||||
if let Some(title) = input.title {
|
||||
if title.len() == 0 {
|
||||
return Err(AppError::Unprocessable(
|
||||
"Title must not be empty".to_string(),
|
||||
));
|
||||
}
|
||||
model.title = title;
|
||||
}
|
||||
|
||||
if let Some(description) = input.description {
|
||||
model.description = Some(description);
|
||||
}
|
||||
|
||||
if let Some(status) = input.status {
|
||||
model.status = status;
|
||||
}
|
||||
|
||||
let model = model.update(&pool).await?;
|
||||
|
||||
Ok((StatusCode::OK, Json(model)))
|
||||
}
|
||||
|
||||
pub async fn delete_task(
|
||||
State(pool): State<Pool<Sqlite>>,
|
||||
WithRejection(Path(task_id), _): WithRejection<Path<Uuid>, AppError>,
|
||||
) -> Result<StatusCode, AppError> {
|
||||
// Ensure that the task exists.
|
||||
TaskModel::get_by_id(&pool, task_id).await?;
|
||||
TaskModel::delete(&pool, task_id).await?;
|
||||
|
||||
Ok(StatusCode::NO_CONTENT)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue