349 lines
11 KiB
Rust

use rocket::{
form::Form,
get, post,
response::{Flash, Redirect},
routes, FromForm, Route, State,
};
use sqlx::SqlitePool;
use crate::model::{
log::Log,
planned_event::PlannedEvent,
trip::{CoxHelpError, Trip, TripDeleteError, TripUpdateError},
tripdetails::TripDetails,
user::CoxUser,
};
#[derive(FromForm)]
struct AddTripForm<'r> {
day: String,
//TODO: properly parse `planned_starting_time`
planned_starting_time: &'r str,
#[field(validate = range(1..))]
max_people: i32,
notes: Option<&'r str>,
trip_type: Option<i64>,
allow_guests: bool,
always_show: bool,
}
#[post("/trip", data = "<data>")]
async fn create(
db: &State<SqlitePool>,
data: Form<AddTripForm<'_>>,
cox: CoxUser,
) -> Flash<Redirect> {
let trip_details_id = TripDetails::create(
db,
data.planned_starting_time,
data.max_people,
&data.day,
data.notes,
data.allow_guests,
data.trip_type,
data.always_show,
)
.await;
let trip_details = TripDetails::find_by_id(db, trip_details_id).await.unwrap(); //Okay, bc just
//created
Trip::new_own(db, &cox, trip_details).await;
Log::create(
db,
format!(
"Cox {} created trip on {} @ {} for {} rower",
cox.name, data.day, data.planned_starting_time, data.max_people,
),
)
.await;
Flash::success(Redirect::to("/"), "Ausfahrt erfolgreich erstellt.")
}
#[derive(FromForm)]
struct EditTripForm<'r> {
max_people: i32,
notes: Option<&'r str>,
trip_type: Option<i64>,
always_show: bool,
}
#[post("/trip/<trip_id>", data = "<data>")]
async fn update(
db: &State<SqlitePool>,
data: Form<EditTripForm<'_>>,
trip_id: i64,
cox: CoxUser,
) -> Flash<Redirect> {
if let Some(trip) = Trip::find_by_id(db, trip_id).await {
match Trip::update_own(
db,
&cox,
&trip,
data.max_people,
data.notes,
data.trip_type,
data.always_show,
)
.await
{
Ok(_) => Flash::success(Redirect::to("/"), "Ausfahrt erfolgreich aktualisiert."),
Err(TripUpdateError::NotYourTrip) => {
Flash::error(Redirect::to("/"), "Nicht deine Ausfahrt!")
}
Err(TripUpdateError::TripDetailsDoesNotExist) => {
Flash::error(Redirect::to("/"), "Ausfahrt gibt's nicht")
}
}
} else {
Flash::error(Redirect::to("/"), "Ausfahrt gibt's nicht")
}
}
#[get("/join/<planned_event_id>")]
async fn join(db: &State<SqlitePool>, planned_event_id: i64, cox: CoxUser) -> Flash<Redirect> {
if let Some(planned_event) = PlannedEvent::find_by_id(db, planned_event_id).await {
match Trip::new_join(db, &cox, &planned_event).await {
Ok(_) => {
Log::create(
db,
format!(
"Cox {} helps at planned_event.id={}",
cox.name, planned_event_id,
),
)
.await;
Flash::success(Redirect::to("/"), "Danke für's helfen!")
}
Err(CoxHelpError::AlreadyRegisteredAsCox) => {
Flash::error(Redirect::to("/"), "Du hilfst bereits aus!")
}
Err(CoxHelpError::AlreadyRegisteredAsRower) => Flash::error(
Redirect::to("/"),
"Du hast dich bereits als Ruderer angemeldet!",
),
}
} else {
Flash::error(Redirect::to("/"), "Event gibt's nicht")
}
}
#[get("/remove/trip/<trip_id>")]
async fn remove_trip(db: &State<SqlitePool>, trip_id: i64, cox: CoxUser) -> Flash<Redirect> {
let trip = Trip::find_by_id(db, trip_id).await;
match trip {
None => Flash::error(Redirect::to("/"), "Trip gibt's nicht!"),
Some(trip) => match trip.delete(db, &cox).await {
Ok(_) => {
Log::create(db, format!("Cox {} deleted trip.id={}", cox.name, trip_id)).await;
Flash::success(Redirect::to("/"), "Erfolgreich gelöscht!")
}
Err(TripDeleteError::SomebodyAlreadyRegistered) => Flash::error(
Redirect::to("/"),
"Ausfahrt kann nicht gelöscht werden, da bereits jemand registriert ist!",
),
Err(TripDeleteError::NotYourTrip) => {
Flash::error(Redirect::to("/"), "Nicht deine Ausfahrt!")
}
},
}
}
#[get("/remove/<planned_event_id>")]
async fn remove(db: &State<SqlitePool>, planned_event_id: i64, cox: CoxUser) -> Flash<Redirect> {
if let Some(planned_event) = PlannedEvent::find_by_id(db, planned_event_id).await {
Trip::delete_by_planned_event(db, &cox, &planned_event).await;
Log::create(
db,
format!(
"Cox {} deleted registration for planned_event.id={}",
cox.name, planned_event_id
),
)
.await;
Flash::success(Redirect::to("/"), "Erfolgreich abgemeldet!")
} else {
Flash::error(Redirect::to("/"), "Planned_event does not exist.")
}
}
pub fn routes() -> Vec<Route> {
routes![create, join, remove, remove_trip, update]
}
#[cfg(test)]
mod test {
use chrono::NaiveDate;
use rocket::{
http::{ContentType, Status},
local::asynchronous::Client,
};
use sqlx::SqlitePool;
use crate::{model::trip::Trip, testdb};
#[sqlx::test]
fn test_trip_create() {
let db = testdb!();
assert_eq!(
0,
Trip::get_for_day(&db, NaiveDate::from_ymd_opt(2999, 12, 30).unwrap())
.await
.len()
);
let rocket = rocket::build().manage(db.clone());
let rocket = crate::tera::config(rocket);
let client = Client::tracked(rocket).await.unwrap();
let login = client
.post("/auth")
.header(ContentType::Form) // Set the content type to form
.body("name=cox&password=cox"); // Add the form data to the request body;
login.dispatch().await;
let req = client
.post("/cox/trip")
.header(ContentType::Form)
.body("day=2999-12-30&planned_starting_time=12:34&max_people=42&allow_guests=false");
let response = req.dispatch().await;
assert_eq!(response.status(), Status::SeeOther);
assert_eq!(response.headers().get("Location").next(), Some("/"));
let flash_cookie = response
.cookies()
.get("_flash")
.expect("Expected flash cookie");
assert_eq!(
flash_cookie.value(),
"7:successAusfahrt erfolgreich erstellt."
);
assert_eq!(
1,
Trip::get_for_day(&db, NaiveDate::from_ymd_opt(2999, 12, 30).unwrap())
.await
.len()
);
}
#[sqlx::test]
fn test_trip_update_succ() {
let db = testdb!();
let trip = &Trip::get_for_day(&db, NaiveDate::from_ymd_opt(1970, 01, 02).unwrap()).await[0];
assert_eq!(1, trip.trip.max_people);
assert_eq!(
"trip_details for trip from cox",
&trip.trip.notes.clone().unwrap()
);
let rocket = rocket::build().manage(db.clone());
let rocket = crate::tera::config(rocket);
let client = Client::tracked(rocket).await.unwrap();
let login = client
.post("/auth")
.header(ContentType::Form) // Set the content type to form
.body("name=cox&password=cox"); // Add the form data to the request body;
login.dispatch().await;
let req = client
.post("/cox/trip/1")
.header(ContentType::Form)
.body("notes=my-new-notes&max_people=12");
let response = req.dispatch().await;
assert_eq!(response.status(), Status::SeeOther);
assert_eq!(response.headers().get("Location").next(), Some("/"));
let flash_cookie = response
.cookies()
.get("_flash")
.expect("Expected flash cookie");
assert_eq!(
flash_cookie.value(),
"7:successAusfahrt erfolgreich aktualisiert."
);
let trip = &Trip::get_for_day(&db, NaiveDate::from_ymd_opt(1970, 01, 02).unwrap()).await[0];
assert_eq!(12, trip.trip.max_people);
assert_eq!("my-new-notes", &trip.trip.notes.clone().unwrap());
}
#[sqlx::test]
fn test_trip_update_wrong_event() {
let db = testdb!();
let rocket = rocket::build().manage(db.clone());
let rocket = crate::tera::config(rocket);
let client = Client::tracked(rocket).await.unwrap();
let login = client
.post("/auth")
.header(ContentType::Form) // Set the content type to form
.body("name=cox&password=cox"); // Add the form data to the request body;
login.dispatch().await;
let req = client
.post("/cox/trip/9999")
.header(ContentType::Form)
.body("notes=my-new-notes&max_people=12");
let response = req.dispatch().await;
assert_eq!(response.status(), Status::SeeOther);
assert_eq!(response.headers().get("Location").next(), Some("/"));
let flash_cookie = response
.cookies()
.get("_flash")
.expect("Expected flash cookie");
assert_eq!(flash_cookie.value(), "5:errorAusfahrt gibt's nicht");
}
#[sqlx::test]
fn test_trip_update_wrong_cox() {
let db = testdb!();
let trip = &Trip::get_for_day(&db, NaiveDate::from_ymd_opt(1970, 01, 02).unwrap()).await[0];
assert_eq!(1, trip.trip.max_people);
assert_eq!(
"trip_details for trip from cox",
&trip.trip.notes.clone().unwrap()
);
let rocket = rocket::build().manage(db.clone());
let rocket = crate::tera::config(rocket);
let client = Client::tracked(rocket).await.unwrap();
let login = client
.post("/auth")
.header(ContentType::Form) // Set the content type to form
.body("name=cox2&password=cox"); // Add the form data to the request body;
login.dispatch().await;
let req = client
.post("/cox/trip/1")
.header(ContentType::Form)
.body("notes=my-new-notes&max_people=12");
let response = req.dispatch().await;
assert_eq!(response.status(), Status::SeeOther);
assert_eq!(response.headers().get("Location").next(), Some("/"));
let flash_cookie = response
.cookies()
.get("_flash")
.expect("Expected flash cookie");
assert_eq!(flash_cookie.value(), "5:errorNicht deine Ausfahrt!");
}
}