+ First pass of db backend and API handlers for badges module

It should be noted that /badge/update needs some more planning so 1 more commit
is justified for it as some consideration must be taken for how individual badge
permissions are going to be handled
This commit is contained in:
shockrah 2021-05-29 19:13:13 -07:00
parent 4352c840ac
commit 2d1f9a37db
2 changed files with 153 additions and 0 deletions

80
json-api/db/src/badges.rs Normal file
View File

@ -0,0 +1,80 @@
use crate::Badge;
use mysql_async::prelude::Queryable;
use mysql_async::{params, Pool, Result as SqlResult};
use rand::RngCore;
pub async fn add(pool: &Pool, name: &str, color: u32, perms: u64) -> SqlResult<Badge>{
// First gather the parameters required for the query
let id = rand::rngs::OsRng.next_u64();
let mut conn = pool.get_conn().await?;
let q = "INSERT INTO badges (id, name, color, permissions)
VALUES(:id, :name, :color, :perms)";
let sqlparams = params!{
"id" => id,
"name" => name,
"color" => color,
"perms" => perms
};
conn.exec_drop(q, sqlparams).await?;
Ok(Badge{
id,
name: String::from(name),
color,
perms
})
}
pub async fn delete(pool: &Pool, id: u64) -> SqlResult<u64> {
let mut conn = pool.get_conn().await?;
let q = "DELETE FROM badges WHERE id = :id";
conn.exec_drop(q, params!{"id" => id}).await?;
Ok(id)
}
async fn get(pool: &Pool, id: u64) -> SqlResult<Option<Badge>> {
let mut conn = pool.get_conn().await?;
let q = "SELECT name, color, permissions FROM badges WHERE id = :id";
let row: Option<(String, u32, u64)> = conn.exec_first(q, params!{ "id" => id}).await?;
match row {
Some(row) => {
let b = Badge {
id,
name: row.0,
color: row.1,
perms: row.2,
};
Ok(Some(b))
},
None => Ok(None)
}
}
pub async fn update(pool: &Pool, badge: Badge) -> SqlResult<Option<Badge>> {
// Ok(None) if that badge was not registered anywhere
// Ok(Some(Badge)) if that badge was registered
if let Ok(None) = get(pool, badge.id).await {
return Ok(None);
}
let q = "UPDATE badges SET name = :name, color = :color, permissions = :perms
WHERE id = :id";
let p = params! {
"id" => badge.id,
"name" => badge.name.clone(),
"color" => badge.color,
"perms" => badge.perms
};
let mut conn = pool.get_conn().await?;
conn.exec_drop(q, p).await?;
Ok(Some(badge))
}
pub async fn list(pool: &Pool) -> SqlResult<Vec<Badge>> {
let q = "SELECT id, name, color, permissions FROM badges";
let mut conn = pool.get_conn().await?;
let set: Vec<Badge> = conn.exec_map(q, (), |(id, name, color, perms)| {
Badge { id, name, color, perms }
}).await?;
Ok(set)
}

73
json-api/src/badges.rs Normal file
View File

@ -0,0 +1,73 @@
use crate::{db, qs_param, http::set_json_body};
use std::collections::HashMap;
use mysql_async::Pool;
use hyper::{Response, StatusCode, Body};
use serde_json::json;
pub async fn new(p: &Pool, response: &mut Response<Body>, params: HashMap<String, String>) {
// Only name is really required the other two can default to white/
let name = qs_param!(params, "badge_name", String);
let perms = match qs_param!(params, "badge_perms", u64) {
Some(perms) => perms,
None => 0
};
let color = match qs_param!(params, "badge_color", u32) {
Some(color) => color,
None => 0xffffffff,
};
if let Some(name) = name {
match db::badges::add(p, &name, color, perms).await {
Ok(badge) => {
set_json_body(response, json!({"badge": json!(badge)}));
// TODO: add some rtc notification here
},
Err(e) => {
eprintln!("[HTTP][ERROR] /badge/new {}", e);
*response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
}
};
} else {
*response.status_mut() = StatusCode::BAD_REQUEST;
}
}
pub async fn update(_p: &Pool, _response: &mut Response<Body>, _params: HashMap<String, String>) {
/* TODO:
* This handler could actually benefit from being split into multiple handlers
* Concern: Permissions handling with is route could get ugly when handling updates to the
* permissions flag in each badge which is why we may want a
* /badge/update/color
* /badge/update/name
* /badge/update/permissions
*/
}
pub async fn delete(p: &Pool, response: &mut Response<Body>, params: HashMap<String, String>) {
if let Some(id) = qs_param!(params, "badge_id", u64) {
match db::badges::delete(p, id).await {
Ok(id) => {
// TODO: add rtc notification here
println!("TODO remove me at some point {}", id);
},
Err(e) => {
eprintln!("[HTTP][ERROR] /badge/delete {}", e);
*response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
}
}
} else {
*response.status_mut() = StatusCode::BAD_REQUEST;
}
}
pub async fn list(p: &Pool, response: &mut Response<Body>) {
match db::badges::list(p).await {
Ok(badges) => {
set_json_body(response, json!({"badges": json!(badges)}));
},
Err(e) => {
eprintln!("[HTTP][ERRO] /badge/list {}", e);
*response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
}
}
}