forked from Ruderverein-Donau-Linz/rowt
		
	
		
			
				
	
	
		
			349 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			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!");
 | 
						|
    }
 | 
						|
}
 |