feat: finish implementing assignment validation
This commit is contained in:
parent
512b061c7a
commit
bdaf8ff20e
@ -1,10 +1,9 @@
|
|||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use chrono::{Local, NaiveDateTime, Utc};
|
use chrono::Local;
|
||||||
use sqlx::migrate::Migrate;
|
use sqlx::migrate::Migrate;
|
||||||
use sqlx::{migrate::Migrator, Executor};
|
use sqlx::{migrate::Migrator, Executor};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::time::SystemTime;
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
@ -37,6 +36,7 @@ enum Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_std::main]
|
#[async_std::main]
|
||||||
|
#[allow(unused)]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
let config = load_config(&cli.environment).expect("Could not load config!");
|
let config = load_config(&cli.environment).expect("Could not load config!");
|
||||||
|
@ -3,6 +3,7 @@ use sqlx::{PgPool, query};
|
|||||||
|
|
||||||
use super::{AssignmentChangeset, Function, Result};
|
use super::{AssignmentChangeset, Function, Result};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Assignment {
|
pub struct Assignment {
|
||||||
pub event_id: i32,
|
pub event_id: i32,
|
||||||
pub availability_id: i32,
|
pub availability_id: i32,
|
||||||
|
@ -31,7 +31,7 @@ impl<'a> AsyncValidate<'a> for AssignmentChangeset {
|
|||||||
Availability::read_by_id_including_user(context.pool, context.availability_id).await?
|
Availability::read_by_id_including_user(context.pool, context.availability_id).await?
|
||||||
else {
|
else {
|
||||||
return Err(AsyncValidateError::new(
|
return Err(AsyncValidateError::new(
|
||||||
"Angegebener Verfügbarkeit des Nutzers existiert nicht!",
|
"Angegebener Verfügbarkeit des Nutzers existiert nicht.",
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ impl<'a> AsyncValidate<'a> for AssignmentChangeset {
|
|||||||
Event::read_by_id_including_location(context.pool, context.event_id).await?
|
Event::read_by_id_including_location(context.pool, context.event_id).await?
|
||||||
else {
|
else {
|
||||||
return Err(AsyncValidateError::new(
|
return Err(AsyncValidateError::new(
|
||||||
"Angegebenes Event existiert nicht!",
|
"Angegebene Veranstaltung existiert nicht.",
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ fn availability_user_inside_event_area(
|
|||||||
|
|
||||||
if user.area_id != location.area_id {
|
if user.area_id != location.area_id {
|
||||||
return Err(AsyncValidateError::new(
|
return Err(AsyncValidateError::new(
|
||||||
"Nutzer der Verfügbarkeit ist nicht im gleichen Bereich wie der Ort der Veranstaltung!",
|
"Nutzer der Verfügbarkeit ist nicht im gleichen Bereich wie der Ort der Veranstaltung.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ fn available_time_fits(
|
|||||||
) -> Result<(), AsyncValidateError> {
|
) -> Result<(), AsyncValidateError> {
|
||||||
if value.0 < availability.start || value.1 > availability.end {
|
if value.0 < availability.start || value.1 > availability.end {
|
||||||
return Err(AsyncValidateError::new(
|
return Err(AsyncValidateError::new(
|
||||||
"time not made available can't be assigned",
|
"Die verfügbar gemachte Zeit passt nicht zu der zugewiesenen Zeit für die Veranstaltung.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,7 +93,7 @@ fn user_of_availability_has_function(
|
|||||||
|
|
||||||
if !user_function.contains(value) {
|
if !user_function.contains(value) {
|
||||||
return Err(AsyncValidateError::new(
|
return Err(AsyncValidateError::new(
|
||||||
"user has not the required function for this assignment",
|
"Nutzer der Verfügbarkeit besitzt nicht die benötigte Funktion um für diese Position zugewiesen zu werden.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,12 +106,15 @@ async fn event_has_free_slot_for_function(
|
|||||||
event: &Event,
|
event: &Event,
|
||||||
pool: &PgPool,
|
pool: &PgPool,
|
||||||
) -> Result<(), AsyncValidateError> {
|
) -> Result<(), AsyncValidateError> {
|
||||||
|
debug!(?event, "event parameter");
|
||||||
let assignments_for_event: Vec<Assignment> = Assignment::read_all_by_event(pool, event.id)
|
let assignments_for_event: Vec<Assignment> = Assignment::read_all_by_event(pool, event.id)
|
||||||
.await?
|
.await?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|a| a.availability_id != availability.id && a.event_id != event.id)
|
.filter(|a| a.availability_id != availability.id)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
debug!(?assignments_for_event, "existing assignments for event");
|
||||||
|
|
||||||
let assignments_with_function = assignments_for_event
|
let assignments_with_function = assignments_for_event
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|a| a.function == *value)
|
.filter(|a| a.function == *value)
|
||||||
@ -119,7 +122,7 @@ async fn event_has_free_slot_for_function(
|
|||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
assignments_with_function,
|
assignments_with_function,
|
||||||
"existing assignments for function"
|
"amount of existing assignments for function"
|
||||||
);
|
);
|
||||||
|
|
||||||
if match *value {
|
if match *value {
|
||||||
@ -130,7 +133,7 @@ async fn event_has_free_slot_for_function(
|
|||||||
Function::Wachhabender => event.voluntary_wachhabender && assignments_with_function >= 1,
|
Function::Wachhabender => event.voluntary_wachhabender && assignments_with_function >= 1,
|
||||||
} {
|
} {
|
||||||
return Err(AsyncValidateError::new(
|
return Err(AsyncValidateError::new(
|
||||||
"event already has enough assignments for this function",
|
"Veranstaltung hat bereits genug Zuweisungen für diese Funktion.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,7 +149,7 @@ async fn availability_not_already_assigned(
|
|||||||
let list: Vec<Assignment> = Assignment::read_all_by_availability(pool, availability.id)
|
let list: Vec<Assignment> = Assignment::read_all_by_availability(pool, availability.id)
|
||||||
.await?
|
.await?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|a| a.availability_id != availability.id && a.event_id != event.id)
|
.filter(|a| a.event_id != event.id)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let has_start_time_during_assignment = |a: &Assignment| a.start >= time.0 && a.start <= time.1;
|
let has_start_time_during_assignment = |a: &Assignment| a.start >= time.0 && a.start <= time.1;
|
||||||
@ -157,7 +160,7 @@ async fn availability_not_already_assigned(
|
|||||||
.any(|a| has_start_time_during_assignment(a) || has_end_time_during_assignment(a))
|
.any(|a| has_start_time_during_assignment(a) || has_end_time_during_assignment(a))
|
||||||
{
|
{
|
||||||
return Err(AsyncValidateError::new(
|
return Err(AsyncValidateError::new(
|
||||||
"availability is already assigned for that time",
|
"Die Verfügbarkeit des Nutzers wurde bereits zu einer anderen Veranstaltung zugewiesen.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,7 +176,9 @@ fn user_is_admin_or_area_manager_of_event_area(
|
|||||||
user.role == Role::AreaManager && user.area_id == event.location.as_ref().unwrap().area_id;
|
user.role == Role::AreaManager && user.area_id == event.location.as_ref().unwrap().area_id;
|
||||||
|
|
||||||
if !user_is_admin && !user_is_area_manager_event_area {
|
if !user_is_admin && !user_is_area_manager_event_area {
|
||||||
return Err(AsyncValidateError::new("TODO: admin or areamanager"));
|
return Err(AsyncValidateError::new(
|
||||||
|
"Du verfügst nicht über die Berechtigung, Zuweisungen zu Veranstaltungen vorzunehmen.",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -85,7 +85,6 @@ mod tests {
|
|||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use fake::{Fake, Faker};
|
use fake::{Fake, Faker};
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use tracing::info;
|
|
||||||
|
|
||||||
use crate::utils::test_helper::{
|
use crate::utils::test_helper::{
|
||||||
assert_snapshot, test_post, DbTestContext, NaiveDateTimeExt, RequestConfig,
|
assert_snapshot, test_post, DbTestContext, NaiveDateTimeExt, RequestConfig,
|
||||||
@ -248,10 +247,7 @@ mod tests {
|
|||||||
assert_eq!(StatusCode::UNPROCESSABLE_ENTITY, response.status());
|
assert_eq!(StatusCode::UNPROCESSABLE_ENTITY, response.status());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[db_test]
|
// fn fails_when_end_time_lies_before_start_time(context: &DbTestContext) -> not possible as event has the same constraint
|
||||||
fn fails_when_end_time_lies_before_start_time(context: &DbTestContext) {
|
|
||||||
assert!(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[db_test]
|
#[db_test]
|
||||||
fn fails_when_availability_time_already_assigned(context: &DbTestContext) {
|
fn fails_when_availability_time_already_assigned(context: &DbTestContext) {
|
||||||
@ -262,6 +258,7 @@ mod tests {
|
|||||||
|
|
||||||
arrange(&context.db_pool).await;
|
arrange(&context.db_pool).await;
|
||||||
arrange_event(&context.db_pool, start, end, 1).await;
|
arrange_event(&context.db_pool, start, end, 1).await;
|
||||||
|
arrange_event(&context.db_pool, start, end, 1).await;
|
||||||
arrange_availability(&context.db_pool, start, end).await;
|
arrange_availability(&context.db_pool, start, end).await;
|
||||||
|
|
||||||
let assignment_changeset = AssignmentChangeset {
|
let assignment_changeset = AssignmentChangeset {
|
||||||
@ -273,7 +270,7 @@ mod tests {
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let config = RequestConfig::new("/assignments/new?availability=1&function=1&event=1")
|
let config = RequestConfig::new("/assignments/new?availability=1&function=1&event=2")
|
||||||
.with_role(Role::Admin);
|
.with_role(Role::Admin);
|
||||||
|
|
||||||
let response = test_post::<_, _, String>(&context.db_pool, app, &config, None).await;
|
let response = test_post::<_, _, String>(&context.db_pool, app, &config, None).await;
|
||||||
@ -303,7 +300,6 @@ mod tests {
|
|||||||
#[db_test]
|
#[db_test]
|
||||||
fn fails_when_event_already_has_enough_assignments_for_function(context: &DbTestContext) {
|
fn fails_when_event_already_has_enough_assignments_for_function(context: &DbTestContext) {
|
||||||
let app = context.app().await;
|
let app = context.app().await;
|
||||||
info!("Hallo welt");
|
|
||||||
|
|
||||||
let start = NaiveDateTime::from_ymd_and_hms(2025, 01, 10, 10, 0, 0).unwrap();
|
let start = NaiveDateTime::from_ymd_and_hms(2025, 01, 10, 10, 0, 0).unwrap();
|
||||||
let end = NaiveDateTime::from_ymd_and_hms(2025, 01, 10, 20, 0, 0).unwrap();
|
let end = NaiveDateTime::from_ymd_and_hms(2025, 01, 10, 20, 0, 0).unwrap();
|
||||||
|
@ -88,6 +88,7 @@ mod tests {
|
|||||||
faker::{internet::en::SafeEmail, name::en::Name},
|
faker::{internet::en::SafeEmail, name::en::Name},
|
||||||
Fake, Faker,
|
Fake, Faker,
|
||||||
};
|
};
|
||||||
|
use tracing::debug;
|
||||||
|
|
||||||
#[db_test]
|
#[db_test]
|
||||||
async fn works_when_user_is_admin(context: &DbTestContext) {
|
async fn works_when_user_is_admin(context: &DbTestContext) {
|
||||||
@ -172,11 +173,14 @@ mod tests {
|
|||||||
is_posten: None,
|
is_posten: None,
|
||||||
is_wachhabender: None,
|
is_wachhabender: None,
|
||||||
is_fuehrungsassistent: Some(true),
|
is_fuehrungsassistent: Some(true),
|
||||||
area: Some(2),
|
area: Some(1),
|
||||||
};
|
};
|
||||||
|
|
||||||
let response = test_post(&context.db_pool, app, &config, Some(form)).await;
|
let response = test_post(&context.db_pool, app, &config, Some(form)).await;
|
||||||
assert_eq!(StatusCode::FOUND, response.status());
|
let (status, body) = response.into_status_and_body().await;
|
||||||
|
debug!(body);
|
||||||
|
|
||||||
|
assert_eq!(StatusCode::FOUND, status);
|
||||||
|
|
||||||
let updated_user = User::read_by_id(&context.db_pool, 1)
|
let updated_user = User::read_by_id(&context.db_pool, 1)
|
||||||
.await
|
.await
|
||||||
|
@ -41,7 +41,7 @@ mod tests {
|
|||||||
use crate::utils::test_helper::{
|
use crate::utils::test_helper::{
|
||||||
assert_snapshot, read_body, test_get, DbTestContext, RequestConfig, StatusCode,
|
assert_snapshot, read_body, test_get, DbTestContext, RequestConfig, StatusCode,
|
||||||
};
|
};
|
||||||
use brass_db::models::{Role, User, Vehicle};
|
use brass_db::models::{Role, Vehicle};
|
||||||
use brass_macros::db_test;
|
use brass_macros::db_test;
|
||||||
|
|
||||||
#[db_test]
|
#[db_test]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user