added result return types to invites module functions

this lets us cut down on worrying about mysql errors
Mysql error responsibility is now the route dispatchers problem
Route dispatcher itself can easily dispatch to another handler
This commit is contained in:
shockrah 2020-06-02 17:05:14 -07:00
parent 20f6273ad7
commit 2400b89b12
2 changed files with 49 additions and 40 deletions

View File

@ -1,7 +1,7 @@
use std::collections::HashMap;
use mysql_async;
use mysql_async::Conn;
use mysql_async::{Conn, Pool};
use mysql_async::error::Error;
use mysql_async::prelude::{params, Queryable};
@ -14,12 +14,17 @@ struct InviteRow {
expires: u64,
uses: i32,
}
/*
* Error handling:
* All errors raisable from this module come from mysql_async and thus
* are of the enum mysql_async::error::Error
*/
impl InviteRow {
pub fn new() -> InviteRow {
let dt = Utc::now() + chrono::Duration::minutes(30);
// TODO:[maybe] ensure no collisions by doing a quick database check here
let mut invite = InviteRow {
let invite = InviteRow {
id: random::<u64>(), // hopefully there won't ever be collision with this size of pool
uses: 1, // default/hardcorded for now
expires: dt.timestamp() as u64
@ -47,46 +52,50 @@ impl InviteRow {
}
}
async fn get_invite_by_code(conn: &Conn, value: Option<&str>) -> Option<InviteRow> {
async fn get_invite_by_code(pool: &Pool, value: Option<&str>) -> Result<Option<InviteRow>, Error> {
if let Some(val) = value {
let db_row_result: Result<(Conn, Option<(u64, u64, i32)>), Error> = conn
.first_exec(r"SELECT * FROM", mysql_async::params!{"code"=>})
.await;
match db_row_result {
Ok(data) => {
if let Some(tup) = data.1 {Some(InviteRow::from_tuple(tup))}
else {None}
}
Err(_) => None,
let conn = pool.get_conn().await?;
let db_row_result: (Conn, Option<(u64, u64, i32)>) = conn
.first_exec(r"SELECT * FROM", mysql_async::params!{"code"=>val})
.await?;
if let Some(tup) = db_row_result.1 {
Ok(Some(InviteRow::from_tuple(tup)))
}
else {
// basically nothing was found but nothing bad happened
Ok(None)
}
}
else {
None
}
// again db didn't throw a fit but we don't have a good input
else {Ok(None)}
}
async fn record_invite_usage(conn: &Conn, data: &InviteRow) {
async fn record_invite_usage(pool: &Pool, data: &InviteRow) -> Result<(), Error>{
/*
* By this this is called we really don't care about what happens as we've
* already been querying the db and the likely hood of this seriously failing
* is low enough to write a wall of text and not a wall of error handling code
*/
let conn = pool.get_conn().await?;
let _db_result = conn
.prep_exec(r"UPDATE invites SET uses = :uses WHERE id = :id", mysql_async::params!{
"uses" => data.uses - 1,
"id" => data.id
}).await;
}).await?;
Ok(())
}
pub async fn join_invite_code(conn: &Conn, response: &mut Response<Body>, params: &HashMap<&str, &str>) {
pub async fn join_invite_code(pool: &Pool, response: &mut Response<Body>, params: &HashMap<&str, &str>) -> Result<(), Error> {
// First check that the code is there
match params.get("code") {
Some(p) => {
if let Some(row) = get_invite_by_code(conn, Some(*p)).await {
if let Some(row) = get_invite_by_code(pool, Some(*p)).await? {
// since we have a row make sure the invite is valid
let now = Utc::now().timestamp() as u64;
// usable and expires in the future
if row.uses > 0 && row.expires > now {
record_invite_usage(conn, &row).await;
record_invite_usage(pool, &row).await?;
// TODO: assign some actual data to the body
*response.status_mut() = StatusCode::OK;
}
@ -96,23 +105,20 @@ pub async fn join_invite_code(conn: &Conn, response: &mut Response<Body>, params
*response.status_mut() = StatusCode::BAD_REQUEST;
}
}
Ok(())
}
pub async fn create_invite(conn: &Conn, response: &mut Response<Body>) {
pub async fn create_invite(pool: &Pool, response: &mut Response<Body>) -> Result<(), Error> {
let invite = InviteRow::new();
let db_result = conn
.prep_exec(r"INSERT INTO invites (id, expires, uses) VALUES (:id, :expires, :uses",
let conn = pool.get_conn().await?;
conn.prep_exec(r"INSERT INTO invites (id, expires, uses) VALUES (:id, :expires, :uses",
mysql_async::params!{
"id" => invite.id,
"expires" => invite.expires,
"uses" => invite.uses
}).await;
"uses" => invite.uses,
}).await?;
match db_result {
Ok(d) => {
*response.body_mut() = Body::from(invite.as_json_str());
*response.status_mut() = StatusCode::OK;
}
Err(e) => {}
}
*response.body_mut() = Body::from(invite.as_json_str());
*response.status_mut() = StatusCode::OK;
Ok(())
}

View File

@ -16,7 +16,7 @@ use hyper::{
Method, StatusCode,
service::{make_service_fn, service_fn}
};
use mysql_async::Conn;
use mysql_async::Pool;
use dotenv::dotenv;
mod auth;
@ -43,9 +43,13 @@ fn map_qs(query_string_raw: Option<&str>) -> HashMap<&str, &str> {
map
}
async fn route_dispatcher(conn: &Conn, resp: &mut Response<Body>, meth: &Method, path: &str, params: &HashMap<&str, &str>) {
async fn route_dispatcher(pool: &Pool, resp: &mut Response<Body>, meth: &Method, path: &str, params: &HashMap<&str, &str>) {
match (meth, path) {
(&Method::GET, routes::INVITE_JOIN) => invites::join_invite_code(conn, &mut resp, params).await,
(&Method::GET, routes::INVITE_JOIN) => {
if let Err(_) = invites::join_invite_code(pool, resp, params).await {
*resp.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
}
},
_ => {
*resp.status_mut() = StatusCode::NOT_FOUND;
}
@ -59,19 +63,18 @@ async fn main_responder(request: Request<Body>) -> Result<Response<Body>, hyper:
let path = request.uri().path();
let params = map_qs(request.uri().query());
if let Ok(conn) = Conn::from_url(env::var("DATABASE_URL").unwrap()).await {
let pool = Pool::new(&env::var("DATABASE_URL").unwrap());
// some more information in the response would be great right about here
match auth::wall_entry(path, conn, &params).await {
OpenAuth | Good => route_dispatcher(&conn, &mut response, &method, path, &params).await,
if let Ok(auth_result) = auth::wall_entry(path, &pool, &params).await {
match auth_result {
OpenAuth | Good => route_dispatcher(&pool, &mut response, &method, path, &params).await,
LimitPassed => *response.status_mut() = StatusCode::UNAUTHORIZED,
NoKey => *response.status_mut() = StatusCode::UNAUTHORIZED,
InternalFailure => *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR
}
}
else {
*response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
}
Ok(response)
}