From c618bd96676ae502dcdf05e588dab5379b80b262 Mon Sep 17 00:00:00 2001 From: Lucien Cartier-Tilet Date: Tue, 24 Jan 2023 03:51:31 +0100 Subject: [PATCH] Add and remove words --- src/db/models/languages.rs | 30 ++++++---- src/db/models/words.rs | 113 +++++++++++++++++++++++++++++++++++++ src/graphql/mutation.rs | 39 ++++++++++++- 3 files changed, 171 insertions(+), 11 deletions(-) diff --git a/src/db/models/languages.rs b/src/db/models/languages.rs index 8744892..3cdb6bb 100644 --- a/src/db/models/languages.rs +++ b/src/db/models/languages.rs @@ -11,7 +11,7 @@ use uuid::Uuid; use super::super::schema; use super::users::User; -use std::convert::Into; +use std::{convert::Into, fmt::Display}; use schema::{langandagents, langtranslatesto, languages, userfollowlanguage}; @@ -103,7 +103,7 @@ impl From for NewLanguageInternal { } impl NewLanguage { - pub fn insert_db( + pub fn insert( &self, db: &Database, owner: &str, @@ -156,7 +156,17 @@ pub struct Language { owner: String, } +impl Display for Language { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}/{} ({})", self.owner, self.name, self.id) + } +} + impl Language { + pub fn is_owned_by(&self, owner: &str) -> bool { + self.owner == owner + } + pub fn find( db: &Database, language: Uuid, @@ -184,14 +194,14 @@ impl Language { .first::(conn) { Ok(language) if context.user_auth == Some(language.owner.clone()) => { - match diesel::delete(dsl::languages.find(language_id)) - .execute(conn) { - Ok(_) => Ok(()), - Err(e) => Err(DatabaseError::new( - format!("Failed to delete language {language_id}: {e:?}"), - "Database Error" - )) - } + match diesel::delete(dsl::languages.find(language_id)) + .execute(conn) { + Ok(_) => Ok(()), + Err(e) => Err(DatabaseError::new( + format!("Failed to delete language {language_id}: {e:?}"), + "Database Error" + )) + } }, Ok(language) => { Err(DatabaseError::new( diff --git a/src/db/models/words.rs b/src/db/models/words.rs index fb229cb..ef906a3 100644 --- a/src/db/models/words.rs +++ b/src/db/models/words.rs @@ -83,6 +83,60 @@ pub struct NewWord { morphology: Option, } +impl NewWord { + pub fn insert( + &self, + context: &Context, + user: &str, + ) -> Result { + use words::dsl; + let conn = &mut context.db.conn()?; + let mut word: NewWordInternal = + self.clone().try_into().map_err(|e| { + DatabaseError::new( + format!("Failed to parse string as uuid: {e:?}"), + "Invalid Input", + ) + })?; + match Language::find(&context.db, word.language) { + Ok(language) if language.is_owned_by(<&str>::clone(&user)) => { + // Check lemma exists + word.lemma = if let Some(id) = word.lemma { + match dsl::words.find(id).first::(conn) { + Ok(_) => Some(id), + Err(_) => None, + } + } else { + None + }; + match diesel::insert_into(dsl::words) + .values(word.clone()) + .execute(conn) + { + Ok(_) => dsl::words.filter(dsl::norm.eq(word.norm.clone())) + .filter(dsl::language.eq(word.language)) + .first::(conn) + .map_err(|e| DatabaseError::new( + format!("Failed to find word {word:?} in database: {e:?}"), + "Database Error" + )), + Err(e) => Err(DatabaseError::new( + format!( + "Failed to insert word {word:?} in database: {e:?}" + ), + "Database Error", + )), + } + } + Ok(language) => Err(DatabaseError::new( + format!("Language {language} is not owned by user {user}"), + "Forbidden", + )), + Err(e) => Err(e), + } + } +} + #[derive(Debug, Clone, Insertable)] #[diesel(table_name = words)] struct NewWordInternal { @@ -145,6 +199,65 @@ pub struct Word { } impl Word { + pub fn find(db: &Database, word: Uuid) -> Result { + use words::dsl; + dsl::words + .find(word) + .first::(&mut db.conn()?) + .map_err(|e| match e { + diesel::NotFound => DatabaseError::new( + format!("Word {word} not found"), + "Not Found", + ), + e => DatabaseError::new( + format!("Error fetching word {word} from database: {e:?}"), + "Database Error", + ), + }) + } + + pub fn is_owned_by( + &self, + db: &Database, + user: &str, + ) -> Result { + let language: Language = Language::find(db, self.language)?; + Ok(language.is_owned_by(user)) + } + + pub fn delete( + context: &Context, + id: Uuid, + user: &str, + ) -> Result<(), DatabaseError> { + use words::dsl; + let conn = &mut context.db.conn()?; + match dsl::words.find(id).first::(conn) { + Ok(word) => { + if let Ok(true) = word.is_owned_by(&context.db, user) { + match diesel::delete(dsl::words.find(id)) + .execute(&mut context.db.conn()?) + { + Ok(_) => Ok(()), + Err(e) => Err(DatabaseError::new( + format!("Failed to delete word {id} from database: {e:?}"), + "Database Error", + )), + } + } else { + Err(DatabaseError::new( + format!("User {user} cannot delete word from language he doesn't own"), + "Forbidden" + )) + } + } + Err(e) => Err(DatabaseError::new( + format!("Failed to find word {id} in database: {e:?}"), + "Database Error", + )), + } + } + fn relationship( &self, db: &Database, diff --git a/src/graphql/mutation.rs b/src/graphql/mutation.rs index 883e859..cf5d49e 100644 --- a/src/graphql/mutation.rs +++ b/src/graphql/mutation.rs @@ -7,6 +7,7 @@ use crate::db::{ models::{ languages::{Language, NewLanguage, UserFollowLanguage}, users::User, + words::{NewWord, Word}, }, DatabaseError, }; @@ -122,7 +123,7 @@ impl Mutation { language: NewLanguage, ) -> FieldResult { if let Some(owner) = &context.user_auth { - language.insert_db(&context.db, owner).map_err(Into::into) + language.insert(&context.db, owner).map_err(Into::into) } else { Err(DatabaseError::new( "User not authentificated, cannot create new language", @@ -157,4 +158,40 @@ impl Mutation { .into()) } } + + pub fn new_word(context: &Context, word: NewWord) -> FieldResult { + if let Some(user) = &context.user_auth { + word.insert(context, user).map_err(Into::into) + } else { + Err(DatabaseError::new( + "User not authentificated, cannot create new language", + "Unauthorized", + ) + .into()) + } + } + + pub fn delete_word( + context: &Context, + word: String, + ) -> FieldResult> { + if let Some(user) = &context.user_auth { + match Uuid::from_str(&word) { + Ok(id) => Word::delete(context, id, user) + .map(|_| None) + .map_err(Into::into), + Err(e) => Err(DatabaseError::new( + format!("Could not parse {word} as a valid UUID: {e:?}"), + "Bad Request", + ) + .into()), + } + } else { + Err(DatabaseError::new( + "User not authentificated, cannot create new language", + "Unauthorized", + ) + .into()) + } + } }