From 428f46b8530e18c1a991b4b8d7b6e66c78984464 Mon Sep 17 00:00:00 2001 From: Max Hohlfeld Date: Wed, 2 Jul 2025 18:56:40 +0200 Subject: [PATCH] refactor: clothing and event changeset --- Cargo.lock | 56 ---- db/Cargo.toml | 1 - db/src/models/assignment_changeset.rs | 1 + db/src/models/event_changeset.rs | 302 +++++++++++++++------- db/src/models/mod.rs | 13 - web/Cargo.toml | 1 - web/src/endpoints/assignment/post_new.rs | 6 +- web/src/endpoints/clothing/mod.rs | 4 +- web/src/endpoints/clothing/post_edit.rs | 5 +- web/src/endpoints/clothing/post_new.rs | 5 +- web/src/endpoints/events/post_edit.rs | 109 +------- web/src/endpoints/events/post_new.rs | 26 +- web/src/utils/test_helper/test_context.rs | 10 +- 13 files changed, 251 insertions(+), 288 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 173c2684..cb310878 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -794,7 +794,6 @@ version = "0.1.0" dependencies = [ "chrono", "fake", - "garde", "rand 0.9.1", "regex", "serde", @@ -831,7 +830,6 @@ dependencies = [ "chrono", "fake", "futures-util", - "garde", "insta", "lettre", "maud", @@ -906,15 +904,6 @@ dependencies = [ "bytes", ] -[[package]] -name = "castaway" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" -dependencies = [ - "rustversion", -] - [[package]] name = "cc" version = "1.2.22" @@ -1023,20 +1012,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" -[[package]] -name = "compact_str" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32" -dependencies = [ - "castaway", - "cfg-if", - "itoa", - "rustversion", - "ryu", - "static_assertions", -] - [[package]] name = "concurrent-queue" version = "2.5.0" @@ -1659,31 +1634,6 @@ dependencies = [ "slab", ] -[[package]] -name = "garde" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a989bd2fd12136080f7825ff410d9239ce84a2a639487fc9d924ee42e2fb84f" -dependencies = [ - "compact_str", - "garde_derive", - "once_cell", - "regex", - "smallvec", -] - -[[package]] -name = "garde_derive" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f7f0545bbbba0a37d4d445890fa5759814e0716f02417b39f6fab292193df68" -dependencies = [ - "proc-macro2", - "quote", - "regex", - "syn 2.0.101", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -3399,12 +3349,6 @@ dependencies = [ "path-slash", ] -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "stringprep" version = "0.1.5" diff --git a/db/Cargo.toml b/db/Cargo.toml index d09eedbb..f99b86b6 100644 --- a/db/Cargo.toml +++ b/db/Cargo.toml @@ -9,7 +9,6 @@ publish = false [dependencies] sqlx = { version = "^0.8", features = ["runtime-async-std-rustls", "postgres", "chrono"] } chrono = { version = "0.4.33", features = ["serde", "now"] } -garde = { version = "0.22.0", features = ["derive", "email"] } # refactor out serde = { version = "1", features = ["derive"] } rand = { version = "0.9", features = ["os_rng"] } regex = "1.11.1" diff --git a/db/src/models/assignment_changeset.rs b/db/src/models/assignment_changeset.rs index bdc0f556..36ad5b31 100644 --- a/db/src/models/assignment_changeset.rs +++ b/db/src/models/assignment_changeset.rs @@ -167,6 +167,7 @@ async fn availability_not_already_assigned( Ok(()) } +// TODO: maybe merge with event changeset fn user_is_admin_or_area_manager_of_event_area( user: &User, event: &Event, diff --git a/db/src/models/event_changeset.rs b/db/src/models/event_changeset.rs index 12629787..ce8b7f97 100644 --- a/db/src/models/event_changeset.rs +++ b/db/src/models/event_changeset.rs @@ -1,34 +1,234 @@ +use chrono::Days; use chrono::NaiveDate; use chrono::NaiveDateTime; #[cfg(feature = "test-helpers")] use fake::{Fake, Faker}; -use garde::Validate; +use sqlx::PgPool; -use super::start_date_time_lies_before_end_date_time; +use crate::END_OF_DAY; +use crate::START_OF_DAY; +use crate::models::Assignment; +use crate::models::Availability; +use crate::models::Event; +use crate::models::Function; +use crate::models::Location; +use crate::models::Role; +use crate::models::User; +use crate::validation::AsyncValidate; +use crate::validation::AsyncValidateError; +use crate::validation::start_date_time_lies_before_end_date_time; -#[derive(Validate)] -#[garde(allow_unvalidated)] -#[garde(context(Option as ctx))] pub struct EventChangeset { - #[garde( - custom(start_date_time_lies_before_end_date_time), - custom(time_can_be_extended_if_edit), - custom(date_unchanged_if_edit) - )] pub time: (NaiveDateTime, NaiveDateTime), pub name: String, - /// check before: must exist and user can create event for this location pub location_id: i32, - #[garde(custom(can_unset_wachhabender))] pub voluntary_wachhabender: bool, - #[garde(custom(can_unset_fuehrungsassistent))] pub voluntary_fuehrungsassistent: bool, - #[garde(range(min = ctx.as_ref().map(|c: &EventContext| c.amount_of_assigned_posten).unwrap_or(0), max = 100))] pub amount_of_posten: i16, pub clothing: i32, pub note: Option, } +pub struct EventContext<'a> { + pub pool: &'a PgPool, + pub event: Option, + pub user: &'a User, +} + +impl<'a> AsyncValidate<'a> for EventChangeset { + type Context = EventContext<'a>; + + async fn validate_with_context( + &self, + context: &'a Self::Context, + ) -> Result<(), AsyncValidateError> { + let Some(location) = Location::read_by_id(context.pool, self.location_id).await? else { + return Err(AsyncValidateError::new( + "Der angegebene Veranstaltungsort existiert nicht.", + )); + }; + + user_is_admin_or_area_manager_of_event_location(context.user, &location)?; + + start_date_time_lies_before_end_date_time(&self.time.0, &self.time.1)?; + + let mut minimum_amount_of_posten = 0_i16; + if let Some(id) = context.event { + let event = Event::read_by_id_including_location(context.pool, id) + .await? + .unwrap(); + let assignments_for_event = + Assignment::read_all_by_event(context.pool, event.id).await?; + minimum_amount_of_posten = assignments_for_event + .iter() + .filter(|a| a.function == Function::Posten) + .count() as i16; + + time_can_be_extended_if_edit(&self.time, &event, &assignments_for_event, context.pool) + .await?; + date_unchanged_if_edit(&self.time, &event.start.date())?; + can_unset_wachhabender(&self.voluntary_wachhabender, &assignments_for_event)?; + can_unset_fuehrungsassistent( + &self.voluntary_fuehrungsassistent, + &assignments_for_event, + )?; + if location.area_id != event.location.unwrap().area_id { + return Err(AsyncValidateError::new( + "Veranstaltungsort kann nicht zu einem Ort außerhalb des initialen Bereichs geändert werden.", + )); + } + } + + if !(minimum_amount_of_posten..=100).contains(&self.amount_of_posten) { + return Err(AsyncValidateError::new( + "Die Anzahl der Posten darf nicht kleiner als die Anzahl der bereits geplanten Posten und maximal 100 sein.", + )); + } + + Ok(()) + } +} + +fn user_is_admin_or_area_manager_of_event_location( + user: &User, + location: &Location, +) -> Result<(), AsyncValidateError> { + if user.role != Role::Admin + && !(user.role == Role::AreaManager && user.area_id == location.area_id) + { + return Err(AsyncValidateError::new( + "Du verfügst nicht über die Berechtigung, diese Veranstaltung zu erstellen bzw. zu bearbeiten.", + )); + } + Ok(()) +} + +fn date_unchanged_if_edit( + time: &(NaiveDateTime, NaiveDateTime), + date_in_db: &NaiveDate, +) -> Result<(), AsyncValidateError> { + if time.0.date() != *date_in_db { + return Err(AsyncValidateError::new("event date can't be changed")); + } + + Ok(()) +} + +async fn time_can_be_extended_if_edit( + time: &(NaiveDateTime, NaiveDateTime), + event: &Event, + assignments_for_event: &Vec, + pool: &PgPool, +) -> Result<(), AsyncValidateError> { + let start = event.start.date(); + let end = event.start.date().checked_add_days(Days::new(1)).unwrap(); + let mut common_time = (start.and_time(START_OF_DAY), end.and_time(END_OF_DAY)); + + for assignment in assignments_for_event { + let availability = Availability::read_by_id(pool, assignment.availability_id) + .await? + .unwrap(); + let all_assignments = + Assignment::read_all_by_availability(pool, assignment.availability_id).await?; + + if all_assignments.len() == 1 { + if availability.start > common_time.0 { + common_time.0 = availability.start; + } + + if availability.end < common_time.1 { + common_time.1 = availability.end; + } + } else { + let mut slots = vec![(availability.start, availability.end)]; + for a in all_assignments + .iter() + .filter(|x| x.event_id != assignment.event_id) + { + let (fit, rest) = slots + .into_iter() + .partition(|s| s.0 >= a.start && s.1 <= a.end); + slots = rest; + let fit = fit.first().unwrap(); + + if fit.0 != a.start { + slots.push((fit.0, a.start)); + } + + if fit.1 != a.end { + slots.push((a.end, fit.1)); + } + } + + let slot = slots + .into_iter() + .find(|s| s.0 >= assignment.start && s.1 <= assignment.end) + .unwrap(); + + if slot.0 > common_time.0 { + common_time.0 = slot.0; + } + + if slot.1 < common_time.1 { + common_time.1 = slot.1; + } + } + } + + let old_start_time = common_time.0; + let new_start_time = time.0; + let old_end_time = common_time.1; + let new_end_time = time.1; + + if new_start_time < old_start_time { + return Err(AsyncValidateError::new( + "starttime lies outside of available time for assigned people", + )); + } + + if new_end_time > old_end_time { + return Err(AsyncValidateError::new( + "endtime lies ouside of available time for assigned people", + )); + } + + Ok(()) +} + +fn can_unset_fuehrungsassistent( + fuehrungsassistent_required: &bool, + assignments_for_event: &Vec, +) -> Result<(), AsyncValidateError> { + if !*fuehrungsassistent_required + && assignments_for_event + .iter() + .any(|a| a.function == Function::Fuehrungsassistent) + { + return Err(AsyncValidateError::new( + "fuehrungsassistent can't be set to not by ff, because a person is already assigned", + )); + } + + Ok(()) +} + +fn can_unset_wachhabender( + voluntary_wachhabender: &bool, + assignments_for_event: &Vec, +) -> Result<(), AsyncValidateError> { + if !*voluntary_wachhabender + && assignments_for_event + .iter() + .any(|a| a.function == Function::Wachhabender) + { + return Err(AsyncValidateError::new( + "wachhabender can't be set to not by ff, because a person is already assigned", + )); + } + + Ok(()) +} + #[cfg(feature = "test-helpers")] impl EventChangeset { pub fn create_for_test(start: NaiveDateTime, end: NaiveDateTime) -> EventChangeset { @@ -46,77 +246,3 @@ impl EventChangeset { changeset } } - -pub struct EventContext { - pub date_in_db: NaiveDate, - pub common_min_max_available_time: (NaiveDateTime, NaiveDateTime), - pub wachhabender_assigned: bool, - pub fuehrungsassistent_assigned: bool, - pub amount_of_assigned_posten: i16, -} - -fn date_unchanged_if_edit( - value: &(NaiveDateTime, NaiveDateTime), - context: &Option, -) -> garde::Result { - if context - .as_ref() - .is_some_and(|c| c.date_in_db != value.0.date()) - { - return Err(garde::Error::new("event date can't be changed")); - } - - Ok(()) -} - -fn time_can_be_extended_if_edit( - value: &(NaiveDateTime, NaiveDateTime), - context: &Option, -) -> garde::Result { - if let Some(context) = context { - let old_start_time = context.common_min_max_available_time.0; - let new_start_time = value.0; - let old_end_time = context.common_min_max_available_time.1; - let new_end_time = value.1; - - if new_start_time < old_start_time { - return Err(garde::Error::new( - "starttime lies outside of available time for assigned people", - )); - } - - if new_end_time > old_end_time { - return Err(garde::Error::new( - "endtime lies ouside of available time for assigned people", - )); - } - } - - Ok(()) -} - -fn can_unset_fuehrungsassistent(value: &bool, context: &Option) -> garde::Result { - if context - .as_ref() - .is_some_and(|c| !*value && c.fuehrungsassistent_assigned) - { - return Err(garde::Error::new( - "fuehrungsassistent can't be set to not by ff, because a person is already assigned", - )); - } - - Ok(()) -} - -fn can_unset_wachhabender(value: &bool, context: &Option) -> garde::Result { - if context - .as_ref() - .is_some_and(|c| !*value && c.wachhabender_assigned) - { - return Err(garde::Error::new( - "wachhabender can't be set to not by ff, because a person is already assigned", - )); - } - - Ok(()) -} diff --git a/db/src/models/mod.rs b/db/src/models/mod.rs index 397e9be5..7d2c6fa0 100644 --- a/db/src/models/mod.rs +++ b/db/src/models/mod.rs @@ -42,17 +42,4 @@ pub use user_funtion::UserFunction; pub use vehicle::Vehicle; pub use vehicle_assignment::VehicleAssignment; -use chrono::NaiveDateTime; - type Result = std::result::Result; - -fn start_date_time_lies_before_end_date_time( - value: &(NaiveDateTime, NaiveDateTime), - _context: &T, -) -> garde::Result { - if value.0 >= value.1 { - return Err(garde::Error::new("endtime can't lie before starttime")); - } - - Ok(()) -} diff --git a/web/Cargo.toml b/web/Cargo.toml index 7090b4c1..7fd13551 100644 --- a/web/Cargo.toml +++ b/web/Cargo.toml @@ -31,7 +31,6 @@ brass-config = { path = "../config" } brass-db = { path = "../db" } actix-http = "3.9.0" askama = "0.13.0" -garde = { version = "0.22.0", features = ["derive", "email"] } maud = "0.27.0" tracing-actix-web = "0.7.18" tracing = "0.1.41" diff --git a/web/src/endpoints/assignment/post_new.rs b/web/src/endpoints/assignment/post_new.rs index 68ec8d22..1b3135b4 100644 --- a/web/src/endpoints/assignment/post_new.rs +++ b/web/src/endpoints/assignment/post_new.rs @@ -13,7 +13,7 @@ use crate::{ }; use brass_db::{ - models::{Assignment, AssignmentChangeset, AssignmentContext, Event, Function, User}, + models::{Assignment, AssignmentChangeset, AssignmentContext, Event, Function, Role, User}, validation::AsyncValidate, }; @@ -30,6 +30,10 @@ pub async fn post( pool: web::Data, query: web::Query, ) -> Result { + if user.role != Role::Admin && user.role != Role::AreaManager { + return Err(ApplicationError::Unauthorized); + } + let Some(event) = Event::read_by_id_including_location(pool.get_ref(), query.event).await? else { return Ok(HttpResponse::NotFound().finish()); diff --git a/web/src/endpoints/clothing/mod.rs b/web/src/endpoints/clothing/mod.rs index 7fbd4822..29b211c4 100644 --- a/web/src/endpoints/clothing/mod.rs +++ b/web/src/endpoints/clothing/mod.rs @@ -1,5 +1,4 @@ use askama::Template; -use garde::Validate; use serde::Deserialize; use crate::filters; @@ -26,8 +25,7 @@ struct ReadClothingPartialTemplate { c: Clothing, } -#[derive(Deserialize, Validate)] +#[derive(Deserialize)] struct NewOrEditClothingForm { - #[garde(length(min = 3))] name: String, } diff --git a/web/src/endpoints/clothing/post_edit.rs b/web/src/endpoints/clothing/post_edit.rs index 4a06c40e..a32b2d44 100644 --- a/web/src/endpoints/clothing/post_edit.rs +++ b/web/src/endpoints/clothing/post_edit.rs @@ -1,5 +1,4 @@ use actix_web::{web, HttpResponse, Responder}; -use garde::Validate; use sqlx::PgPool; use crate::{ @@ -26,8 +25,8 @@ pub async fn post( return Ok(HttpResponse::NotFound().finish()); }; - if let Err(e) = form.validate() { - return Ok(HttpResponse::UnprocessableEntity().body(e.to_string())); + if form.name.chars().count() < 3 { + return Ok(HttpResponse::UnprocessableEntity().body("Name der Bekleidung ist zu kurz.")); }; clothing.name = form.name.to_string(); diff --git a/web/src/endpoints/clothing/post_new.rs b/web/src/endpoints/clothing/post_new.rs index 5e76524b..bca24c1a 100644 --- a/web/src/endpoints/clothing/post_new.rs +++ b/web/src/endpoints/clothing/post_new.rs @@ -1,5 +1,4 @@ use actix_web::{web, HttpResponse, Responder}; -use garde::Validate; use sqlx::PgPool; use crate::{ @@ -18,8 +17,8 @@ pub async fn post( return Err(ApplicationError::Unauthorized); } - if let Err(e) = form.validate() { - return Ok(HttpResponse::UnprocessableEntity().body(e.to_string())); + if form.name.chars().count() < 3 { + return Ok(HttpResponse::UnprocessableEntity().body("Name der Bekleidung ist zu kurz.")); }; let clothing = Clothing::create(pool.get_ref(), &form.name).await?; diff --git a/web/src/endpoints/events/post_edit.rs b/web/src/endpoints/events/post_edit.rs index c754ddb7..e2fe04d4 100644 --- a/web/src/endpoints/events/post_edit.rs +++ b/web/src/endpoints/events/post_edit.rs @@ -1,16 +1,13 @@ use actix_web::{http::header::LOCATION, web, HttpResponse, Responder}; -use chrono::Days; -use garde::Validate; use sqlx::PgPool; use crate::{ endpoints::{events::NewOrEditEventForm, IdPath}, utils::{self, ApplicationError}, - END_OF_DAY, START_OF_DAY, }; -use brass_db::models::{ - Assignment, AssignmentChangeset, Availability, Event, EventChangeset, EventContext, Function, - Location, Role, User, +use brass_db::{ + models::{Assignment, AssignmentChangeset, Event, EventChangeset, EventContext, Role, User}, + validation::AsyncValidate, }; #[actix_web::post("/events/{id}/edit")] @@ -28,21 +25,6 @@ pub async fn post( return Ok(HttpResponse::NotFound().finish()); }; - if event.location_id != form.location { - let Some(location) = Location::read_by_id(pool.get_ref(), form.location).await? else { - return Ok(HttpResponse::BadRequest().body("Location does not exist")); - }; - - if user.role != Role::Admin && user.area_id != location.area_id { - return Ok(HttpResponse::BadRequest().body("Can't use location outside of your area")); - } - - if event.location.as_ref().unwrap().area_id != location.area_id { - return Ok(HttpResponse::BadRequest() - .body("Can't change to a location outside of previous location area")); - } - } - let changeset = EventChangeset { amount_of_posten: form.amount, clothing: form.clothing, @@ -57,85 +39,18 @@ pub async fn post( voluntary_fuehrungsassistent: form.voluntaryfuehrungsassistent, }; - let assignments_for_event = Assignment::read_all_by_event(pool.get_ref(), event.id).await?; + let context = EventContext { + event: Some(event.id), + pool: pool.get_ref(), + user: &user.into_inner(), + }; - let start = event.start.date(); - let end = event.start.date().checked_add_days(Days::new(1)).unwrap(); - let mut common_time = (start.and_time(START_OF_DAY), end.and_time(END_OF_DAY)); - - for assignment in &assignments_for_event { - let availability = Availability::read_by_id(pool.get_ref(), assignment.availability_id) - .await? - .unwrap(); - let all_assignments = - Assignment::read_all_by_availability(pool.get_ref(), assignment.availability_id) - .await?; - - if all_assignments.len() == 1 { - if availability.start > common_time.0 { - common_time.0 = availability.start; - } - - if availability.end < common_time.1 { - common_time.1 = availability.end; - } - } else { - let mut slots = vec![(availability.start, availability.end)]; - for a in all_assignments - .iter() - .filter(|x| x.event_id != assignment.event_id) - { - let (fit, rest) = slots - .into_iter() - .partition(|s| s.0 >= a.start && s.1 <= a.end); - slots = rest; - let fit = fit.first().unwrap(); - - if fit.0 != a.start { - slots.push((fit.0, a.start)); - } - - if fit.1 != a.end { - slots.push((a.end, fit.1)); - } - } - - let slot = slots - .into_iter() - .find(|s| s.0 >= assignment.start && s.1 <= assignment.end) - .unwrap(); - - if slot.0 > common_time.0 { - common_time.0 = slot.0; - } - - if slot.1 < common_time.1 { - common_time.1 = slot.1; - } - } - } - - let context = Some(EventContext { - date_in_db: event.start.date(), - common_min_max_available_time: common_time, - // safe as max amount of posten can be only 100 - amount_of_assigned_posten: assignments_for_event - .iter() - .filter(|a| a.function == Function::Posten) - .count() as i16, - wachhabender_assigned: assignments_for_event - .iter() - .any(|a| a.function == Function::Wachhabender), - fuehrungsassistent_assigned: assignments_for_event - .iter() - .any(|a| a.function == Function::Fuehrungsassistent), - }); - - if let Err(e) = changeset.validate_with(&context) { - return Ok(HttpResponse::BadRequest().body(e.to_string())); + if let Err(e) = changeset.validate_with_context(&context).await { + return Ok(HttpResponse::UnprocessableEntity().body(e.to_string())); }; if event.start != changeset.time.0 || event.end != changeset.time.1 { + let assignments_for_event = Assignment::read_all_by_event(pool.get_ref(), event.id).await?; for a in assignments_for_event { let c = AssignmentChangeset { function: a.function, @@ -148,7 +63,7 @@ pub async fn post( Event::update(pool.get_ref(), event.id, changeset).await?; let url = utils::get_return_url_for_date(&form.date); - //println!("redirecto to {url}"); + Ok(HttpResponse::Found() .insert_header((LOCATION, url.clone())) .insert_header(("HX-LOCATION", url)) diff --git a/web/src/endpoints/events/post_new.rs b/web/src/endpoints/events/post_new.rs index 6be0c2bc..e7554706 100644 --- a/web/src/endpoints/events/post_new.rs +++ b/web/src/endpoints/events/post_new.rs @@ -1,12 +1,14 @@ use actix_web::{http::header::LOCATION, web, HttpResponse, Responder}; -use garde::Validate; use sqlx::PgPool; use crate::{ endpoints::events::NewOrEditEventForm, utils::{self, ApplicationError}, }; -use brass_db::models::{Event, EventChangeset, Location, Role, User}; +use brass_db::{ + models::{Event, EventChangeset, EventContext, Role, User}, + validation::AsyncValidate, +}; #[actix_web::post("/events/new")] pub async fn post( @@ -18,14 +20,6 @@ pub async fn post( return Err(ApplicationError::Unauthorized); } - let Some(location) = Location::read_by_id(pool.get_ref(), form.location).await? else { - return Ok(HttpResponse::BadRequest().body("Location does not exist")); - }; - - if user.role != Role::Admin && user.area_id != location.area_id { - return Ok(HttpResponse::BadRequest().body("Can't use location outside of your area")); - } - let changeset = EventChangeset { amount_of_posten: form.amount, clothing: form.clothing, @@ -40,14 +34,20 @@ pub async fn post( voluntary_fuehrungsassistent: form.voluntaryfuehrungsassistent, }; - if let Err(e) = changeset.validate_with(&None) { - return Ok(HttpResponse::BadRequest().body(e.to_string())); + let event_context = EventContext { + event: None, + user: &user.into_inner(), + pool: pool.get_ref(), + }; + + if let Err(e) = changeset.validate_with_context(&event_context).await { + return Ok(HttpResponse::UnprocessableEntity().body(e.to_string())); }; Event::create(pool.get_ref(), changeset).await?; let url = utils::get_return_url_for_date(&form.date); - //println!("redirecto to {url}"); + Ok(HttpResponse::Found() .insert_header((LOCATION, url.clone())) .insert_header(("HX-LOCATION", url)) diff --git a/web/src/utils/test_helper/test_context.rs b/web/src/utils/test_helper/test_context.rs index ad949d67..1cd1e97d 100644 --- a/web/src/utils/test_helper/test_context.rs +++ b/web/src/utils/test_helper/test_context.rs @@ -7,7 +7,6 @@ use actix_web::{ test::init_service, }; use rand::{distr::Alphanumeric, rng, Rng}; -use tracing_subscriber::{fmt, layer::SubscriberExt, registry, util::SubscriberInitExt, EnvFilter}; use crate::utils::Customization; use crate::{create_app, mail::Mailer}; @@ -33,7 +32,7 @@ impl DbTestContext { > { let customization = Customization { webmaster_email: self.config.webmaster_email.clone(), - hostname: self.config.hostname.clone(), + hostname: self.config.hostname.clone() }; init_service(create_app( @@ -51,13 +50,6 @@ pub async fn setup() -> DbTestContext { let init_config: OnceCell = OnceCell::new(); let config = init_config.get_or_init(|| load_config(&Environment::Test).unwrap()); - // TODO: unite with init_tracing in main - let filter = EnvFilter::try_from_default_env() - .or_else(|_| EnvFilter::try_new("info")) - .unwrap(); - - registry().with(fmt::layer()).with(filter).init(); - let test_db_config = prepare_db(&config.database_url).await; let test_db_pool = PgPoolOptions::new()