Create Project APIs.
Some checks failed
Check / Backend (pull_request) Failing after 2m29s
Check / Frontend (pull_request) Successful in 1m55s

This commit is contained in:
Drew 2025-10-22 22:20:14 -07:00
parent d8094c4812
commit d4b8302306
9 changed files with 250 additions and 6 deletions

View file

@ -0,0 +1,106 @@
use axum::{
Json, Router,
extract::{Path, State},
http::StatusCode,
routing::{get, post},
};
use axum_extra::extract::WithRejection;
use serde::Deserialize;
use sqlx::{Pool, Sqlite};
use uuid::Uuid;
use crate::models::{ProjectModel, ProjectStatus};
use super::AppError;
pub fn create_project_router() -> Router<Pool<Sqlite>> {
Router::new()
.route("/", post(create_project).get(list_projects))
.route(
"/{project_id}",
get(get_project).put(update_project).delete(delete_project),
)
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CreateProjectRequest {
title: String,
color: String,
}
pub async fn create_project(
State(pool): State<Pool<Sqlite>>,
WithRejection(Json(input), _): WithRejection<Json<CreateProjectRequest>, AppError>,
) -> Result<(StatusCode, Json<ProjectModel>), AppError> {
Ok((
StatusCode::CREATED,
Json(ProjectModel::insert(&pool, input.title, input.color).await?),
))
}
pub async fn list_projects(
State(pool): State<Pool<Sqlite>>,
) -> Result<(StatusCode, Json<Vec<ProjectModel>>), AppError> {
Ok((StatusCode::OK, Json(ProjectModel::list(&pool).await?)))
}
pub async fn get_project(
State(pool): State<Pool<Sqlite>>,
WithRejection(Path(project_id), _): WithRejection<Path<Uuid>, AppError>,
) -> Result<(StatusCode, Json<ProjectModel>), AppError> {
Ok((
StatusCode::OK,
Json(ProjectModel::get_by_id(&pool, project_id).await?),
))
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
pub struct UpdateProjectRequest {
title: Option<String>,
color: Option<String>,
status: Option<ProjectStatus>,
}
pub async fn update_project(
State(pool): State<Pool<Sqlite>>,
WithRejection(Path(project_id), _): WithRejection<Path<Uuid>, AppError>,
WithRejection(Json(input), _): WithRejection<Json<UpdateProjectRequest>, AppError>,
) -> Result<(StatusCode, Json<ProjectModel>), AppError> {
let mut project = ProjectModel::get_by_id(&pool, project_id).await?;
if let Some(new_title) = input.title {
if new_title.len() == 0 {
return Err(AppError::Unprocessable(
"Title must not be empty".to_string(),
));
}
project.title = new_title;
}
if let Some(color) = input.color {
if color.len() != 7 || color.chars().next() != Some('#') {
return Err(AppError::Unprocessable(
"Color is not a valid hex".to_string(),
));
}
project.color = color;
}
if let Some(status) = input.status {
project.status = status;
}
Ok((StatusCode::OK, Json(project.update(&pool).await?)))
}
pub async fn delete_project(
State(pool): State<Pool<Sqlite>>,
WithRejection(Path(project_id), _): WithRejection<Path<Uuid>, AppError>,
) -> Result<StatusCode, AppError> {
ProjectModel::get_by_id(&pool, project_id).await?;
ProjectModel::delete(&pool, project_id).await?;
Ok(StatusCode::NO_CONTENT)
}