From 6a3e8508a76ec86ed0387824188ba48026b083ee Mon Sep 17 00:00:00 2001 From: Mitchell Marino Date: Fri, 14 Apr 2023 22:17:16 -0500 Subject: [PATCH] added attending endpoints, added some events endpoints --- src/attending.rs | 150 +++++++++++++++++++++++++++++++++++++++++++++++ src/events.rs | 8 +-- src/main.rs | 11 ++-- src/models.rs | 16 ++++- src/user.rs | 6 +- 5 files changed, 179 insertions(+), 12 deletions(-) create mode 100644 src/attending.rs diff --git a/src/attending.rs b/src/attending.rs new file mode 100644 index 0000000..3705caf --- /dev/null +++ b/src/attending.rs @@ -0,0 +1,150 @@ +use axum::{extract::State, http::StatusCode, response::IntoResponse, Json}; +use axum_auth::AuthBearer; +use serde_json::json; +use sqlx::query; + +use crate::{ + jwt::handle_token, + models::{ConfirmAttending, MarkAttending, Role}, + AppState, +}; + +pub async fn confirm_attending( + AuthBearer(token): AuthBearer, + State(app_state): State, + Json(body): Json, +) -> impl IntoResponse { + let token_data = match handle_token(token, &app_state, Role::Teacher) { + Ok(token_data) => token_data, + Err(err) => return err, + }; + + let result = query!( + r#" + UPDATE event_attendees + SET + confirmed = $1 + WHERE event_id = $2 + AND user_id = $3 + AND event_id IN (SELECT id FROM events WHERE created_by = $4); + "#, + body.new_confirmed, + body.event_id, + body.user_id, + token_data.id, + ) + .execute(&app_state.db_pool) + .await; + + match result { + Ok(success) => { + if success.rows_affected() > 0 { + (StatusCode::OK, Json(json!({}))) + } else { + ( + StatusCode::NOT_FOUND, + Json(json!({"error": "no record matched the parameters."})), + ) + } + } + Err(err) => ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(json!({ + "error": format!("Unknown error confirming attendence: {:?}", err) + })), + ), + } + .into_response() +} + +pub async fn mark_attending( + AuthBearer(token): AuthBearer, + State(app_state): State, + Json(body): Json, +) -> impl IntoResponse { + let token_data = match handle_token(token, &app_state, Role::Student) { + Ok(token_data) => token_data, + Err(err) => return err, + }; + + let result = query!( + r#" + INSERT INTO event_attendees + (event_id, user_id) + VALUES + ($1, $2) + "#, + body.event_id, + token_data.id, + ) + .execute(&app_state.db_pool) + .await; + + match result { + Ok(_) => (StatusCode::OK, Json(json!({}))), + Err(err) => { + // See if we get an error for marking an already marked. + if let sqlx::Error::Database(database_err) = &err { + if let Some(err_code) = database_err.code() { + if err_code == "23505" { + return (StatusCode::OK, Json(json!({}))).into_response(); + } + } + } + + ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(json!({ + "error": format!("Unknown error marking attendance: {:?}", err) + })), + ) + } + } + .into_response() +} + +pub async fn unmark_attending( + AuthBearer(token): AuthBearer, + State(app_state): State, + Json(body): Json, +) -> impl IntoResponse { + let token_data = match handle_token(token, &app_state, Role::Student) { + Ok(token_data) => token_data, + Err(err) => return err, + }; + + let result = query!( + r#" + DELETE FROM event_attendees + WHERE + event_id = $1 AND + user_id = $2 + "#, + body.event_id, + token_data.id, + ) + .execute(&app_state.db_pool) + .await; + + match result { + Ok(_) => (StatusCode::OK, Json(json!({}))), + Err(err) => { + // See if we get an error for marking an already marked. + if let sqlx::Error::Database(database_err) = &err { + if let Some(err_code) = database_err.code() { + if err_code == "23505" { + return (StatusCode::OK, Json(json!({}))).into_response(); + } + } + } + + ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(json!({ + "error": format!("Unknown error unmarking attendance: {:?}", err) + })), + ) + } + } + .into_response() +} diff --git a/src/events.rs b/src/events.rs index 0ab1d63..39764a6 100644 --- a/src/events.rs +++ b/src/events.rs @@ -55,7 +55,7 @@ LIMIT Err(err) => ( StatusCode::INTERNAL_SERVER_ERROR, Json(json!({ - "error": format!("Unknown error getting events: {}", err) + "error": format!("Unknown error getting events: {:?}", err) })), ), } @@ -103,7 +103,7 @@ LIMIT Err(err) => ( StatusCode::INTERNAL_SERVER_ERROR, Json(json!({ - "error": format!("Unknown error getting events: {}", err) + "error": format!("Unknown error getting events: {:?}", err) })), ), } @@ -158,7 +158,7 @@ pub async fn get_event( ( StatusCode::INTERNAL_SERVER_ERROR, Json(json!({ - "error": format!("Unknown error getting event: {}", err) + "error": format!("Unknown error getting event: {:?}", err) })), ) } @@ -199,7 +199,7 @@ pub async fn create_event( Err(err) => ( StatusCode::BAD_REQUEST, Json(json!({ - "error": format!("Unknown error creating event: {}", err) + "error": format!("Unknown error creating event: {:?}", err) })), ) .into_response(), diff --git a/src/main.rs b/src/main.rs index 81dca1d..15ab68c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +mod attending; mod events; mod jwt; mod models; @@ -5,7 +6,7 @@ mod report; mod user; use axum::{ - routing::{get, post}, + routing::{delete, get, post, put}, Router, }; use jsonwebtoken::{DecodingKey, EncodingKey}; @@ -38,15 +39,17 @@ async fn main() { .await .expect("could not connect to database_url"); - // build our application with a route let app = Router::new() .route("/", get(root)) .route("/user/signup", post(signup)) .route("/user/signin", post(signin)) - .route("/event", post(events::create_event).get(events::get_event)) + .route("/event", get(events::get_event).post(events::create_event)) .route("/event/preview", get(events::get_events_preview)) - .route("/event/all", get(events::get_all_events)) + .route("/event/future", get(events::get_all_events)) .route("/report", get(report::get_report)) + .route("/attending/confirm", put(attending::confirm_attending)) + .route("/attending/mark", post(attending::mark_attending)) + .route("/attending/unmark", delete(attending::unmark_attending)) .with_state(AppState { db_pool, jwt_encode, diff --git a/src/models.rs b/src/models.rs index c115359..8dee6aa 100644 --- a/src/models.rs +++ b/src/models.rs @@ -69,7 +69,7 @@ pub struct Signup { #[derive( sqlx::Type, Copy, Clone, Hash, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, )] -// #[sqlx(type_name = "role", rename_all = "snake_case")] +#[sqlx(type_name = "role", rename_all = "snake_case")] pub enum Role { Student, Teacher, @@ -89,6 +89,20 @@ pub struct ReportQuery { pub grade: Option, } +/// The model for the confirm attending request. +#[derive(Copy, Clone, Hash, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct ConfirmAttending { + pub new_confirmed: bool, + pub event_id: i32, + pub user_id: i32, +} + +/// The model for the mark attending request. +#[derive(Copy, Clone, Hash, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct MarkAttending { + pub event_id: i32, +} + /// The claims for the JWT. #[derive(Clone, Serialize, Deserialize, Debug, Hash)] pub struct Claims { diff --git a/src/user.rs b/src/user.rs index 4a44292..4a05be0 100644 --- a/src/user.rs +++ b/src/user.rs @@ -49,7 +49,7 @@ pub async fn signup( ( StatusCode::INTERNAL_SERVER_ERROR, Json(json!({ - "error": format!("Unknown error signing up: {}", err) + "error": format!("Unknown error signing up: {:?}", err) })), ) } @@ -93,7 +93,7 @@ pub async fn signin( StatusCode::INTERNAL_SERVER_ERROR, Json(json!({ "error": - format!("Unknown error signing in when creating JWT: {}", err) + format!("Unknown error signing in when creating JWT: {:?}", err) })), ) } @@ -113,7 +113,7 @@ pub async fn signin( return ( StatusCode::INTERNAL_SERVER_ERROR, Json(json!({ - "error": format!("Unknown error signing in: {}", err) + "error": format!("Unknown error signing in: {:?}", err) })), ) }