[BE] Remove panics from DB code

master
Araozu 2023-12-06 11:39:31 -05:00
parent 4db7c19409
commit 64007d39bd
11 changed files with 234 additions and 90 deletions

View File

@ -6,11 +6,18 @@ use rocket::serde::json::Json;
use crate::json_result::JsonResult;
use crate::model::person::{PersonCreate, PersonLink};
use crate::model::reniec_person::ReniecPerson;
use crate::model::DBError;
use crate::{db, model::person::Person};
#[get("/person/<dni>")]
pub async fn get_by_dni(dni: i32) -> (Status, Json<JsonResult<Person>>) {
let db = db().await;
let db = match db().await {
Ok(db) => db,
Err(reason) => {
return (Status::InternalServerError, JsonResult::err(reason));
}
};
info!("get person with dni {}", dni);
/*
@ -23,15 +30,22 @@ pub async fn get_by_dni(dni: i32) -> (Status, Json<JsonResult<Person>>) {
Err(error) => {
match error {
// Only if the person is not found in DB, continue
sqlx::Error::RowNotFound => (),
DBError::Sqlx(sqlx::Error::RowNotFound) => (),
// Otherwise, throw an error
_ => {
DBError::Sqlx(error) => {
error!("Error searching person with dni {}: {:?}", dni, error);
return (
Status::InternalServerError,
JsonResult::err(format!("Error buscando persona en DB.")),
);
}
DBError::Str(reason) => {
error!("Error searching person with dni {}: {}", dni, reason);
return (
Status::InternalServerError,
JsonResult::err(format!("Error buscando persona en DB.")),
);
}
}
}
};

View File

@ -224,7 +224,7 @@ fn get_image_info(path: PathBuf) -> ScanInfo {
ScanInfo::Error("Error renombrando archivo.".into())
}
}
};
}
};

View File

@ -1,6 +1,7 @@
#![feature(lazy_cell)]
use cors::Cors;
use once_cell::sync::OnceCell;
use rocket::tokio;
use sqlx::mysql::MySqlPoolOptions;
use sqlx::{MySql, Pool};
use std::env;
@ -23,36 +24,49 @@ static DB: OnceCell<Pool<MySql>> = OnceCell::new();
/// up to 3 times
///
/// If the database pool fails to initialize, this function will panic.
pub async fn db() -> &'static Pool<MySql> {
pub async fn db() -> Result<&'static Pool<MySql>, String> {
let attempts = 3;
for _ in 0..attempts {
match DB.get() {
Some(db) => return db,
Some(db) => return Ok(db),
None => {
log::info!("DB not initialized, initializing from db()");
init_db().await;
let _ = init_db().await;
}
}
};
}
log::error!("Failed to initialize DB after {} attempts", attempts);
panic!("DB not initialized");
Err("Failed to initialize DB".to_string())
}
pub async fn init_db() {
pub async fn init_db() -> Result<(), String> {
/*
Init DB and set it as a global variable
*/
let db_url = env::var("DATABASE_URL").expect("DATABASE_URL is not set in .env file");
let db_url = match env::var("DATABASE_URL") {
Ok(url) => url,
Err(_) => return Err("env DATABASE_URL not found".to_string()),
};
let pool = MySqlPoolOptions::new()
.max_connections(5)
.connect(db_url.as_str())
.await;
match pool {
Ok(pool) => DB.set(pool).expect("Failed to set DB pool"),
Err(e) => log::error!("Error connecting to DB: {}", e),
Ok(pool) => match DB.set(pool) {
Ok(_) => Ok(()),
Err(_) => {
log::error!("Failed to set DB global variable");
Err("Failed to set DB".to_string())
}
},
Err(e) => {
log::error!("Error connecting to DB: {}", e);
Err("Error connecting to DB".to_string())
}
}
}

View File

@ -27,12 +27,25 @@ pub struct Course {
}
impl Course {
pub async fn get_all() -> Result<Vec<Course>, sqlx::Error> {
let db = db().await;
pub async fn get_all() -> Result<Vec<Course>, String> {
let db = match db().await {
Ok(db) => db,
Err(reason) => {
return Err(reason);
}
};
let results = sqlx::query!("SELECT * FROM course")
.fetch_all(db)
.await?
let results = sqlx::query!("SELECT * FROM course").fetch_all(db).await;
let results = match results {
Ok(res) => res,
Err(error) => {
error!("Error getting courses: {:?}", error);
return Err(format!("Error getting courses"));
}
};
let results = results
.iter()
.map(|d| Course {
course_id: d.course_id,
@ -46,8 +59,13 @@ impl Course {
Ok(results)
}
pub async fn get_course_name(course_id: i32) -> Option<String> {
let db = db().await;
pub async fn get_course_name(course_id: i32) -> Result<String, String> {
let db = match db().await {
Ok(db) => db,
Err(reason) => {
return Err(reason);
}
};
let res = sqlx::query!(
"SELECT course_name FROM course WHERE course_id = ?",
@ -57,8 +75,12 @@ impl Course {
.await;
match res {
Ok(data) => Some(data.course_name),
Err(_) => None,
Ok(data) => Ok(data.course_name),
Err(sqlx::Error::RowNotFound) => Ok("".into()),
Err(err) => {
log::error!("Error fetching course name: {:?}", err);
return Err("Error fetching course name".into());
}
}
}
}

View File

@ -8,8 +8,11 @@ pub struct CustomLabel {
}
impl CustomLabel {
pub async fn get_all() -> Result<Vec<CustomLabel>, sqlx::Error> {
let db = db().await;
pub async fn get_all() -> Result<Vec<CustomLabel>, String> {
let db = match db().await {
Ok(db) => db,
Err(reason) => return Err(reason),
};
let result = sqlx::query_as::<_, CustomLabel>(
r#"
@ -18,20 +21,37 @@ impl CustomLabel {
"#,
)
.fetch_all(db)
.await?;
.await;
Ok(result)
match result {
Ok(res) => Ok(res),
Err(err) => {
log::error!("Error fetching custom labels: {:?}", err);
Err("Error fetching custom labels".into())
}
}
}
pub async fn get_id_by_value(value: &String) -> Result<i32, sqlx::Error> {
let db = db().await;
pub async fn get_id_by_value(value: &String) -> Result<i32, String> {
let db = match db().await {
Ok(db) => db,
Err(reason) => return Err(reason),
};
let result = sqlx::query!(
"SELECT custom_label_id FROM custom_label WHERE custom_label_value = ?",
value
)
.fetch_all(db)
.await?;
.await;
let result = match result {
Ok(r) => r,
Err(err) => {
log::error!("Error fetching label by value: {:?}", err);
return Err("Error fetching label by value".into());
}
};
if result.is_empty() {
Ok(-1)
@ -42,15 +62,24 @@ impl CustomLabel {
}
}
pub async fn create(value: &String) -> Result<i32, sqlx::Error> {
let db = db().await;
pub async fn create(value: &String) -> Result<i32, String> {
let db = match db().await {
Ok(db) => db,
Err(reason) => return Err(reason),
};
sqlx::query!(
let result = sqlx::query!(
"INSERT INTO custom_label (custom_label_value) VALUES (?)",
value
)
.execute(db)
.await?;
.await;
if let Err(err) = result {
log::error!("Error inserting custom label: {:?}", err);
return Err("Error inserting custom label".into());
}
let result = Self::get_id_by_value(value).await?;
Ok(result)

View File

@ -4,3 +4,8 @@ pub mod custom_label;
pub mod person;
pub mod register;
pub mod reniec_person;
pub enum DBError {
Str(String),
Sqlx(sqlx::Error),
}

View File

@ -3,6 +3,8 @@ use serde::{Deserialize, Serialize};
use crate::db;
use super::DBError;
/// Information about a person registered in DB
#[derive(Serialize, Clone)]
pub struct Person {
@ -31,8 +33,11 @@ pub struct Person {
}
impl Person {
pub async fn get_by_dni(dni: i32) -> Result<Person, sqlx::Error> {
let db = db().await;
pub async fn get_by_dni(dni: i32) -> Result<Person, DBError> {
let db = match db().await {
Ok(db) => db,
Err(reason) => return Err(DBError::Str(reason)),
};
let result = sqlx::query_as!(Person, "SELECT * FROM person WHERE person_dni = ?", dni)
.fetch_one(db)
@ -42,7 +47,7 @@ impl Person {
Ok(v) => Ok(v),
Err(e) => {
error!("Error searching person with dni {}: {:?}", dni, e);
Err(e)
Err(DBError::Sqlx(e))
}
}
}
@ -57,10 +62,13 @@ pub struct PersonCreate {
}
impl PersonCreate {
pub async fn create(&self) -> Result<(), sqlx::Error> {
let db = db().await;
pub async fn create(&self) -> Result<(), String> {
let db = match db().await {
Ok(db) => db,
Err(err) => return Err(err),
};
sqlx::query!(
let result = sqlx::query!(
"INSERT INTO person (person_dni, person_names, person_paternal_surname, person_maternal_surname) VALUES (?, ?, ?, ?)",
self.person_dni,
self.person_names,
@ -68,9 +76,15 @@ impl PersonCreate {
self.person_maternal_surname,
)
.execute(db)
.await?;
.await;
Ok(())
match result {
Ok(_) => Ok(()),
Err(err) => {
log::error!("Error creating person: {:?}", err);
Err("Error creating person".into())
}
}
}
}
@ -84,7 +98,10 @@ pub struct PersonLink {
impl PersonLink {
/// Links a person to a user in the online classroom
pub async fn insert(&self) -> Result<(), String> {
let db = db().await;
let db = match db().await {
Ok(db) => db,
Err(reason) => return Err(reason),
};
let res = sqlx::query!(
"UPDATE person SET person_classroom_id = ?, person_classroom_username = ? WHERE person_id = ?",

View File

@ -31,8 +31,11 @@ pub struct RegisterCreate {
impl RegisterCreate {
/// Registers a new certificate
pub async fn create(&self) -> Result<(), sqlx::Error> {
let db = db().await;
pub async fn create(&self) -> Result<(), String> {
let db = match db().await {
Ok(db) => db,
Err(reason) => return Err(reason),
};
// Get custom_label_id from db based of self.custom_label
let custom_label_id = {
@ -57,7 +60,7 @@ impl RegisterCreate {
// Current date in YYYY-MM-DD format
let current_date = chrono::Local::now().format("%Y-%m-%d").to_string();
let _ = sqlx::query!(
let result = sqlx::query!(
"INSERT INTO register (
register_code,
register_creation_date,
@ -76,15 +79,21 @@ impl RegisterCreate {
self.course_id
)
.execute(db)
.await?;
.await;
Ok(())
match result {
Ok(_) => Ok(()),
Err(err) => {
log::error!("Error inserting person: {:?}", err);
Err("Error inserting person".into())
}
}
}
async fn get_next_register_code(course_id: i32) -> Result<i32, sqlx::Error> {
let db = db().await;
async fn get_next_register_code(course_id: i32) -> Result<i32, String> {
let db = db().await?;
let course_name = Course::get_course_name(course_id).await;
let course_name = Course::get_course_name(course_id).await?;
// All matpel certs share codes
let new_max = if course_name.contains("Matpel") {
@ -94,10 +103,18 @@ impl RegisterCreate {
(SELECT course_id FROM course WHERE course_name LIKE 'Matpel%')",
)
.fetch_one(db)
.await?;
.await;
// If there are no registers, the new code will start from 50, as per the requirements
res.max.unwrap_or(50) + 1
match res {
Ok(res) => {
// If there are no registers, the new code will start from 50, as per the requirements
res.max.unwrap_or(50) + 1
}
Err(err) => {
log::error!("Error fetching matpel code: {:?}", err);
return Err("Error fetching matpel code".into());
}
}
} else {
let res = sqlx::query!(
"SELECT MAX(register_code) AS max FROM register
@ -105,10 +122,18 @@ impl RegisterCreate {
course_id
)
.fetch_one(db)
.await?;
.await;
// If there are no registers, the new code will start from 50, as per the requirements
res.max.unwrap_or(50) + 1
match res {
Ok(res) => {
// If there are no registers, the new code will start from 50, as per the requirements
res.max.unwrap_or(50) + 1
}
Err(err) => {
log::error!("Error fetching cert code: {:?}", err);
return Err("Error fetching cert code".into());
}
}
};
Ok(new_max)
@ -139,8 +164,8 @@ pub struct Register {
}
impl Register {
pub async fn get_by_dni(dni: String) -> Result<Vec<Register>, sqlx::Error> {
let db = db().await;
pub async fn get_by_dni(dni: String) -> Result<Vec<Register>, String> {
let db = db().await?;
let res = sqlx::query!(
"SELECT * FROM register
@ -148,38 +173,54 @@ impl Register {
dni
)
.fetch_all(db)
.await?
.into_iter()
.map(|r| Register {
register_id: r.register_id,
register_code: r.register_code,
register_creation_date: r.register_creation_date.format("%Y-%m-%d").to_string(),
register_display_date: r.register_display_date.format("%Y-%m-%d").to_string(),
register_custom_label: r.register_custom_label,
register_is_preview: r.register_is_preview == 1,
register_person_id: r.register_person_id,
register_course_id: r.register_course_id,
})
.collect();
.await;
let res = match res {
Ok(res) => res,
Err(err) => {
log::error!("Error fetching register for DNI: {:?}", err);
return Err("Error fetching register for DNI".into());
}
};
let res = res
.into_iter()
.map(|r| Register {
register_id: r.register_id,
register_code: r.register_code,
register_creation_date: r.register_creation_date.format("%Y-%m-%d").to_string(),
register_display_date: r.register_display_date.format("%Y-%m-%d").to_string(),
register_custom_label: r.register_custom_label,
register_is_preview: r.register_is_preview == 1,
register_person_id: r.register_person_id,
register_course_id: r.register_course_id,
})
.collect();
Ok(res)
}
pub async fn delete(register_id: i32) -> Result<(), sqlx::Error> {
let db = db().await;
pub async fn delete(register_id: i32) -> Result<(), String> {
let db = db().await?;
let _ = sqlx::query!("DELETE FROM register WHERE register_id = ?", register_id)
let res = sqlx::query!("DELETE FROM register WHERE register_id = ?", register_id)
.execute(db)
.await?;
.await;
Ok(())
match res {
Ok(_) => Ok(()),
Err(err) => {
log::error!("Error deleting register: {:?}", err);
Err("Error deleting register".into())
}
}
}
pub async fn get_course_and_person(
register_id_list: String,
person_dni_list: String,
) -> Result<Vec<ScanData>, sqlx::Error> {
let db = db().await;
) -> Result<Vec<ScanData>, String> {
let db = db().await?;
let sql = format!(
"
@ -209,7 +250,13 @@ impl Register {
log::info!("sql: {}", sql);
let result = db.fetch_all(sql.as_str()).await?;
let result = match db.fetch_all(sql.as_str()).await {
Ok(res) => res,
Err(err) => {
log::error!("Error fetching course & person: {:?}", err);
return Err("Error fetching course & person".into());
}
};
log::info!("rows: {}", result.len());

View File

@ -61,7 +61,6 @@ pub async fn create(data: &ClassroomPersonCreate) -> Result<PersonLink, String>
Err("Expected empty body, found something else".into())
}
/// Makes a request to the user creation form, and gets the
/// security token.
///
@ -91,7 +90,6 @@ async fn get_form_sec_token() -> Result<String, String> {
Ok(sec_token_value.into())
}
/// Creates a request body data to send to the classroom
fn get_request_body(
surnames: &String,

View File

@ -12,7 +12,6 @@ mod session;
pub mod update_expiration_date;
pub mod user;
/// Tries to connect to the online classroom, and gets a session cookie
/// if neccesary.
///

View File

@ -238,7 +238,6 @@ async fn login() -> Result<(), String> {
}
}
/// Helper function to log the responses recieved from the online classroom,
/// for debugging purposes
pub fn log_html(html: &String) {