refactor crud
This commit is contained in:
@@ -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"
|
||||
}
|
||||
12
.sqlx/query-4b087f9afbc94acbdf09a04c934b158668704d4497c2f7fef327d56559c5ee8a.json
generated
Normal file
12
.sqlx/query-4b087f9afbc94acbdf09a04c934b158668704d4497c2f7fef327d56559c5ee8a.json
generated
Normal file
@@ -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"
|
||||
}
|
||||
32
.sqlx/query-4f9e7f676f42a548c8993a0182313fb3beebb547e658ea80302490953205a4c5.json
generated
Normal file
32
.sqlx/query-4f9e7f676f42a548c8993a0182313fb3beebb547e658ea80302490953205a4c5.json
generated
Normal file
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
32
.sqlx/query-d20992191e5fd34a3b03a117abf5e95f04eedf2875afa5b2d687b893fee5fbbb.json
generated
Normal file
32
.sqlx/query-d20992191e5fd34a3b03a117abf5e95f04eedf2875afa5b2d687b893fee5fbbb.json
generated
Normal file
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
12
.sqlx/query-d47bf74e8aaecdeb3730edab4d267f61ab03356497e777e55493c4f188e14a25.json
generated
Normal file
12
.sqlx/query-d47bf74e8aaecdeb3730edab4d267f61ab03356497e777e55493c4f188e14a25.json
generated
Normal file
@@ -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"
|
||||
}
|
||||
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -1428,6 +1428,7 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||
name = "packager"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum",
|
||||
"axum-prometheus",
|
||||
"base64 0.21.4",
|
||||
|
||||
@@ -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
|
||||
|
||||
89
src/components/mod.rs
Normal file
89
src/components/mod.rs
Normal file
@@ -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<Self::Id, Error>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait Read: Sized {
|
||||
type Filter;
|
||||
type Id;
|
||||
|
||||
async fn findall(
|
||||
ctx: &Context,
|
||||
pool: &sqlite::Pool,
|
||||
filter: Self::Filter,
|
||||
) -> Result<Vec<Self>, Error>;
|
||||
|
||||
async fn find(
|
||||
ctx: &Context,
|
||||
pool: &sqlite::Pool,
|
||||
filter: Self::Filter,
|
||||
id: Self::Id,
|
||||
) -> Result<Option<Self>, 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<Option<Self>, 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<bool, Error>;
|
||||
|
||||
async fn delete_all(
|
||||
ctx: &Context,
|
||||
pool: &sqlite::Pool,
|
||||
filter: Self::Filter,
|
||||
ids: Vec<Self::Id>,
|
||||
) -> Result<bool, Error> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
|
||||
@@ -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<TodoRow> 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<Vec<Self>, 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<Todo> = 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<Option<Self>, 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<Uuid, Error> {
|
||||
filter: Self::Filter,
|
||||
info: Self::Info,
|
||||
) -> Result<Self::Id, Error> {
|
||||
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
|
||||
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",
|
||||
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<Option<Self>, 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<bool, Error> {
|
||||
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
|
||||
r#"
|
||||
DELETE FROM trip_todos
|
||||
WHERE
|
||||
id = ?
|
||||
AND EXISTS (SELECT 1 FROM trips WHERE trip_id = ? AND user_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<Option<Self>, 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))
|
||||
}
|
||||
|
||||
@@ -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,16 +1275,21 @@ pub async fn trip_todo_done_htmx(
|
||||
Path((trip_id, todo_id)): Path<(Uuid, Uuid)>,
|
||||
) -> Result<impl IntoResponse, Error> {
|
||||
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)
|
||||
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 {
|
||||
@@ -1283,7 +1297,10 @@ pub async fn trip_todo_done_htmx(
|
||||
})
|
||||
})?;
|
||||
|
||||
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<impl IntoResponse, Error> {
|
||||
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,16 +1330,21 @@ pub async fn trip_todo_undone_htmx(
|
||||
Path((trip_id, todo_id)): Path<(Uuid, Uuid)>,
|
||||
) -> Result<impl IntoResponse, Error> {
|
||||
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)
|
||||
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 {
|
||||
@@ -1330,7 +1352,10 @@ pub async fn trip_todo_undone_htmx(
|
||||
})
|
||||
})?;
|
||||
|
||||
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<impl IntoResponse, Error> {
|
||||
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<impl IntoResponse, Error> {
|
||||
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<TripTodoDescription>,
|
||||
) -> Result<impl IntoResponse, Error> {
|
||||
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<impl IntoResponse, Error> {
|
||||
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,8 +1497,14 @@ pub async fn trip_todo_new(
|
||||
) -> Result<impl IntoResponse, Error> {
|
||||
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)
|
||||
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) {
|
||||
@@ -1486,8 +1536,13 @@ pub async fn trip_todo_delete(
|
||||
Path((trip_id, todo_id)): Path<(Uuid, Uuid)>,
|
||||
) -> Result<impl IntoResponse, Error> {
|
||||
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 {
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user