updated events endpoints to include preview and create

This commit is contained in:
Mitchell Marino 2023-04-11 22:53:29 -05:00
parent a96867d1b9
commit 65425b5626
4 changed files with 153 additions and 20 deletions

View File

@ -8,7 +8,7 @@ edition = "2021"
[dependencies] [dependencies]
tokio = { version = "1.27", features = ["full"] } tokio = { version = "1.27", features = ["full"] }
axum = "0.6" 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 = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
tracing = "0.1" tracing = "0.1"

View File

@ -1,25 +1,104 @@
use axum::{ use axum::{extract::State, http::StatusCode, response::IntoResponse, Json};
extract::{Query, State},
http::StatusCode,
response::IntoResponse,
Json,
};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::json; use serde_json::json;
use sqlx::query; use sqlx::{
query, query_as,
types::{chrono::NaiveDateTime, BigDecimal},
};
use crate::AppState; 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)] #[derive(Copy, Clone, Hash, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct EventsPreviewQuery { pub struct EventsPreviewQuery {
count: u32, count: u32,
} }
pub async fn get_events_preview( #[derive(Clone, Deserialize, Debug)]
State(app_state): State<AppState>, pub struct NewEventRequestEntry {
Query(query): Query<EventsPreviewQuery>, title: String,
) -> impl IntoResponse { description: String,
let result = query!( #[serde(with = "serialize_datetime")]
time_start: NaiveDateTime,
#[serde(with = "serialize_datetime")]
time_end: NaiveDateTime,
event_type: EventType,
points: i32,
place: Option<String>,
#[serde(with = "serialize_big_decimal")]
price: BigDecimal,
created_by: Option<i32>,
}
#[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<String>,
#[serde(with = "serialize_big_decimal")]
price: BigDecimal,
created_by: Option<i32>,
}
mod serialize_datetime {
use std::str::FromStr;
use serde::{self, Deserialize, Deserializer, Serializer};
use sqlx::types::chrono::NaiveDateTime;
pub fn serialize<S>(dt: &NaiveDateTime, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&dt.to_string())
}
pub fn deserialize<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<NaiveDateTime, D::Error> {
let str = <String as Deserialize>::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<S>(bd: &BigDecimal, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&bd.to_string())
}
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<BigDecimal, D::Error> {
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<AppState>) -> impl IntoResponse {
let result = query_as!(
Event,
r#" r#"
SELECT SELECT
id, id,
@ -27,10 +106,9 @@ SELECT
description, description,
time_start, time_start,
time_end, time_end,
event_type, event_type AS "event_type!: EventType",
points, points,
place, place,
attending,
price, price,
created_by created_by
FROM FROM
@ -42,7 +120,49 @@ ORDER BY
LIMIT LIMIT
10; 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<AppState>,
Json(event_post_query): Json<NewEventRequestEntry>,
) -> 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(),
}
} }

View File

@ -40,7 +40,8 @@ async fn main() {
.route("/", get(root)) .route("/", get(root))
.route("/user/signup", post(signup)) .route("/user/signup", post(signup))
.route("/user/signin", post(signin)) .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 }); .with_state(AppState { db_pool, jwt_key });
// run our app with hyper // run our app with hyper

View File

@ -8,9 +8,20 @@ use crate::{jwt::Claims, AppState};
#[derive(Clone, Debug, Deserialize)] #[derive(Clone, Debug, Deserialize)]
pub struct Signup { pub struct Signup {
username: String, username: String,
role: Role,
password: String, 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)] #[derive(Clone, Debug, Deserialize)]
pub struct Signin { pub struct Signin {
username: String, username: String,
@ -32,11 +43,12 @@ pub async fn signup(
let result = sqlx::query!( let result = sqlx::query!(
r#" r#"
INSERT INTO users (username, password) INSERT INTO users (username, role, password)
VALUES ( $1, $2 ) VALUES ( $1, $2, $3 )
RETURNING id; RETURNING id;
"#, "#,
signup.username, signup.username,
signup.role as Role,
pass_hash.as_bytes(), pass_hash.as_bytes(),
) )
.fetch_one(&app_state.db_pool) .fetch_one(&app_state.db_pool)