Add update_person route
This commit is contained in:
parent
d2a94b078c
commit
cd18427d1b
|
|
@ -80,7 +80,7 @@ impl Adaptor for SqlAdaptor {
|
|||
};
|
||||
|
||||
Ok(
|
||||
match person::Entity::find_by_id((event_id, person.name))
|
||||
match person::Entity::find_by_id((person.name, event_id))
|
||||
.one(&self.db)
|
||||
.await?
|
||||
{
|
||||
|
|
@ -156,7 +156,16 @@ impl SqlAdaptor {
|
|||
|
||||
// Connect to the database
|
||||
let db = Database::connect(&connection_string).await.unwrap();
|
||||
println!("Connected to database at {}", connection_string);
|
||||
println!(
|
||||
"{} Connected to database at {}",
|
||||
match db {
|
||||
DatabaseConnection::SqlxMySqlPoolConnection(_) => "🐬",
|
||||
DatabaseConnection::SqlxPostgresPoolConnection(_) => "🐘",
|
||||
DatabaseConnection::SqlxSqlitePoolConnection(_) => "🪶",
|
||||
DatabaseConnection::Disconnected => panic!("Failed to connect"),
|
||||
},
|
||||
connection_string
|
||||
);
|
||||
|
||||
// Setup tables
|
||||
Migrator::up(&db, None).await.unwrap();
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::{net::SocketAddr, sync::Arc};
|
|||
|
||||
use axum::{
|
||||
extract,
|
||||
routing::{get, post},
|
||||
routing::{get, patch, post},
|
||||
Router, Server,
|
||||
};
|
||||
use routes::*;
|
||||
|
|
@ -43,11 +43,15 @@ async fn main() {
|
|||
.route("/event/:event_id", get(get_event))
|
||||
.route("/event/:event_id/people", get(get_people))
|
||||
.route("/event/:event_id/people/:person_name", get(get_person))
|
||||
.route("/event/:event_id/people/:person_name", patch(update_person))
|
||||
.with_state(shared_state);
|
||||
|
||||
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
|
||||
|
||||
println!("Crab Fit API listening at http://{} in {} mode", addr, MODE);
|
||||
println!(
|
||||
"🦀 Crab Fit API listening at http://{} in {} mode",
|
||||
addr, MODE
|
||||
);
|
||||
Server::bind(&addr)
|
||||
.serve(app.into_make_service())
|
||||
.await
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use axum::Json;
|
||||
use common::{event::Event, person::Person};
|
||||
use common::{event::Event, person::Person, stats::Stats};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::errors::ApiError;
|
||||
|
|
@ -19,7 +19,7 @@ pub struct EventResponse {
|
|||
pub name: String,
|
||||
pub times: Vec<String>,
|
||||
pub timezone: String,
|
||||
pub created: i64,
|
||||
pub created_at: i64,
|
||||
}
|
||||
|
||||
impl From<Event> for EventResponse {
|
||||
|
|
@ -29,24 +29,33 @@ impl From<Event> for EventResponse {
|
|||
name: value.name,
|
||||
times: value.times,
|
||||
timezone: value.timezone,
|
||||
created: value.created_at.timestamp(),
|
||||
created_at: value.created_at.timestamp(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all(serialize = "camelCase"))]
|
||||
pub struct StatsResponse {
|
||||
pub event_count: i32,
|
||||
pub person_count: i32,
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
impl From<Stats> for StatsResponse {
|
||||
fn from(value: Stats) -> Self {
|
||||
Self {
|
||||
event_count: value.event_count,
|
||||
person_count: value.person_count,
|
||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct PersonResponse {
|
||||
pub name: String,
|
||||
pub availability: Vec<String>,
|
||||
pub created: i64,
|
||||
pub created_at: i64,
|
||||
}
|
||||
|
||||
impl From<Person> for PersonResponse {
|
||||
|
|
@ -54,12 +63,18 @@ impl From<Person> for PersonResponse {
|
|||
Self {
|
||||
name: value.name,
|
||||
availability: value.availability,
|
||||
created: value.created_at.timestamp(),
|
||||
created_at: value.created_at.timestamp(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct PersonInput {
|
||||
pub struct GetPersonInput {
|
||||
pub password: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct UpdatePersonInput {
|
||||
pub password: Option<String>,
|
||||
pub availability: Vec<String>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,13 +22,7 @@ pub async fn get_event<A: Adaptor>(
|
|||
.map_err(ApiError::AdaptorError)?;
|
||||
|
||||
match event {
|
||||
Some(event) => Ok(Json(EventResponse {
|
||||
id: event.id,
|
||||
name: event.name,
|
||||
times: event.times,
|
||||
timezone: event.timezone,
|
||||
created: event.created_at.timestamp(),
|
||||
})),
|
||||
Some(event) => Ok(Json(event.into())),
|
||||
None => Err(ApiError::NotFound),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,14 +6,14 @@ use common::{adaptor::Adaptor, person::Person};
|
|||
|
||||
use crate::{
|
||||
errors::ApiError,
|
||||
payloads::{ApiResult, PersonInput, PersonResponse},
|
||||
payloads::{ApiResult, GetPersonInput, PersonResponse},
|
||||
State,
|
||||
};
|
||||
|
||||
pub async fn get_person<A: Adaptor>(
|
||||
extract::State(state): State<A>,
|
||||
Path((event_id, person_name)): Path<(String, String)>,
|
||||
input: Option<Json<PersonInput>>,
|
||||
input: Option<Json<GetPersonInput>>,
|
||||
) -> ApiResult<PersonResponse, A> {
|
||||
let adaptor = &state.lock().await.adaptor;
|
||||
|
||||
|
|
@ -77,7 +77,7 @@ pub async fn get_person<A: Adaptor>(
|
|||
}
|
||||
}
|
||||
|
||||
fn verify_password(person: &Person, raw: Option<String>) -> bool {
|
||||
pub fn verify_password(person: &Person, raw: Option<String>) -> bool {
|
||||
match &person.password_hash {
|
||||
Some(hash) => bcrypt::verify(raw.unwrap_or(String::from("")), hash).unwrap_or(false),
|
||||
// Specifically allow a user who doesn't have a password
|
||||
|
|
|
|||
|
|
@ -12,9 +12,5 @@ pub async fn get_stats<A: Adaptor>(extract::State(state): State<A>) -> ApiResult
|
|||
|
||||
let stats = adaptor.get_stats().await.map_err(ApiError::AdaptorError)?;
|
||||
|
||||
Ok(Json(StatsResponse {
|
||||
event_count: stats.event_count,
|
||||
person_count: stats.person_count,
|
||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||
}))
|
||||
Ok(Json(stats.into()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,3 +12,6 @@ pub use get_people::get_people;
|
|||
|
||||
mod get_person;
|
||||
pub use get_person::get_person;
|
||||
|
||||
mod update_person;
|
||||
pub use update_person::update_person;
|
||||
|
|
|
|||
59
backend/src/routes/update_person.rs
Normal file
59
backend/src/routes/update_person.rs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
use axum::{
|
||||
extract::{self, Path},
|
||||
Json,
|
||||
};
|
||||
use common::{adaptor::Adaptor, person::Person};
|
||||
|
||||
use crate::{
|
||||
errors::ApiError,
|
||||
payloads::{ApiResult, PersonResponse, UpdatePersonInput},
|
||||
State,
|
||||
};
|
||||
|
||||
use super::get_person::verify_password;
|
||||
|
||||
pub async fn update_person<A: Adaptor>(
|
||||
extract::State(state): State<A>,
|
||||
Path((event_id, person_name)): Path<(String, String)>,
|
||||
Json(input): Json<UpdatePersonInput>,
|
||||
) -> ApiResult<PersonResponse, A> {
|
||||
let adaptor = &state.lock().await.adaptor;
|
||||
|
||||
let existing_people = adaptor
|
||||
.get_people(event_id.clone())
|
||||
.await
|
||||
.map_err(ApiError::AdaptorError)?;
|
||||
|
||||
// Event not found
|
||||
if existing_people.is_none() {
|
||||
return Err(ApiError::NotFound);
|
||||
}
|
||||
|
||||
// Check if the user exists
|
||||
let existing_person = existing_people
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.find(|p| p.name == person_name)
|
||||
.ok_or(ApiError::NotFound)?;
|
||||
|
||||
// Verify password (if set)
|
||||
if !verify_password(&existing_person, input.password) {
|
||||
return Err(ApiError::NotAuthorized);
|
||||
}
|
||||
|
||||
Ok(Json(
|
||||
adaptor
|
||||
.upsert_person(
|
||||
event_id,
|
||||
Person {
|
||||
name: existing_person.name,
|
||||
password_hash: existing_person.password_hash,
|
||||
created_at: existing_person.created_at,
|
||||
availability: input.availability,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.map_err(ApiError::AdaptorError)?
|
||||
.into(),
|
||||
))
|
||||
}
|
||||
Loading…
Reference in a new issue