From bcb20c3db8e34b502541e8960f97c759075830ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20K=C3=B6rber?= Date: Fri, 15 Sep 2023 13:13:56 +0200 Subject: [PATCH] refactor crud --- ...45f9326e3fa966b3f8e864b3e01d4fe7b25b8.json | 32 -- ...b158668704d4497c2f7fef327d56559c5ee8a.json | 12 + ...13fb3beebb547e658ea80302490953205a4c5.json | 32 ++ ...f92d4eae3aa4281eafc2ac29fdb83525e0536.json | 12 - ...8d8fa3ba73c423cafcaa555c6f0a588b622a3.json | 12 - ...5e95f04eedf2875afa5b2d687b893fee5fbbb.json | 32 ++ ...b3b87f29e85ccdf67dc40f9ce372d5db9727d.json | 12 - ...67f61ab03356497e777e55493c4f188e14a25.json | 12 + Cargo.lock | 1 + Cargo.toml | 3 + src/components/mod.rs | 89 +++++ src/lib.rs | 1 + src/models/trips/mod.rs | 5 +- src/models/trips/todos.rs | 303 +++++++++++------- src/routing/routes.rs | 145 ++++++--- src/telemetry/tracing/mod.rs | 2 +- 16 files changed, 466 insertions(+), 239 deletions(-) delete mode 100644 .sqlx/query-33cba5d1fbcfb492f8f8443782c45f9326e3fa966b3f8e864b3e01d4fe7b25b8.json create mode 100644 .sqlx/query-4b087f9afbc94acbdf09a04c934b158668704d4497c2f7fef327d56559c5ee8a.json create mode 100644 .sqlx/query-4f9e7f676f42a548c8993a0182313fb3beebb547e658ea80302490953205a4c5.json delete mode 100644 .sqlx/query-5ac0e60ed79f626300f0dfde880f92d4eae3aa4281eafc2ac29fdb83525e0536.json delete mode 100644 .sqlx/query-9784595191d25448b2a24c856288d8fa3ba73c423cafcaa555c6f0a588b622a3.json create mode 100644 .sqlx/query-d20992191e5fd34a3b03a117abf5e95f04eedf2875afa5b2d687b893fee5fbbb.json delete mode 100644 .sqlx/query-d29d72b5c9dbf34d672aa271823b3b87f29e85ccdf67dc40f9ce372d5db9727d.json create mode 100644 .sqlx/query-d47bf74e8aaecdeb3730edab4d267f61ab03356497e777e55493c4f188e14a25.json create mode 100644 src/components/mod.rs diff --git a/.sqlx/query-33cba5d1fbcfb492f8f8443782c45f9326e3fa966b3f8e864b3e01d4fe7b25b8.json b/.sqlx/query-33cba5d1fbcfb492f8f8443782c45f9326e3fa966b3f8e864b3e01d4fe7b25b8.json deleted file mode 100644 index 58d9808..0000000 --- a/.sqlx/query-33cba5d1fbcfb492f8f8443782c45f9326e3fa966b3f8e864b3e01d4fe7b25b8.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "db_name": "SQLite", - "query": "UPDATE trip_todos\n SET description = ?\n WHERE \n id = ? \n AND trip_id = ?\n AND EXISTS(SELECT 1 FROM trips WHERE trip_id = ? AND user_id = ?)\n RETURNING\n id,\n description,\n done\n ", - "describe": { - "columns": [ - { - "name": "id", - "ordinal": 0, - "type_info": "Text" - }, - { - "name": "description", - "ordinal": 1, - "type_info": "Text" - }, - { - "name": "done", - "ordinal": 2, - "type_info": "Bool" - } - ], - "parameters": { - "Right": 5 - }, - "nullable": [ - false, - false, - false - ] - }, - "hash": "33cba5d1fbcfb492f8f8443782c45f9326e3fa966b3f8e864b3e01d4fe7b25b8" -} diff --git a/.sqlx/query-4b087f9afbc94acbdf09a04c934b158668704d4497c2f7fef327d56559c5ee8a.json b/.sqlx/query-4b087f9afbc94acbdf09a04c934b158668704d4497c2f7fef327d56559c5ee8a.json new file mode 100644 index 0000000..d56fb6c --- /dev/null +++ b/.sqlx/query-4b087f9afbc94acbdf09a04c934b158668704d4497c2f7fef327d56559c5ee8a.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "\n INSERT INTO trip_todos\n (id, description, done, trip_id)\n SELECT ?, ?, false, id as trip_id\n FROM trips\n WHERE trip_id = ? AND EXISTS(SELECT 1 FROM trips WHERE id = ? and user_id = ?)\n LIMIT 1\n ", + "describe": { + "columns": [], + "parameters": { + "Right": 5 + }, + "nullable": [] + }, + "hash": "4b087f9afbc94acbdf09a04c934b158668704d4497c2f7fef327d56559c5ee8a" +} diff --git a/.sqlx/query-4f9e7f676f42a548c8993a0182313fb3beebb547e658ea80302490953205a4c5.json b/.sqlx/query-4f9e7f676f42a548c8993a0182313fb3beebb547e658ea80302490953205a4c5.json new file mode 100644 index 0000000..209ffa8 --- /dev/null +++ b/.sqlx/query-4f9e7f676f42a548c8993a0182313fb3beebb547e658ea80302490953205a4c5.json @@ -0,0 +1,32 @@ +{ + "db_name": "SQLite", + "query": "\n UPDATE trip_todos\n SET description = ?\n WHERE \n id = ? \n AND trip_id = ?\n AND EXISTS(SELECT 1 FROM trips WHERE trip_id = ? AND user_id = ?)\n RETURNING\n id,\n description,\n done\n ", + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Text" + }, + { + "name": "description", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "done", + "ordinal": 2, + "type_info": "Bool" + } + ], + "parameters": { + "Right": 5 + }, + "nullable": [ + false, + false, + false + ] + }, + "hash": "4f9e7f676f42a548c8993a0182313fb3beebb547e658ea80302490953205a4c5" +} diff --git a/.sqlx/query-5ac0e60ed79f626300f0dfde880f92d4eae3aa4281eafc2ac29fdb83525e0536.json b/.sqlx/query-5ac0e60ed79f626300f0dfde880f92d4eae3aa4281eafc2ac29fdb83525e0536.json deleted file mode 100644 index c6e4e7e..0000000 --- a/.sqlx/query-5ac0e60ed79f626300f0dfde880f92d4eae3aa4281eafc2ac29fdb83525e0536.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "db_name": "SQLite", - "query": "DELETE FROM trip_todos\n WHERE \n id = ?\n AND EXISTS (SELECT 1 FROM trips WHERE trip_id = ? AND user_id = ?)", - "describe": { - "columns": [], - "parameters": { - "Right": 3 - }, - "nullable": [] - }, - "hash": "5ac0e60ed79f626300f0dfde880f92d4eae3aa4281eafc2ac29fdb83525e0536" -} diff --git a/.sqlx/query-9784595191d25448b2a24c856288d8fa3ba73c423cafcaa555c6f0a588b622a3.json b/.sqlx/query-9784595191d25448b2a24c856288d8fa3ba73c423cafcaa555c6f0a588b622a3.json deleted file mode 100644 index 6c99c3e..0000000 --- a/.sqlx/query-9784595191d25448b2a24c856288d8fa3ba73c423cafcaa555c6f0a588b622a3.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "db_name": "SQLite", - "query": "INSERT INTO trip_todos\n (id, description, done, trip_id)\n SELECT ?, ?, false, id as trip_id\n FROM trips\n WHERE trip_id = ? AND EXISTS(SELECT 1 FROM trips WHERE id = ? and user_id = ?)\n LIMIT 1", - "describe": { - "columns": [], - "parameters": { - "Right": 5 - }, - "nullable": [] - }, - "hash": "9784595191d25448b2a24c856288d8fa3ba73c423cafcaa555c6f0a588b622a3" -} diff --git a/.sqlx/query-d20992191e5fd34a3b03a117abf5e95f04eedf2875afa5b2d687b893fee5fbbb.json b/.sqlx/query-d20992191e5fd34a3b03a117abf5e95f04eedf2875afa5b2d687b893fee5fbbb.json new file mode 100644 index 0000000..13f05ef --- /dev/null +++ b/.sqlx/query-d20992191e5fd34a3b03a117abf5e95f04eedf2875afa5b2d687b893fee5fbbb.json @@ -0,0 +1,32 @@ +{ + "db_name": "SQLite", + "query": "\n UPDATE trip_todos\n SET done = ?\n WHERE trip_id = ?\n AND id = ?\n AND EXISTS(SELECT 1 FROM trips WHERE id = ? AND user_id = ?)\n RETURNING\n id,\n description,\n done\n ", + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Text" + }, + { + "name": "description", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "done", + "ordinal": 2, + "type_info": "Bool" + } + ], + "parameters": { + "Right": 5 + }, + "nullable": [ + false, + false, + false + ] + }, + "hash": "d20992191e5fd34a3b03a117abf5e95f04eedf2875afa5b2d687b893fee5fbbb" +} diff --git a/.sqlx/query-d29d72b5c9dbf34d672aa271823b3b87f29e85ccdf67dc40f9ce372d5db9727d.json b/.sqlx/query-d29d72b5c9dbf34d672aa271823b3b87f29e85ccdf67dc40f9ce372d5db9727d.json deleted file mode 100644 index c187050..0000000 --- a/.sqlx/query-d29d72b5c9dbf34d672aa271823b3b87f29e85ccdf67dc40f9ce372d5db9727d.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "db_name": "SQLite", - "query": "\n UPDATE trip_todos\n SET done = ?\n WHERE trip_id = ?\n AND id = ?\n AND EXISTS(SELECT 1 FROM trips WHERE id = ? AND user_id = ?)", - "describe": { - "columns": [], - "parameters": { - "Right": 5 - }, - "nullable": [] - }, - "hash": "d29d72b5c9dbf34d672aa271823b3b87f29e85ccdf67dc40f9ce372d5db9727d" -} diff --git a/.sqlx/query-d47bf74e8aaecdeb3730edab4d267f61ab03356497e777e55493c4f188e14a25.json b/.sqlx/query-d47bf74e8aaecdeb3730edab4d267f61ab03356497e777e55493c4f188e14a25.json new file mode 100644 index 0000000..9eb5b95 --- /dev/null +++ b/.sqlx/query-d47bf74e8aaecdeb3730edab4d267f61ab03356497e777e55493c4f188e14a25.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "\n DELETE FROM trip_todos\n WHERE \n id = ?\n AND EXISTS (SELECT 1 FROM trips WHERE trip_id = ? AND user_id = ?)\n ", + "describe": { + "columns": [], + "parameters": { + "Right": 3 + }, + "nullable": [] + }, + "hash": "d47bf74e8aaecdeb3730edab4d267f61ab03356497e777e55493c4f188e14a25" +} diff --git a/Cargo.lock b/Cargo.lock index d59ef36..3481d32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1428,6 +1428,7 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" name = "packager" version = "0.1.0" dependencies = [ + "async-trait", "axum", "axum-prometheus", "base64 0.21.4", diff --git a/Cargo.toml b/Cargo.toml index 6bdf174..7e6290e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,9 @@ default = ["jaeger", "prometheus", "tokio-console"] opt-level = 0 lto = "off" +[dependencies.async-trait] +version = "0.1" + [dependencies.opentelemetry] version = "0.20" optional = true diff --git a/src/components/mod.rs b/src/components/mod.rs new file mode 100644 index 0000000..097d182 --- /dev/null +++ b/src/components/mod.rs @@ -0,0 +1,89 @@ +pub mod crud { + use async_trait::async_trait; + + use crate::{models::Error, sqlite, Context}; + + #[async_trait] + pub trait Create: Sized { + type Id; + type Filter; + type Info; + + async fn create( + ctx: &Context, + pool: &sqlite::Pool, + filter: Self::Filter, + info: Self::Info, + ) -> Result; + } + + #[async_trait] + pub trait Read: Sized { + type Filter; + type Id; + + async fn findall( + ctx: &Context, + pool: &sqlite::Pool, + filter: Self::Filter, + ) -> Result, Error>; + + async fn find( + ctx: &Context, + pool: &sqlite::Pool, + filter: Self::Filter, + id: Self::Id, + ) -> Result, Error>; + } + + #[async_trait] + pub trait Update: Sized { + type Id; + type Filter; + type Update; + + async fn update( + ctx: &Context, + pool: &sqlite::Pool, + filter: Self::Filter, + id: Self::Id, + update: Self::Update, + ) -> Result, Error>; + } + + #[async_trait] + pub trait Delete: Sized { + type Id; + type Filter; + + async fn delete( + ctx: &Context, + pool: impl sqlx::Acquire, + filter: Self::Filter, + id: Self::Id, + ) -> Result; + + async fn delete_all( + ctx: &Context, + pool: &sqlite::Pool, + filter: Self::Filter, + ids: Vec, + ) -> Result { + let mut transaction = pool.begin().await?; + + for id in ids { + Self::delete(ctx, &mut transaction, filter, id).await?; + } + } + } +} + +pub mod view { + use maud::Markup; + + pub trait View { + type Input; + + fn build(&self, input: Self::Input) -> Markup; + } +} diff --git a/src/lib.rs b/src/lib.rs index 93d955c..9c79c04 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ use std::fmt; pub mod auth; pub mod cli; +pub mod components; pub mod error; pub mod htmx; pub mod models; diff --git a/src/models/trips/mod.rs b/src/models/trips/mod.rs index 879d94c..c4a8ee4 100644 --- a/src/models/trips/mod.rs +++ b/src/models/trips/mod.rs @@ -1,5 +1,7 @@ use std::fmt; +use crate::components::crud::*; + use super::{ consts, error::{DatabaseError, Error, QueryError}, @@ -1019,7 +1021,8 @@ impl Trip { #[tracing::instrument] pub async fn load_todos(&mut self, ctx: &Context, pool: &sqlite::Pool) -> Result<(), Error> { - self.todos = Some(todos::Todo::load(ctx, pool, self.id).await?); + self.todos = + Some(todos::Todo::findall(ctx, pool, todos::TodoFilter { trip_id: self.id }).await?); Ok(()) } diff --git a/src/models/trips/todos.rs b/src/models/trips/todos.rs index 6938aa3..f1a12b5 100644 --- a/src/models/trips/todos.rs +++ b/src/models/trips/todos.rs @@ -1,10 +1,12 @@ use maud::{html, Markup}; use uuid::Uuid; -use crate::{ - models::{Error, QueryError}, - sqlite, Context, -}; +use crate::components::crud; +use crate::components::view::{self, *}; + +use async_trait::async_trait; + +use crate::{models::Error, sqlite, Context}; use super::Trip; @@ -58,17 +60,28 @@ impl TryFrom for Todo { } } +#[derive(Debug)] +pub struct TodoFilter { + pub trip_id: Uuid, +} + impl Todo { pub fn is_done(&self) -> bool { self.state == State::Done } +} - pub async fn load( +#[async_trait] +impl crud::Read for Todo { + type Filter = TodoFilter; + type Id = Uuid; + + async fn findall( ctx: &Context, pool: &sqlite::Pool, - trip_id: Uuid, + filter: TodoFilter, ) -> Result, Error> { - let trip_id_param = trip_id.to_string(); + let trip_id_param = filter.trip_id.to_string(); let user_id = ctx.user.id.to_string(); let todos: Vec = crate::query_all!( @@ -100,13 +113,13 @@ impl Todo { } #[tracing::instrument] - pub async fn find( + async fn find( ctx: &Context, pool: &sqlite::Pool, - trip_id: Uuid, + filter: TodoFilter, todo_id: Uuid, ) -> Result, Error> { - let trip_id_param = trip_id.to_string(); + let trip_id_param = filter.trip_id.to_string(); let todo_id_param = todo_id.to_string(); let user_id = ctx.user.id.to_string(); crate::query_one!( @@ -136,72 +149,45 @@ impl Todo { ) .await } +} - #[tracing::instrument] - pub async fn set_state( +pub struct TodoNew { + pub description: String, +} + +#[async_trait] +impl crud::Create for Todo { + type Id = Uuid; + type Filter = TodoFilter; + type Info = TodoNew; + + async fn create( ctx: &Context, pool: &sqlite::Pool, - trip_id: Uuid, - todo_id: Uuid, - state: State, - ) -> Result<(), Error> { - let user_id = ctx.user.id.to_string(); - let trip_id_param = trip_id.to_string(); - let todo_id_param = todo_id.to_string(); - let done = state == State::Done; - - let result = crate::execute!( - &sqlite::QueryClassification { - query_type: sqlite::QueryType::Update, - component: sqlite::Component::Trips, - }, - pool, - r#" - UPDATE trip_todos - SET done = ? - WHERE trip_id = ? - AND id = ? - AND EXISTS(SELECT 1 FROM trips WHERE id = ? AND user_id = ?)"#, - done, - trip_id_param, - todo_id_param, - trip_id_param, - user_id - ) - .await?; - - (result.rows_affected() != 0).then_some(()).ok_or_else(|| { - Error::Query(QueryError::NotFound { - description: format!("todo {todo_id} not found for trip {trip_id}"), - }) - }) - } - - pub async fn new( - ctx: &Context, - pool: &sqlite::Pool, - trip_id: Uuid, - description: String, - ) -> Result { + filter: Self::Filter, + info: Self::Info, + ) -> Result { let user_id = ctx.user.id.to_string(); let id = Uuid::new_v4(); tracing::info!("adding new todo with id {id}"); let id_param = id.to_string(); - let trip_id_param = trip_id.to_string(); + let trip_id_param = filter.trip_id.to_string(); crate::execute!( &sqlite::QueryClassification { query_type: sqlite::QueryType::Insert, component: sqlite::Component::Todo, }, pool, - "INSERT INTO trip_todos - (id, description, done, trip_id) - SELECT ?, ?, false, id as trip_id - FROM trips - WHERE trip_id = ? AND EXISTS(SELECT 1 FROM trips WHERE id = ? and user_id = ?) - LIMIT 1", + r#" + INSERT INTO trip_todos + (id, description, done, trip_id) + SELECT ?, ?, false, id as trip_id + FROM trips + WHERE trip_id = ? AND EXISTS(SELECT 1 FROM trips WHERE id = ? and user_id = ?) + LIMIT 1 + "#, id_param, - description, + info.description, trip_id_param, trip_id_param, user_id, @@ -210,27 +196,130 @@ impl Todo { Ok(id) } +} + +#[derive(Debug)] +pub enum TodoUpdate { + State(State), + Description(String), +} + +#[async_trait] +impl crud::Update for Todo { + type Id = Uuid; + type Filter = TodoFilter; + type Update = TodoUpdate; #[tracing::instrument] - pub async fn delete( + async fn update( ctx: &Context, pool: &sqlite::Pool, - trip_id: Uuid, + filter: Self::Filter, + id: Self::Id, + update: Self::Update, + ) -> Result, Error> { + let user_id = ctx.user.id.to_string(); + let trip_id_param = filter.trip_id.to_string(); + let todo_id_param = id.to_string(); + match update { + TodoUpdate::State(state) => { + let done = state == State::Done; + + let result = crate::query_one!( + &sqlite::QueryClassification { + query_type: sqlite::QueryType::Update, + component: sqlite::Component::Trips, + }, + pool, + TodoRow, + Todo, + r#" + UPDATE trip_todos + SET done = ? + WHERE trip_id = ? + AND id = ? + AND EXISTS(SELECT 1 FROM trips WHERE id = ? AND user_id = ?) + RETURNING + id, + description, + done + "#, + done, + trip_id_param, + todo_id_param, + trip_id_param, + user_id + ) + .await?; + + Ok(result) + } + TodoUpdate::Description(new_description) => { + let user_id = ctx.user.id.to_string(); + let trip_id_param = filter.trip_id.to_string(); + let todo_id_param = id.to_string(); + + let result = crate::query_one!( + &sqlite::QueryClassification { + query_type: sqlite::QueryType::Update, + component: sqlite::Component::Todo, + }, + pool, + TodoRow, + Todo, + r#" + UPDATE trip_todos + SET description = ? + WHERE + id = ? + AND trip_id = ? + AND EXISTS(SELECT 1 FROM trips WHERE trip_id = ? AND user_id = ?) + RETURNING + id, + description, + done + "#, + new_description, + todo_id_param, + trip_id_param, + trip_id_param, + user_id, + ) + .await?; + + Ok(result) + } + } + } +} + +#[async_trait] +impl crud::Delete for Todo { + type Id = Uuid; + type Filter = TodoFilter; + + #[tracing::instrument] + async fn delete( + ctx: &Context, + pool: &sqlite::Pool, + filter: TodoFilter, id: Uuid, ) -> Result { let id_param = id.to_string(); let user_id = ctx.user.id.to_string(); - let trip_id_param = trip_id.to_string(); + let trip_id_param = filter.trip_id.to_string(); let results = crate::execute!( &sqlite::QueryClassification { query_type: sqlite::QueryType::Delete, component: sqlite::Component::Todo, }, pool, - "DELETE FROM trip_todos - WHERE - id = ? - AND EXISTS (SELECT 1 FROM trips WHERE trip_id = ? AND user_id = ?)", + r#" + DELETE FROM trip_todos + WHERE + id = ? + AND EXISTS (SELECT 1 FROM trips WHERE trip_id = ? AND user_id = ?) + "#, id_param, trip_id_param, user_id, @@ -247,9 +336,17 @@ pub enum TodoUiState { Edit, } -impl Todo { +#[derive(Debug)] +pub struct TodoBuildInput { + pub trip_id: Uuid, + pub state: TodoUiState, +} + +impl view::View for Todo { + type Input = TodoBuildInput; + #[tracing::instrument] - pub fn build(&self, trip_id: &Uuid, state: TodoUiState) -> Markup { + fn build(&self, input: Self::Input) -> Markup { let done = self.is_done(); html!( li @@ -261,19 +358,19 @@ impl Todo { ."bg-red-50"[!done] ."h-full" { - @if state == TodoUiState::Edit { + @if input.state == TodoUiState::Edit { form name="edit-todo" id="edit-todo" action={ - "/trips/" (trip_id) + "/trips/" (input.trip_id) "/todo/" (self.id) "/edit/save" } target="_self" method="post" hx-post={ - "/trips/" (trip_id) + "/trips/" (input.trip_id) "/todo/" (self.id) "/edit/save" } @@ -325,7 +422,7 @@ impl Todo { a href="." hx-post={ - "/trips/" (trip_id) + "/trips/" (input.trip_id) "/todo/" (self.id) "/edit/cancel" } @@ -352,12 +449,12 @@ impl Todo { ."aspect-square" ."hover:bg-red-50" href={ - "/trips/" (trip_id) + "/trips/" (input.trip_id) "/todo/" (self.id) "/undone" } hx-post={ - "/trips/" (trip_id) + "/trips/" (input.trip_id) "/todo/" (self.id) "/undone" } @@ -378,12 +475,12 @@ impl Todo { ."aspect-square" ."hover:bg-green-50" href={ - "/trips/" (trip_id) + "/trips/" (input.trip_id) "/todo/" (self.id) "/done" } hx-post={ - "/trips/" (trip_id) + "/trips/" (input.trip_id) "/todo/" (self.id) "/done" } @@ -412,7 +509,7 @@ impl Todo { ."hover:bg-blue-400" href=(format!("?edit_todo={id}", id = self.id)) hx-post={ - "/trips/" (trip_id) + "/trips/" (input.trip_id) "/todo/" (self.id) "/edit" } @@ -429,7 +526,7 @@ impl Todo { ."hover:bg-red-200" href=(format!("?delete_todo={id}", id = self.id)) hx-post={ - "/trips/" (trip_id) + "/trips/" (input.trip_id) "/todo/" (self.id) "/delete" } @@ -442,48 +539,6 @@ impl Todo { } ) } - - #[tracing::instrument] - pub async fn set_description( - ctx: &Context, - pool: &sqlite::Pool, - trip_id: Uuid, - todo_id: Uuid, - new_description: String, - ) -> Result, Error> { - let user_id = ctx.user.id.to_string(); - let trip_id_param = trip_id.to_string(); - let todo_id_param = todo_id.to_string(); - - let result = crate::query_one!( - &sqlite::QueryClassification { - query_type: sqlite::QueryType::Update, - component: sqlite::Component::Todo, - }, - pool, - TodoRow, - Todo, - "UPDATE trip_todos - SET description = ? - WHERE - id = ? - AND trip_id = ? - AND EXISTS(SELECT 1 FROM trips WHERE trip_id = ? AND user_id = ?) - RETURNING - id, - description, - done - ", - new_description, - todo_id_param, - trip_id_param, - trip_id_param, - user_id, - ) - .await?; - - Ok(result) - } } pub struct NewTodo; @@ -573,7 +628,7 @@ impl<'a> TodoList<'a> { } else { TodoUiState::Default }).unwrap_or(TodoUiState::Default); - (todo.build(&self.trip.id, state)) + (todo.build(TodoBuildInput{trip_id:self.trip.id, state})) } (NewTodo::build(&self.trip.id)) } diff --git a/src/routing/routes.rs b/src/routing/routes.rs index ebb999a..613883f 100644 --- a/src/routing/routes.rs +++ b/src/routing/routes.rs @@ -5,6 +5,10 @@ use axum::{ Form, }; +use crate::components::crud::*; +use crate::components::view::*; +use crate::models::trips::todos::{TodoBuildInput, TodoFilter, TodoNew, TodoUpdate}; + use crate::view::Component; use serde::Deserialize; @@ -433,8 +437,13 @@ pub async fn trip( state.client_state.active_category_id = trip_query.category; if let Some(delete_todo) = trip_query.delete_todo { - let deleted = - models::trips::todos::Todo::delete(&ctx, &state.database_pool, id, delete_todo).await?; + let deleted = models::trips::todos::Todo::delete( + &ctx, + &state.database_pool, + TodoFilter { trip_id: id }, + delete_todo, + ) + .await?; return if deleted { Ok(Redirect::to(get_referer(&headers)?).into_response()) @@ -1266,24 +1275,32 @@ pub async fn trip_todo_done_htmx( Path((trip_id, todo_id)): Path<(Uuid, Uuid)>, ) -> Result { let ctx = Context::build(current_user); - models::trips::todos::Todo::set_state( + models::trips::todos::Todo::update( &ctx, &state.database_pool, - trip_id, + TodoFilter { trip_id }, todo_id, - models::trips::todos::State::Done, + TodoUpdate::State(models::trips::todos::State::Done), ) .await?; - let todo_item = models::trips::todos::Todo::find(&ctx, &state.database_pool, trip_id, todo_id) - .await? - .ok_or_else(|| { - Error::Request(RequestError::NotFound { - message: format!("todo with id {todo_id} not found"), - }) - })?; + let todo_item = models::trips::todos::Todo::find( + &ctx, + &state.database_pool, + TodoFilter { trip_id }, + todo_id, + ) + .await? + .ok_or_else(|| { + Error::Request(RequestError::NotFound { + message: format!("todo with id {todo_id} not found"), + }) + })?; - Ok(todo_item.build(&trip_id, models::trips::todos::TodoUiState::Default)) + Ok(todo_item.build(TodoBuildInput { + trip_id, + state: models::trips::todos::TodoUiState::Default, + })) } #[tracing::instrument] @@ -1294,12 +1311,12 @@ pub async fn trip_todo_done( headers: HeaderMap, ) -> Result { let ctx = Context::build(current_user); - models::trips::todos::Todo::set_state( + models::trips::todos::Todo::update( &ctx, &state.database_pool, - trip_id, + TodoFilter { trip_id }, todo_id, - models::trips::todos::State::Done, + TodoUpdate::State(models::trips::todos::State::Done), ) .await?; @@ -1313,24 +1330,32 @@ pub async fn trip_todo_undone_htmx( Path((trip_id, todo_id)): Path<(Uuid, Uuid)>, ) -> Result { let ctx = Context::build(current_user); - models::trips::todos::Todo::set_state( + models::trips::todos::Todo::update( &ctx, &state.database_pool, - trip_id, + TodoFilter { trip_id }, todo_id, - models::trips::todos::State::Todo, + TodoUpdate::State(models::trips::todos::State::Todo), ) .await?; - let todo_item = models::trips::todos::Todo::find(&ctx, &state.database_pool, trip_id, todo_id) - .await? - .ok_or_else(|| { - Error::Request(RequestError::NotFound { - message: format!("todo with id {todo_id} not found"), - }) - })?; + let todo_item = models::trips::todos::Todo::find( + &ctx, + &state.database_pool, + TodoFilter { trip_id }, + todo_id, + ) + .await? + .ok_or_else(|| { + Error::Request(RequestError::NotFound { + message: format!("todo with id {todo_id} not found"), + }) + })?; - Ok(todo_item.build(&trip_id, models::trips::todos::TodoUiState::Default)) + Ok(todo_item.build(TodoBuildInput { + trip_id, + state: models::trips::todos::TodoUiState::Default, + })) } #[tracing::instrument] @@ -1341,12 +1366,12 @@ pub async fn trip_todo_undone( headers: HeaderMap, ) -> Result { let ctx = Context::build(current_user); - models::trips::todos::Todo::set_state( + models::trips::todos::Todo::update( &ctx, &state.database_pool, - trip_id, + TodoFilter { trip_id }, todo_id, - models::trips::todos::State::Todo, + TodoUpdate::State(models::trips::todos::State::Todo), ) .await?; @@ -1368,15 +1393,23 @@ pub async fn trip_todo_edit( Path((trip_id, todo_id)): Path<(Uuid, Uuid)>, ) -> Result { let ctx = Context::build(current_user); - let todo_item = - models::trips::todos::Todo::find(&ctx, &state.database_pool, trip_id, todo_id).await?; + let todo_item = models::trips::todos::Todo::find( + &ctx, + &state.database_pool, + TodoFilter { trip_id }, + todo_id, + ) + .await?; match todo_item { None => Err(Error::Request(RequestError::NotFound { message: format!("todo with id {todo_id} not found"), })), Some(todo_item) => Ok(todo_item - .build(&trip_id, models::trips::todos::TodoUiState::Edit) + .build(TodoBuildInput { + trip_id, + state: models::trips::todos::TodoUiState::Edit, + }) .into_response()), } } @@ -1390,12 +1423,12 @@ pub async fn trip_todo_edit_save( Form(form): Form, ) -> Result { let ctx = Context::build(current_user); - let todo_item = models::trips::todos::Todo::set_description( + let todo_item = models::trips::todos::Todo::update( &ctx, &state.database_pool, - trip_id, + TodoFilter { trip_id }, todo_id, - form.description, + TodoUpdate::Description(form.description), ) .await?; @@ -1406,7 +1439,10 @@ pub async fn trip_todo_edit_save( Some(todo_item) => { if htmx::is_htmx(&headers) { Ok(todo_item - .build(&trip_id, models::trips::todos::TodoUiState::Default) + .build(TodoBuildInput { + trip_id, + state: models::trips::todos::TodoUiState::Default, + }) .into_response()) } else { Ok(Redirect::to(&format!("/trips/{trip_id}/")).into_response()) @@ -1423,15 +1459,23 @@ pub async fn trip_todo_edit_cancel( Path((trip_id, todo_id)): Path<(Uuid, Uuid)>, ) -> Result { let ctx = Context::build(current_user); - let todo_item = - models::trips::todos::Todo::find(&ctx, &state.database_pool, trip_id, todo_id).await?; + let todo_item = models::trips::todos::Todo::find( + &ctx, + &state.database_pool, + TodoFilter { trip_id }, + todo_id, + ) + .await?; match todo_item { None => Err(Error::Request(RequestError::NotFound { message: format!("todo with id {todo_id} not found"), })), Some(todo_item) => Ok(todo_item - .build(&trip_id, models::trips::todos::TodoUiState::Default) + .build(TodoBuildInput { + trip_id, + state: models::trips::todos::TodoUiState::Default, + }) .into_response()), } } @@ -1453,9 +1497,15 @@ pub async fn trip_todo_new( ) -> Result { let ctx = Context::build(current_user); // method output is not required as we reload the whole trip todos anyway - let _todo_item = - models::trips::todos::Todo::new(&ctx, &state.database_pool, trip_id, form.description) - .await?; + let _todo_item = models::trips::todos::Todo::create( + &ctx, + &state.database_pool, + TodoFilter { trip_id }, + TodoNew { + description: form.description, + }, + ) + .await?; if htmx::is_htmx(&headers) { let trip = models::trips::Trip::find(&ctx, &state.database_pool, trip_id).await?; @@ -1486,8 +1536,13 @@ pub async fn trip_todo_delete( 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, trip_id, todo_id).await?; + 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 { diff --git a/src/telemetry/tracing/mod.rs b/src/telemetry/tracing/mod.rs index bf3c14d..16623f8 100644 --- a/src/telemetry/tracing/mod.rs +++ b/src/telemetry/tracing/mod.rs @@ -88,7 +88,7 @@ fn get_jaeger_layer< // mentioned earlier. let tracer = opentelemetry_jaeger::new_agent_pipeline() .with_service_name(env!("CARGO_PKG_NAME")) - .with_max_packet_size(20_000) + .with_max_packet_size(50_000) .with_auto_split_batch(true) .install_batch(Tokio) .unwrap();