diff --git a/Cargo.lock b/Cargo.lock index 955144f..141b3b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -132,7 +132,6 @@ checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" dependencies = [ "async-trait", "axum-core", - "axum-macros", "bitflags 1.3.2", "bytes", "futures-util", @@ -175,18 +174,6 @@ dependencies = [ "tower-service", ] -[[package]] -name = "axum-macros" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdca6a10ecad987bda04e95606ef85a5417dcaac1a78455242d72e031e2b6b62" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.35", -] - [[package]] name = "axum-prometheus" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 8d8a171..f71f18d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,8 @@ default = ["jaeger", "prometheus", "tokio-console"] opt-level = 0 lto = "off" +[dependencies] + [dependencies.async-trait] version = "0.1" @@ -49,7 +51,7 @@ features = ["derive"] [dependencies.axum] version = "0.6" -features = ["headers", "macros"] +features = ["headers"] [dependencies.tokio] version = "1" diff --git a/src/components/mod.rs b/src/components/mod.rs index 5b37cfa..d6447b2 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -115,7 +115,7 @@ pub mod route { use crate::AppState; use axum::{ body::BoxBody, - extract::{Path, State}, + extract::{Path, Query, State}, http::HeaderMap, response::Response, Extension, Form, @@ -128,7 +128,9 @@ pub mod route { #[async_trait] pub trait Create: super::crud::Create { - type FormX: Send + Sync + 'static; + type Form: Send + Sync + 'static; + + type ParentUrlParams: Send + Sync + 'static; type UrlParams: Send + Sync + 'static; const URL: &'static str; @@ -137,8 +139,51 @@ pub mod route { user: Extension, state: State, headers: HeaderMap, - path: Path, - form: Form, + path: Path<(Self::ParentUrlParams, Self::UrlParams)>, + form: Form, ) -> Result, crate::Error>; + + fn with_prefix(prefix: &'static str) -> String { + format!("{}{}", prefix, Self::URL) + } + } + + #[async_trait] + pub trait Read: super::crud::Read { + type UrlParams: Send + Sync + 'static; + type QueryParams: Send + Sync + 'static; + + const URL: &'static str; + + async fn read( + user: Extension, + state: State, + headers: HeaderMap, + query: Query, + path: Path, + ) -> Result, crate::Error>; + + fn with_prefix(prefix: &'static str) -> String { + format!("{}{}", prefix, Self::URL) + } + } + + #[async_trait] + pub trait Delete: super::crud::Delete { + type ParentUrlParams: Send + Sync + 'static; + type UrlParams: Send + Sync + 'static; + + const URL: &'static str; + + async fn delete( + user: Extension, + state: State, + headers: HeaderMap, + path: Path<(Self::ParentUrlParams, Self::UrlParams)>, + ) -> Result, crate::Error>; + + fn with_prefix(prefix: &'static str) -> String { + format!("{}{}", prefix, Self::URL) + } } } diff --git a/src/models/trips/todos.rs b/src/models/trips/todos.rs index 3cfcc5a..44bfa14 100644 --- a/src/models/trips/todos.rs +++ b/src/models/trips/todos.rs @@ -567,18 +567,19 @@ pub struct TripTodoNew { #[async_trait] impl route::Create for Todo { - type FormX = TripTodoNew; - type UrlParams = (Uuid,); + type Form = TripTodoNew; + type ParentUrlParams = (Uuid,); + type UrlParams = (); - const URL: &'static str = "/:id/todo/new"; + const URL: &'static str = "/new"; #[tracing::instrument] async fn create( Extension(current_user): Extension, axum::extract::State(state): axum::extract::State, headers: HeaderMap, - Path((trip_id,)): Path<(Uuid,)>, - Form(form): Form, + Path(((trip_id,), ())): Path<(Self::ParentUrlParams, Self::UrlParams)>, + Form(form): Form, ) -> Result, crate::Error> { let ctx = Context::build(current_user); // method output is not required as we reload the whole trip todos anyway @@ -614,6 +615,53 @@ impl route::Create for Todo { } } +#[async_trait] +impl route::Delete for Todo { + type ParentUrlParams = (Uuid,); + type UrlParams = (Uuid,); + + const URL: &'static str = "/:id/delete"; + + #[tracing::instrument] + async fn delete( + Extension(current_user): Extension, + axum::extract::State(state): axum::extract::State, + _headers: HeaderMap, + Path(((trip_id,), (todo_id,))): Path<(Self::ParentUrlParams, Self::UrlParams)>, + ) -> Result, crate::Error> { + let ctx = Context::build(current_user); + let deleted = ::delete( + &ctx, + &state.database_pool, + &TodoFilter { trip_id }, + todo_id, + ) + .await?; + + if !deleted { + return Err(crate::Error::Request(RequestError::NotFound { + message: format!("todo with id {todo_id} not found"), + })); + } + + let trip = crate::models::trips::Trip::find(&ctx, &state.database_pool, trip_id).await?; + match trip { + None => Err(crate::Error::Request(RequestError::NotFound { + message: format!("trip with id {trip_id} not found"), + })), + Some(mut trip) => { + trip.load_todos(&ctx, &state.database_pool).await?; + Ok(TodoList { + trip: &trip, + todos: &trip.todos(), + } + .build(None) + .into_response()) + } + } + } +} + pub struct NewTodo; impl NewTodo { diff --git a/src/routing/mod.rs b/src/routing/mod.rs index e4392f5..c9b0517 100644 --- a/src/routing/mod.rs +++ b/src/routing/mod.rs @@ -152,10 +152,17 @@ pub fn router(state: AppState) -> Router { .route("/:id/todo/:id/edit/save", post(trip_todo_edit_save)) .route("/:id/todo/:id/edit/cancel", post(trip_todo_edit_cancel)) .route( - "/:id/todo/new", + &::with_prefix( + "/:id/todo", + ), post(::create), ) - .route("/:id/todo/:id/delete", post(trip_todo_delete)), + .route( + &::with_prefix( + "/:id/todo", + ), + post(::delete), + ), ) .nest( (&TopLevelPage::Inventory.path()).into(), diff --git a/src/routing/routes.rs b/src/routing/routes.rs index 8fc7c41..216bb43 100644 --- a/src/routing/routes.rs +++ b/src/routing/routes.rs @@ -1479,42 +1479,3 @@ pub async fn trip_todo_edit_cancel( .into_response()), } } - -#[tracing::instrument] -pub async fn trip_todo_delete( - Extension(current_user): Extension, - State(state): State, - headers: HeaderMap, - Path((trip_id, todo_id)): Path<(Uuid, Uuid)>, -) -> Result { - let ctx = Context::build(current_user); - let deleted = models::trips::todos::Todo::delete( - &ctx, - &state.database_pool, - &TodoFilter { trip_id }, - todo_id, - ) - .await?; - - if !deleted { - return Err(Error::Request(RequestError::NotFound { - message: format!("todo with id {todo_id} not found"), - })); - } - - let trip = models::trips::Trip::find(&ctx, &state.database_pool, trip_id).await?; - match trip { - None => Err(Error::Request(RequestError::NotFound { - message: format!("trip with id {trip_id} not found"), - })), - Some(mut trip) => { - trip.load_todos(&ctx, &state.database_pool).await?; - Ok(models::trips::todos::TodoList { - trip: &trip, - todos: &trip.todos(), - } - .build(None) - .into_response()) - } - } -}