Better error handling

This commit is contained in:
Lucien Cartier-Tilet 2023-01-16 00:22:58 +01:00
parent 425e00acc1
commit dcaa920c51
Signed by: phundrak
GPG Key ID: BD7789E705CB8DCA
3 changed files with 54 additions and 21 deletions

View File

@ -18,10 +18,12 @@ use tracing::info;
#[derive(Debug)] #[derive(Debug)]
pub struct DatabaseError { pub struct DatabaseError {
long: String, long: String,
#[allow(dead_code)]
short: String, short: String,
} }
impl DatabaseError { impl DatabaseError {
#[allow(clippy::needless_pass_by_value)]
pub fn new<S, T>(long: S, short: T) -> Self pub fn new<S, T>(long: S, short: T) -> Self
where where
T: ToString, T: ToString,

View File

@ -1,10 +1,15 @@
use super::super::schema; use super::super::schema;
use crate::{db::Database, graphql::Context}; use crate::{
db::{Database, DatabaseError},
graphql::Context,
};
use diesel::prelude::*; use diesel::prelude::*;
use juniper::GraphQLEnum; use juniper::{FieldResult, GraphQLEnum};
use schema::{wordrelation, words}; use schema::{wordrelation, words};
use tracing::info; use tracing::info;
use std::convert::Into;
use super::languages::Language; use super::languages::Language;
#[derive(diesel_derive_enum::DbEnum, Debug, Clone, PartialEq, Eq)] #[derive(diesel_derive_enum::DbEnum, Debug, Clone, PartialEq, Eq)]
@ -62,24 +67,29 @@ impl Word {
&self, &self,
db: &Database, db: &Database,
relationship: WordRelationship, relationship: WordRelationship,
) -> Vec<Word> { ) -> Result<Vec<Word>, DatabaseError> {
use schema::wordrelation::dsl; use schema::wordrelation::dsl;
match &mut db.conn() { match &mut db.conn() {
Ok(conn) => dsl::wordrelation Ok(conn) => Ok(dsl::wordrelation
.filter(dsl::wordsource.eq(self.norm.clone())) .filter(dsl::wordsource.eq(self.norm.clone()))
.filter(dsl::relationship.eq(relationship)) .filter(dsl::relationship.eq(relationship))
.load::<WordRelation>(conn) .load::<WordRelation>(conn)
.unwrap() .map_err(|e| {
DatabaseError::new(
format!("Failed to retrieve word relations: {:?}", e),
"Database reading failed",
)
})?
.into_iter() .into_iter()
.flat_map(|w| { .flat_map(|w| {
use schema::words::dsl; use schema::words::dsl;
dsl::words.find(w.wordtarget).first::<Word>(conn) dsl::words.find(w.wordtarget).first::<Word>(conn)
}) })
.collect::<Vec<Word>>(), .collect::<Vec<Word>>()),
Err(e) => { Err(e) => Err(DatabaseError::new(
info!("Could not connect to database: {:?}", e); format!("Failed to connect to the database: {:?}", e),
Vec::new() "Database connection error",
} )),
} }
} }
} }
@ -185,16 +195,18 @@ impl Word {
name = "related", name = "related",
description = "Words related to the current word" description = "Words related to the current word"
)] )]
fn related_words(&self, context: &Context) -> Vec<Word> { fn related_words(&self, context: &Context) -> FieldResult<Vec<Word>> {
self.relationship(&context.db, WordRelationship::Related) self.relationship(&context.db, WordRelationship::Related)
.map_err(Into::into)
} }
#[graphql( #[graphql(
name = "definitions", name = "definitions",
description = "Words that define the current word" description = "Words that define the current word"
)] )]
fn definitions(&self, context: &Context) -> Vec<Word> { fn definitions(&self, context: &Context) -> FieldResult<Vec<Word>> {
self.relationship(&context.db, WordRelationship::Definition) self.relationship(&context.db, WordRelationship::Definition)
.map_err(Into::into)
} }
} }

View File

@ -1,7 +1,10 @@
use juniper::FieldResult; use juniper::FieldResult;
use super::Context; use super::Context;
use crate::db::{models::{languages::Language, users::User, words::Word}, DatabaseError}; use crate::db::{
models::{languages::Language, users::User, words::Word},
DatabaseError,
};
use std::str::FromStr; use std::str::FromStr;
@ -16,15 +19,19 @@ impl Query {
name = "allLanguages", name = "allLanguages",
description = "Retrieve all languages defined in the database" description = "Retrieve all languages defined in the database"
)] )]
fn all_languages(context: &Context) -> Vec<Language> { fn all_languages(context: &Context) -> FieldResult<Vec<Language>> {
context.db.all_languages().unwrap() context.db.all_languages().map_err(Into::into)
} }
fn all_users(context: &Context, admin_key: String) -> FieldResult<Vec<User>> { fn all_users(
context: &Context,
admin_key: String,
) -> FieldResult<Vec<User>> {
if admin_key == context.other_vars.admin_key { if admin_key == context.other_vars.admin_key {
context.db.all_users().map_err(Into::into) context.db.all_users().map_err(Into::into)
} else { } else {
Err(DatabaseError::new("Invalid admin key", "Invalid admin key").into()) Err(DatabaseError::new("Invalid admin key", "Invalid admin key")
.into())
} }
} }
@ -69,9 +76,21 @@ impl Query {
word(description = "Word to search") word(description = "Word to search")
) )
)] )]
fn words(context: &Context, language: String, word: String) -> Vec<Word> { fn words(
context context: &Context,
.db language: String,
.words(uuid::Uuid::from_str(&language).unwrap(), word.as_str()) word: String,
) -> FieldResult<Vec<Word>> {
match uuid::Uuid::from_str(&language) {
Ok(uuid) => Ok(context.db.words(uuid, word.as_str())),
Err(e) => Err(DatabaseError::new(
format!(
"Failed to convert {} to a proper UUID: {:?}",
language, e
),
"Conversion Error",
)
.into()),
}
} }
} }