From 65425b5626c58fa7603d3cc5702a22557113ba6e Mon Sep 17 00:00:00 2001 From: Mitchell Marino Date: Tue, 11 Apr 2023 22:53:29 -0500 Subject: [PATCH] updated events endpoints to include preview and create --- Cargo.toml | 2 +- src/events.rs | 152 ++++++++++++++++++++++++++++++++++++++++++++------ src/main.rs | 3 +- src/user.rs | 16 +++++- 4 files changed, 153 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 52cd3e9..f158bc3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" [dependencies] tokio = { version = "1.27", features = ["full"] } axum = "0.6" -sqlx = { version = "0.6", features = ["postgres", "runtime-tokio-rustls"] } +sqlx = { version = "0.6", features = ["postgres", "runtime-tokio-rustls", "all-types"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" tracing = "0.1" diff --git a/src/events.rs b/src/events.rs index a37adc8..c648d4e 100644 --- a/src/events.rs +++ b/src/events.rs @@ -1,25 +1,104 @@ -use axum::{ - extract::{Query, State}, - http::StatusCode, - response::IntoResponse, - Json, -}; +use axum::{extract::State, http::StatusCode, response::IntoResponse, Json}; use serde::{Deserialize, Serialize}; use serde_json::json; -use sqlx::query; +use sqlx::{ + query, query_as, + types::{chrono::NaiveDateTime, BigDecimal}, +}; use crate::AppState; +#[derive( + sqlx::Type, Copy, Clone, Hash, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, +)] +#[sqlx(type_name = "event_type", rename_all = "snake_case")] +enum EventType { + Sports, + Meetings, + Drama, + Music, + Other, +} + #[derive(Copy, Clone, Hash, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub struct EventsPreviewQuery { count: u32, } -pub async fn get_events_preview( - State(app_state): State, - Query(query): Query, -) -> impl IntoResponse { - let result = query!( +#[derive(Clone, Deserialize, Debug)] +pub struct NewEventRequestEntry { + title: String, + description: String, + #[serde(with = "serialize_datetime")] + time_start: NaiveDateTime, + #[serde(with = "serialize_datetime")] + time_end: NaiveDateTime, + event_type: EventType, + points: i32, + place: Option, + #[serde(with = "serialize_big_decimal")] + price: BigDecimal, + created_by: Option, +} + +#[derive(Clone, Serialize, Debug)] +pub struct Event { + id: i32, + title: String, + description: String, + #[serde(with = "serialize_datetime")] + time_start: NaiveDateTime, + #[serde(with = "serialize_datetime")] + time_end: NaiveDateTime, + event_type: EventType, + points: i32, + place: Option, + #[serde(with = "serialize_big_decimal")] + price: BigDecimal, + created_by: Option, +} + +mod serialize_datetime { + use std::str::FromStr; + + use serde::{self, Deserialize, Deserializer, Serializer}; + use sqlx::types::chrono::NaiveDateTime; + + pub fn serialize(dt: &NaiveDateTime, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&dt.to_string()) + } + + pub fn deserialize<'de, D: Deserializer<'de>>( + deserializer: D, + ) -> Result { + let str = ::deserialize(deserializer)?; + NaiveDateTime::from_str(&str).map_err(|err| serde::de::Error::custom(err)) + } +} + +mod serialize_big_decimal { + use serde::{self, Deserialize, Deserializer, Serializer}; + use sqlx::types::BigDecimal; + + pub fn serialize(bd: &BigDecimal, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&bd.to_string()) + } + + pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result { + let float = f32::deserialize(deserializer)?; + BigDecimal::try_from(float).map_err(|err| serde::de::Error::custom(err)) + } +} + +pub async fn get_events_preview(State(app_state): State) -> impl IntoResponse { + let result = query_as!( + Event, r#" SELECT id, @@ -27,10 +106,9 @@ SELECT description, time_start, time_end, - event_type, + event_type AS "event_type!: EventType", points, place, - attending, price, created_by FROM @@ -42,7 +120,49 @@ ORDER BY LIMIT 10; "# - ); + ) + .fetch_all(&app_state.db_pool) + .await; - (StatusCode::OK, Json(json!({}))) + match result { + Ok(events) => (StatusCode::OK, Json(json!(events))), + Err(err) => ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(json!({ + "error": format!("Unknown error getting events: {}", err) + })), + ), + } +} + +pub async fn create_event( + State(app_state): State, + Json(event_post_query): Json, +) -> impl IntoResponse { + let result = query!( + r#" + INSERT INTO events (title, description, time_start, time_end, event_type, points, place, price, created_by) + VALUES ( $1, $2, $3, $4, $5, $6, $7, $8, $9 ) + "#, + event_post_query.title, + event_post_query.description, + event_post_query.time_start, + event_post_query.time_end, + event_post_query.event_type as EventType, + event_post_query.points, + event_post_query.place, + event_post_query.price, + event_post_query.created_by, + ).execute(&app_state.db_pool).await; + + match result { + Ok(_) => (StatusCode::OK, "").into_response(), + Err(err) => ( + StatusCode::BAD_REQUEST, + Json(json!({ + "error": format!("Unknown error creating event: {}", err) + })), + ) + .into_response(), + } } diff --git a/src/main.rs b/src/main.rs index 877001c..7ab5c0b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -40,7 +40,8 @@ async fn main() { .route("/", get(root)) .route("/user/signup", post(signup)) .route("/user/signin", post(signin)) - .route("/event/prview", get(events::get_events_preview)) + .route("/event", post(events::create_event)) + .route("/event/preview", get(events::get_events_preview)) .with_state(AppState { db_pool, jwt_key }); // run our app with hyper diff --git a/src/user.rs b/src/user.rs index a133ab6..280d894 100644 --- a/src/user.rs +++ b/src/user.rs @@ -8,9 +8,20 @@ use crate::{jwt::Claims, AppState}; #[derive(Clone, Debug, Deserialize)] pub struct Signup { username: String, + role: Role, password: String, } +#[derive( + sqlx::Type, Copy, Clone, Hash, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, +)] +#[sqlx(type_name = "role", rename_all = "snake_case")] +pub enum Role { + Student, + Teacher, + Admin, +} + #[derive(Clone, Debug, Deserialize)] pub struct Signin { username: String, @@ -32,11 +43,12 @@ pub async fn signup( let result = sqlx::query!( r#" - INSERT INTO users (username, password) - VALUES ( $1, $2 ) + INSERT INTO users (username, role, password) + VALUES ( $1, $2, $3 ) RETURNING id; "#, signup.username, + signup.role as Role, pass_hash.as_bytes(), ) .fetch_one(&app_state.db_pool)