Even more error handling
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Lucien Cartier-Tilet 2023-01-17 00:58:01 +01:00
parent 3220f5c005
commit a36fd740af
Signed by: phundrak
GPG Key ID: BD7789E705CB8DCA
2 changed files with 56 additions and 66 deletions

View File

@ -7,12 +7,12 @@ use self::models::words::Word;
use diesel::pg::PgConnection; use diesel::pg::PgConnection;
use diesel::r2d2::{ConnectionManager, Pool, PooledConnection}; use diesel::r2d2::{ConnectionManager, Pool, PooledConnection};
use diesel::result::Error;
use diesel::{insert_into, prelude::*}; use diesel::{insert_into, prelude::*};
use dotenvy::dotenv; use dotenvy::dotenv;
use juniper::{graphql_value, DefaultScalarValue, FieldError, IntoFieldError}; use juniper::{graphql_value, DefaultScalarValue, FieldError, IntoFieldError};
use std::env; use std::env;
use std::error::Error;
use tracing::info; use tracing::info;
#[derive(Debug)] #[derive(Debug)]
@ -36,7 +36,7 @@ impl DatabaseError {
} }
} }
impl Error for DatabaseError {} impl std::error::Error for DatabaseError {}
impl std::fmt::Display for DatabaseError { impl std::fmt::Display for DatabaseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@ -53,23 +53,6 @@ impl IntoFieldError for DatabaseError {
} }
} }
macro_rules! find_element {
($conn:expr,$dsl:ident,$type:ty,$value:expr,$errmsg:expr) => {
if let Ok(val) = $conn {
$dsl.find($value).first::<$type>(val).map_or_else(
|e| {
info!("{}: {:?}", $errmsg, e);
None
},
Some,
)
} else {
info!("Failed to obtain connection for the database");
None
}
};
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Database { pub struct Database {
conn: Pool<ConnectionManager<PgConnection>>, conn: Pool<ConnectionManager<PgConnection>>,
@ -170,37 +153,42 @@ impl Database {
}) })
} }
pub fn language(&self, name: &str, owner: &str) -> Option<Language> { pub fn language(
&self,
name: &str,
owner: &str,
) -> Result<Option<Language>, DatabaseError> {
use self::schema::languages::dsl; use self::schema::languages::dsl;
match &mut self.conn() { match dsl::languages
Ok(conn) => match dsl::languages .filter(dsl::name.eq(name))
.filter(dsl::name.eq(name)) .filter(dsl::owner.eq(owner))
.filter(dsl::owner.eq(owner)) .first(&mut self.conn()?)
.first::<Language>(conn) {
{ Ok(val) => Ok(Some(val)),
Ok(val) => Some(val), Err(Error::NotFound) => Ok(None),
Err(e) => { Err(e) => Err(DatabaseError::new(
info!("Could not retrieve language {} of user {} from database: {:?}", format!(
name, owner, e); "Failed to find language {} belonging to {}: {:?}",
None name, owner, e
} ),
}, "Database error",
Err(e) => { )),
info!("Could not connect to the database: {:?}", e);
None
}
} }
} }
pub fn user(&self, id: &str) -> Option<User> { pub fn user(&self, id: &str) -> Result<Option<User>, DatabaseError> {
use self::schema::users::dsl::users; use self::schema::users::dsl::users;
find_element!( match users.find(id).first::<User>(&mut self.conn()?) {
&mut self.conn(), Ok(val) => Ok(Some(val)),
users, Err(Error::NotFound) => Ok(None),
User, Err(e) => Err(DatabaseError::new(
id.to_string(), format!(
format!("Failed to retrieve user {} from database", id) "Failed to retrieve user {} from database: {:?}",
) id, e
),
"Database Error",
)),
}
} }
pub fn insert_user( pub fn insert_user(
@ -239,18 +227,18 @@ impl Database {
} }
} }
pub fn word_id(&self, id: &str) -> Option<Word> { pub fn word_id(&self, id: &str) -> Result<Option<Word>, DatabaseError> {
use self::schema::words::dsl; use self::schema::words::dsl;
if let Ok(conn) = &mut self.conn() { match dsl::words.find(id).first::<Word>(&mut self.conn()?) {
match dsl::words.find(id).first::<Word>(conn) { Ok(val) => Ok(Some(val)),
Ok(val) => Some(val), Err(Error::NotFound) => Ok(None),
Err(e) => { Err(e) => Err(DatabaseError::new(
info!("Error retrieving {}: {:?}", id, e); format!(
None "Failed to retrieve word {} from database: {:?}",
} id, e
} ),
} else { "Database Error",
None )),
} }
} }

View File

@ -76,24 +76,27 @@ impl Query {
context: &Context, context: &Context,
name: String, name: String,
owner: String, owner: String,
) -> Option<Language> { ) -> FieldResult<Option<Language>> {
context.db.language(name.as_str(), owner.as_str()) context
.db
.language(name.as_str(), owner.as_str())
.map_err(Into::into)
} }
#[graphql( #[graphql(
description = "Retrieve a specific user from its id", description = "Retrieve a specific user from its id",
arguments(id(description = "Appwrite ID of a user")) arguments(id(description = "Appwrite ID of a user"))
)] )]
fn user(context: &Context, id: String) -> Option<User> { fn user(context: &Context, id: String) -> FieldResult<Option<User>> {
context.db.user(id.as_str()) context.db.user(id.as_str()).map_err(Into::into)
} }
#[graphql( #[graphql(
description = "Retrieve a specific word from its id", description = "Retrieve a specific word from its id",
arguments(id(description = "Unique identifier of a word")) arguments(id(description = "Unique identifier of a word"))
)] )]
fn word(context: &Context, id: String) -> Option<Word> { fn word(context: &Context, id: String) -> FieldResult<Option<Word>> {
context.db.word_id(id.as_str()) context.db.word_id(id.as_str()).map_err(Into::into)
} }
#[graphql( #[graphql(
@ -140,10 +143,9 @@ impl Query {
word: String, word: String,
) -> FieldResult<Vec<Word>> { ) -> FieldResult<Vec<Word>> {
match Uuid::from_str(&language) { match Uuid::from_str(&language) {
Ok(uuid) => context Ok(uuid) => {
.db context.db.words(uuid, word.as_str()).map_err(Into::into)
.words(uuid, word.as_str()) }
.map_err(Into::into),
Err(e) => Err(DatabaseError::new( Err(e) => Err(DatabaseError::new(
format!("Failed to convert {} to a UUID: {:?}", language, e), format!("Failed to convert {} to a UUID: {:?}", language, e),
"Conversion Error", "Conversion Error",