Implement more mutations
Implement mutations: - create language - delete language - user follows a language - user unfollows a language - new word Context's user_auth now contains either the user's ID or nothing if not authentificated.
This commit is contained in:
		
							parent
							
								
									51f0fc3108
								
							
						
					
					
						commit
						a624636939
					
				@ -27,6 +27,12 @@ pub enum Release {
 | 
				
			|||||||
    Private,
 | 
					    Private,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Default for Release {
 | 
				
			||||||
 | 
					    fn default() -> Self {
 | 
				
			||||||
 | 
					        Self::Public
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(
 | 
					#[derive(
 | 
				
			||||||
    diesel_derive_enum::DbEnum, Debug, Clone, PartialEq, Eq, GraphQLEnum,
 | 
					    diesel_derive_enum::DbEnum, Debug, Clone, PartialEq, Eq, GraphQLEnum,
 | 
				
			||||||
)]
 | 
					)]
 | 
				
			||||||
@ -50,6 +56,91 @@ pub enum AgentLanguageRelation {
 | 
				
			|||||||
    Author,
 | 
					    Author,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Default, Debug, Clone, juniper::GraphQLInputObject)]
 | 
				
			||||||
 | 
					pub struct NewLanguage {
 | 
				
			||||||
 | 
					    name: String,
 | 
				
			||||||
 | 
					    native: Option<String>,
 | 
				
			||||||
 | 
					    release: Option<Release>,
 | 
				
			||||||
 | 
					    genre: Vec<DictGenre>,
 | 
				
			||||||
 | 
					    abstract_: Option<String>,
 | 
				
			||||||
 | 
					    description: Option<String>,
 | 
				
			||||||
 | 
					    rights: Option<String>,
 | 
				
			||||||
 | 
					    license: Option<String>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Insertable, Debug, Clone)]
 | 
				
			||||||
 | 
					#[diesel(table_name = languages)]
 | 
				
			||||||
 | 
					struct NewLanguageInternal {
 | 
				
			||||||
 | 
					    name: String,
 | 
				
			||||||
 | 
					    native: Option<String>,
 | 
				
			||||||
 | 
					    release: Release,
 | 
				
			||||||
 | 
					    genre: Vec<DictGenre>,
 | 
				
			||||||
 | 
					    abstract_: Option<String>,
 | 
				
			||||||
 | 
					    description: Option<String>,
 | 
				
			||||||
 | 
					    rights: Option<String>,
 | 
				
			||||||
 | 
					    license: Option<String>,
 | 
				
			||||||
 | 
					    owner: String,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<NewLanguage> for NewLanguageInternal {
 | 
				
			||||||
 | 
					    fn from(val: NewLanguage) -> Self {
 | 
				
			||||||
 | 
					        NewLanguageInternal {
 | 
				
			||||||
 | 
					            name: val.name,
 | 
				
			||||||
 | 
					            native: val.native,
 | 
				
			||||||
 | 
					            release: if let Some(release) = val.release {
 | 
				
			||||||
 | 
					                release
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                Release::default()
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            genre: val.genre,
 | 
				
			||||||
 | 
					            abstract_: val.abstract_,
 | 
				
			||||||
 | 
					            description: val.description,
 | 
				
			||||||
 | 
					            rights: val.rights,
 | 
				
			||||||
 | 
					            license: val.license,
 | 
				
			||||||
 | 
					            owner: String::new(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl NewLanguage {
 | 
				
			||||||
 | 
					    pub fn insert_db(
 | 
				
			||||||
 | 
					        &self,
 | 
				
			||||||
 | 
					        db: &Database,
 | 
				
			||||||
 | 
					        owner: &str,
 | 
				
			||||||
 | 
					    ) -> Result<Language, DatabaseError> {
 | 
				
			||||||
 | 
					        use languages::dsl;
 | 
				
			||||||
 | 
					        let conn = &mut db.conn()?;
 | 
				
			||||||
 | 
					        match diesel::insert_into(dsl::languages)
 | 
				
			||||||
 | 
					            .values(NewLanguageInternal {
 | 
				
			||||||
 | 
					                owner: owner.to_string(),
 | 
				
			||||||
 | 
					                ..self.clone().into()
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .execute(conn)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Ok(_) => dsl::languages
 | 
				
			||||||
 | 
					                .filter(dsl::name.eq(self.name.clone()))
 | 
				
			||||||
 | 
					                .filter(dsl::owner.eq(owner))
 | 
				
			||||||
 | 
					                .first::<Language>(conn)
 | 
				
			||||||
 | 
					                .map_err(|e| {
 | 
				
			||||||
 | 
					                    DatabaseError::new(
 | 
				
			||||||
 | 
					                        format!(
 | 
				
			||||||
 | 
					                            "Failed to find language {} by user {owner}: {e:?}",
 | 
				
			||||||
 | 
					                            self.name
 | 
				
			||||||
 | 
					                        ),
 | 
				
			||||||
 | 
					                        "Database Error",
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                }),
 | 
				
			||||||
 | 
					            Err(e) => Err(DatabaseError::new(
 | 
				
			||||||
 | 
					                format!(
 | 
				
			||||||
 | 
					                    "Failed to insert language {} by user {owner}: {e:?}",
 | 
				
			||||||
 | 
					                    self.name
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                "Database Error",
 | 
				
			||||||
 | 
					            )),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Queryable, Insertable, Debug, Clone)]
 | 
					#[derive(Queryable, Insertable, Debug, Clone)]
 | 
				
			||||||
pub struct Language {
 | 
					pub struct Language {
 | 
				
			||||||
    id: Uuid,
 | 
					    id: Uuid,
 | 
				
			||||||
@ -66,6 +157,59 @@ pub struct Language {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Language {
 | 
					impl Language {
 | 
				
			||||||
 | 
					    pub fn find(
 | 
				
			||||||
 | 
					        db: &Database,
 | 
				
			||||||
 | 
					        language: Uuid,
 | 
				
			||||||
 | 
					    ) -> Result<Language, DatabaseError> {
 | 
				
			||||||
 | 
					        use languages::dsl;
 | 
				
			||||||
 | 
					        dsl::languages.find(language).first::<Language>(&mut db.conn()?).map_err(|e| match e {
 | 
				
			||||||
 | 
					            diesel::NotFound => DatabaseError::new(
 | 
				
			||||||
 | 
					                format!("Language {language} not found"),
 | 
				
			||||||
 | 
					                "Not Found"
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            e => DatabaseError::new(
 | 
				
			||||||
 | 
					                format!("Error fetching language {language} from database: {e:?}"),
 | 
				
			||||||
 | 
					                "Database Error"
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn delete(
 | 
				
			||||||
 | 
					        context: &Context,
 | 
				
			||||||
 | 
					        language_id: Uuid,
 | 
				
			||||||
 | 
					    ) -> Result<(), DatabaseError> {
 | 
				
			||||||
 | 
					        use languages::dsl;
 | 
				
			||||||
 | 
					        let conn = &mut context.db.conn()?;
 | 
				
			||||||
 | 
					        match dsl::languages.find(language_id)
 | 
				
			||||||
 | 
					                            .first::<Language>(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"
 | 
				
			||||||
 | 
					                ))
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            Ok(language) => {
 | 
				
			||||||
 | 
					                Err(DatabaseError::new(
 | 
				
			||||||
 | 
					                    format!(
 | 
				
			||||||
 | 
					                        "User {} not allowed to delete other user's language {language_id}",
 | 
				
			||||||
 | 
					                        language.owner),
 | 
				
			||||||
 | 
					                    "Unauthorized"
 | 
				
			||||||
 | 
					                ))
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            Err(e) => {
 | 
				
			||||||
 | 
					                Err(DatabaseError::new(
 | 
				
			||||||
 | 
					                    format!("Failed to delete language {language_id}: {e:?}"),
 | 
				
			||||||
 | 
					                    "Database Error"
 | 
				
			||||||
 | 
					                ))
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn relationship(
 | 
					    fn relationship(
 | 
				
			||||||
        &self,
 | 
					        &self,
 | 
				
			||||||
        db: &Database,
 | 
					        db: &Database,
 | 
				
			||||||
@ -290,6 +434,13 @@ pub struct LangTranslatesTo {
 | 
				
			|||||||
    langto: Uuid,
 | 
					    langto: Uuid,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Insertable)]
 | 
				
			||||||
 | 
					#[diesel(table_name = userfollowlanguage)]
 | 
				
			||||||
 | 
					pub struct UserFollowLanguageInsert {
 | 
				
			||||||
 | 
					    pub lang: Uuid,
 | 
				
			||||||
 | 
					    pub userid: String,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Queryable, Insertable, Debug, Clone, PartialEq, Eq)]
 | 
					#[derive(Queryable, Insertable, Debug, Clone, PartialEq, Eq)]
 | 
				
			||||||
#[diesel(table_name = userfollowlanguage)]
 | 
					#[diesel(table_name = userfollowlanguage)]
 | 
				
			||||||
pub struct UserFollowLanguage {
 | 
					pub struct UserFollowLanguage {
 | 
				
			||||||
@ -297,3 +448,71 @@ pub struct UserFollowLanguage {
 | 
				
			|||||||
    pub lang: Uuid,
 | 
					    pub lang: Uuid,
 | 
				
			||||||
    pub userid: String,
 | 
					    pub userid: String,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl UserFollowLanguage {
 | 
				
			||||||
 | 
					    pub fn user_follow_language(
 | 
				
			||||||
 | 
					        context: &Context,
 | 
				
			||||||
 | 
					        userid: &str,
 | 
				
			||||||
 | 
					        lang: Uuid,
 | 
				
			||||||
 | 
					    ) -> Result<Language, DatabaseError> {
 | 
				
			||||||
 | 
					        let conn = &mut context.db.conn()?;
 | 
				
			||||||
 | 
					        match languages::dsl::languages.find(lang).first::<Language>(conn) {
 | 
				
			||||||
 | 
					            Err(diesel::NotFound) => Err(DatabaseError::new(
 | 
				
			||||||
 | 
					                format!("Cannot follow non-existing language {lang}"),
 | 
				
			||||||
 | 
					                "Invalid Language",
 | 
				
			||||||
 | 
					            )),
 | 
				
			||||||
 | 
					            Err(e) => Err(DatabaseError::new(
 | 
				
			||||||
 | 
					                format!(
 | 
				
			||||||
 | 
					                    "Could not retrieve language {lang} from database: {e:?}"
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                "Database error",
 | 
				
			||||||
 | 
					            )),
 | 
				
			||||||
 | 
					            Ok(language) => {
 | 
				
			||||||
 | 
					                use userfollowlanguage::dsl;
 | 
				
			||||||
 | 
					                match diesel::insert_into(dsl::userfollowlanguage)
 | 
				
			||||||
 | 
					                    .values(UserFollowLanguageInsert { lang, userid: userid.to_string() })
 | 
				
			||||||
 | 
					                    .execute(conn) {
 | 
				
			||||||
 | 
					                        Ok(_) => Ok(language),
 | 
				
			||||||
 | 
					                        Err(e) => Err(DatabaseError::new(
 | 
				
			||||||
 | 
					                            format!("Failed to follow language {lang} as user {userid}: {e:?}"),
 | 
				
			||||||
 | 
					                            "Database Error"
 | 
				
			||||||
 | 
					                        ))
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn user_unfollow_language(
 | 
				
			||||||
 | 
					        context: &Context,
 | 
				
			||||||
 | 
					        userid: &str,
 | 
				
			||||||
 | 
					        lang: Uuid,
 | 
				
			||||||
 | 
					    ) -> Result<Language, DatabaseError> {
 | 
				
			||||||
 | 
					        use userfollowlanguage::dsl;
 | 
				
			||||||
 | 
					        let conn = &mut context.db.conn()?;
 | 
				
			||||||
 | 
					        match dsl::userfollowlanguage
 | 
				
			||||||
 | 
					            .filter(dsl::userid.eq(userid.to_string()))
 | 
				
			||||||
 | 
					            .filter(dsl::lang.eq(lang))
 | 
				
			||||||
 | 
					            .first::<UserFollowLanguage>(conn) {
 | 
				
			||||||
 | 
					                Ok(relationship) => {
 | 
				
			||||||
 | 
					                    match diesel::delete(dsl::userfollowlanguage.find(relationship.id))
 | 
				
			||||||
 | 
					                        .execute(conn) {
 | 
				
			||||||
 | 
					                            Ok(_) => Language::find(&context.db, lang),
 | 
				
			||||||
 | 
					                            Err(e) => Err(DatabaseError::new(
 | 
				
			||||||
 | 
					                                format!("Failed to make user {userid} unfollow language {lang}: {e:?}"),
 | 
				
			||||||
 | 
					                                "Database Error"
 | 
				
			||||||
 | 
					                            ))
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                Err(diesel::NotFound) => {
 | 
				
			||||||
 | 
					                    Err(DatabaseError::new(
 | 
				
			||||||
 | 
					                        format!("User {userid} does not follow language {lang}"),
 | 
				
			||||||
 | 
					                        "Invalid",
 | 
				
			||||||
 | 
					                    ))
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                Err(e) => Err(DatabaseError::new(
 | 
				
			||||||
 | 
					                    format!("Failed to retrieve relationship between user {userid} and language {lang} from database: {e:?}"),
 | 
				
			||||||
 | 
					                    "Database Error",
 | 
				
			||||||
 | 
					                ))
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -5,11 +5,11 @@ use crate::{
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
use diesel::prelude::*;
 | 
					use diesel::prelude::*;
 | 
				
			||||||
use juniper::{FieldResult, GraphQLEnum};
 | 
					use juniper::{FieldResult, GraphQLEnum};
 | 
				
			||||||
use schema::{wordrelation, words, wordlearning};
 | 
					use schema::{wordlearning, wordrelation, words};
 | 
				
			||||||
use tracing::info;
 | 
					use tracing::info;
 | 
				
			||||||
use uuid::Uuid;
 | 
					use uuid::Uuid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use std::convert::Into;
 | 
					use std::{convert::Into, str::FromStr};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::languages::Language;
 | 
					use super::languages::Language;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -20,11 +20,18 @@ pub enum WordRelationship {
 | 
				
			|||||||
    Related,
 | 
					    Related,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(diesel_derive_enum::DbEnum, Debug, Clone, PartialEq, Eq, juniper::GraphQLEnum)]
 | 
					#[derive(
 | 
				
			||||||
 | 
					    diesel_derive_enum::DbEnum,
 | 
				
			||||||
 | 
					    Debug,
 | 
				
			||||||
 | 
					    Clone,
 | 
				
			||||||
 | 
					    PartialEq,
 | 
				
			||||||
 | 
					    Eq,
 | 
				
			||||||
 | 
					    juniper::GraphQLEnum,
 | 
				
			||||||
 | 
					)]
 | 
				
			||||||
#[DieselTypePath = "crate::db::schema::sql_types::Wordlearningstatus"]
 | 
					#[DieselTypePath = "crate::db::schema::sql_types::Wordlearningstatus"]
 | 
				
			||||||
pub enum WordLearningStatus {
 | 
					pub enum WordLearningStatus {
 | 
				
			||||||
    Learning,
 | 
					    Learning,
 | 
				
			||||||
    Learned
 | 
					    Learned,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(
 | 
					#[derive(
 | 
				
			||||||
@ -54,6 +61,72 @@ pub enum PartOfSpeech {
 | 
				
			|||||||
    Other,
 | 
					    Other,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Default for PartOfSpeech {
 | 
				
			||||||
 | 
					    fn default() -> Self {
 | 
				
			||||||
 | 
					        Self::Noun
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, juniper::GraphQLInputObject)]
 | 
				
			||||||
 | 
					pub struct NewWord {
 | 
				
			||||||
 | 
					    norm: String,
 | 
				
			||||||
 | 
					    native: Option<String>,
 | 
				
			||||||
 | 
					    lemma: Option<String>,
 | 
				
			||||||
 | 
					    language: String,
 | 
				
			||||||
 | 
					    partofspeech: PartOfSpeech,
 | 
				
			||||||
 | 
					    audio: Option<String>,
 | 
				
			||||||
 | 
					    video: Option<String>,
 | 
				
			||||||
 | 
					    image: Option<String>,
 | 
				
			||||||
 | 
					    description: Option<String>,
 | 
				
			||||||
 | 
					    etymology: Option<String>,
 | 
				
			||||||
 | 
					    lusage: Option<String>,
 | 
				
			||||||
 | 
					    morphology: Option<String>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Insertable)]
 | 
				
			||||||
 | 
					#[diesel(table_name = words)]
 | 
				
			||||||
 | 
					struct NewWordInternal {
 | 
				
			||||||
 | 
					    norm: String,
 | 
				
			||||||
 | 
					    native: Option<String>,
 | 
				
			||||||
 | 
					    lemma: Option<Uuid>,
 | 
				
			||||||
 | 
					    language: Uuid,
 | 
				
			||||||
 | 
					    partofspeech: PartOfSpeech,
 | 
				
			||||||
 | 
					    audio: Option<String>,
 | 
				
			||||||
 | 
					    video: Option<String>,
 | 
				
			||||||
 | 
					    image: Option<String>,
 | 
				
			||||||
 | 
					    description: Option<String>,
 | 
				
			||||||
 | 
					    etymology: Option<String>,
 | 
				
			||||||
 | 
					    lusage: Option<String>,
 | 
				
			||||||
 | 
					    morphology: Option<String>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl TryFrom<NewWord> for NewWordInternal {
 | 
				
			||||||
 | 
					    type Error = uuid::Error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn try_from(value: NewWord) -> Result<Self, Self::Error> {
 | 
				
			||||||
 | 
					        let language = Uuid::from_str(&value.language)?;
 | 
				
			||||||
 | 
					        let lemma = if let Some(original_lemma) = value.lemma {
 | 
				
			||||||
 | 
					            Some(Uuid::from_str(&original_lemma)?)
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            None
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        Ok(Self {
 | 
				
			||||||
 | 
					            norm: value.norm,
 | 
				
			||||||
 | 
					            native: value.native,
 | 
				
			||||||
 | 
					            lemma,
 | 
				
			||||||
 | 
					            language,
 | 
				
			||||||
 | 
					            partofspeech: value.partofspeech,
 | 
				
			||||||
 | 
					            audio: value.audio,
 | 
				
			||||||
 | 
					            video: value.video,
 | 
				
			||||||
 | 
					            image: value.image,
 | 
				
			||||||
 | 
					            description: value.description,
 | 
				
			||||||
 | 
					            etymology: value.etymology,
 | 
				
			||||||
 | 
					            lusage: value.lusage,
 | 
				
			||||||
 | 
					            morphology: value.morphology,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Queryable, Insertable, Debug, Clone, PartialEq, Eq)]
 | 
					#[derive(Queryable, Insertable, Debug, Clone, PartialEq, Eq)]
 | 
				
			||||||
pub struct Word {
 | 
					pub struct Word {
 | 
				
			||||||
    id: Uuid,
 | 
					    id: Uuid,
 | 
				
			||||||
@ -229,5 +302,5 @@ pub struct WordLearning {
 | 
				
			|||||||
    pub id: i32,
 | 
					    pub id: i32,
 | 
				
			||||||
    pub word: Uuid,
 | 
					    pub word: Uuid,
 | 
				
			||||||
    pub userid: String,
 | 
					    pub userid: String,
 | 
				
			||||||
    pub status: WordLearningStatus
 | 
					    pub status: WordLearningStatus,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -27,35 +27,42 @@ impl Default for OtherEnvVar {
 | 
				
			|||||||
pub struct Context {
 | 
					pub struct Context {
 | 
				
			||||||
    pub db: Database,
 | 
					    pub db: Database,
 | 
				
			||||||
    pub appwrite: APVariables,
 | 
					    pub appwrite: APVariables,
 | 
				
			||||||
    pub user_auth: bool,
 | 
					    pub user_auth: Option<String>,
 | 
				
			||||||
    pub other_vars: OtherEnvVar,
 | 
					    pub other_vars: OtherEnvVar,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Context {
 | 
					impl Context {
 | 
				
			||||||
    /// HTTP header for a user's session
 | 
					    /// Check if a request is performed by an autentificated user.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// This header `Authorization` must be a single string in the
 | 
					    /// The HTTP header `Authorization` must be a single string in the
 | 
				
			||||||
    /// form `userId;userSessionId` with `userId` and `userSessionId`
 | 
					    /// form `userId;userSessionId` with `userId` and `userSessionId`
 | 
				
			||||||
    /// being variables given by Appwrite to users that are logged in.
 | 
					    /// being variables given by Appwrite to users that are logged in.
 | 
				
			||||||
    pub async fn user_auth<'r>(&self, auth_token: Option<&'r str>) -> bool {
 | 
					    ///
 | 
				
			||||||
 | 
					    /// The function returns either the user's ID if the user is
 | 
				
			||||||
 | 
					    /// authentified or `None`.
 | 
				
			||||||
 | 
					    pub async fn user_auth<'r>(
 | 
				
			||||||
 | 
					        &self,
 | 
				
			||||||
 | 
					        auth_token: Option<&'r str>,
 | 
				
			||||||
 | 
					    ) -> Option<String> {
 | 
				
			||||||
        if let Some(token) = auth_token {
 | 
					        if let Some(token) = auth_token {
 | 
				
			||||||
            let key = token.split(';').collect::<Vec<_>>();
 | 
					            let key = token.split(';').collect::<Vec<_>>();
 | 
				
			||||||
            if key.len() == 2 {
 | 
					            if key.len() == 2 {
 | 
				
			||||||
                let user_id = key[0];
 | 
					                let user_id = key[0];
 | 
				
			||||||
                let session_id = key[1];
 | 
					                let session_id = key[1];
 | 
				
			||||||
                match self.appwrite.check_session(session_id, user_id).await {
 | 
					                match self.appwrite.check_session(session_id, user_id).await {
 | 
				
			||||||
                    Ok(val) => val,
 | 
					                    Ok(true) => Some(key[0].to_string()),
 | 
				
			||||||
 | 
					                    Ok(false) => None,
 | 
				
			||||||
                    Err(e) => {
 | 
					                    Err(e) => {
 | 
				
			||||||
                        info!("Error checking user session: {:?}", e);
 | 
					                        info!("Error checking user session: {:?}", e);
 | 
				
			||||||
                        false
 | 
					                        None
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                info!("Invalid session key: {}", token);
 | 
					                info!("Invalid session key: {}", token);
 | 
				
			||||||
                false
 | 
					                None
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            false
 | 
					            None
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,15 @@
 | 
				
			|||||||
use juniper::FieldResult;
 | 
					use std::str::FromStr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::db::{models::users::User, DatabaseError};
 | 
					use juniper::FieldResult;
 | 
				
			||||||
 | 
					use uuid::Uuid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::db::{
 | 
				
			||||||
 | 
					    models::{
 | 
				
			||||||
 | 
					        languages::{Language, NewLanguage, UserFollowLanguage},
 | 
				
			||||||
 | 
					        users::User,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    DatabaseError,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::Context;
 | 
					use super::Context;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -9,7 +18,7 @@ pub struct Mutation;
 | 
				
			|||||||
#[juniper::graphql_object(Context = Context)]
 | 
					#[juniper::graphql_object(Context = Context)]
 | 
				
			||||||
impl Mutation {
 | 
					impl Mutation {
 | 
				
			||||||
    fn api_version(context: &Context) -> String {
 | 
					    fn api_version(context: &Context) -> String {
 | 
				
			||||||
        if context.user_auth {
 | 
					        if context.user_auth.is_some() {
 | 
				
			||||||
            "0.1 (authentified)"
 | 
					            "0.1 (authentified)"
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            "0.1 (not authentified)"
 | 
					            "0.1 (not authentified)"
 | 
				
			||||||
@ -49,4 +58,103 @@ impl Mutation {
 | 
				
			|||||||
                .into())
 | 
					                .into())
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn user_follow_language(
 | 
				
			||||||
 | 
					        context: &Context,
 | 
				
			||||||
 | 
					        language: String,
 | 
				
			||||||
 | 
					    ) -> FieldResult<Language> {
 | 
				
			||||||
 | 
					        if let Some(userid) = &context.user_auth {
 | 
				
			||||||
 | 
					            match Uuid::from_str(&language) {
 | 
				
			||||||
 | 
					                Err(e) => Err(DatabaseError::new(
 | 
				
			||||||
 | 
					                    format!(
 | 
				
			||||||
 | 
					                        "Could not parse {language} as a valid UUID: {e:?}"
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                    "Bad Request",
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					                .into()),
 | 
				
			||||||
 | 
					                Ok(lang) => UserFollowLanguage::user_follow_language(
 | 
				
			||||||
 | 
					                    context,
 | 
				
			||||||
 | 
					                    &userid.to_string(),
 | 
				
			||||||
 | 
					                    lang,
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					                .map_err(Into::into),
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            Err(DatabaseError::new(
 | 
				
			||||||
 | 
					                "User not authentificated, cannot proceed",
 | 
				
			||||||
 | 
					                "Unauthorized",
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .into())
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn user_unfollow_language(
 | 
				
			||||||
 | 
					        context: &Context,
 | 
				
			||||||
 | 
					        language: String,
 | 
				
			||||||
 | 
					    ) -> FieldResult<Language> {
 | 
				
			||||||
 | 
					        if let Some(userid) = &context.user_auth {
 | 
				
			||||||
 | 
					            match Uuid::from_str(&language) {
 | 
				
			||||||
 | 
					                Err(e) => Err(DatabaseError::new(
 | 
				
			||||||
 | 
					                    format!(
 | 
				
			||||||
 | 
					                        "Could not parse {language} as a valid UUID: {e:?}"
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                    "Bad Request",
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					                .into()),
 | 
				
			||||||
 | 
					                Ok(lang) => UserFollowLanguage::user_unfollow_language(
 | 
				
			||||||
 | 
					                    context,
 | 
				
			||||||
 | 
					                    &userid.to_string(),
 | 
				
			||||||
 | 
					                    lang,
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					                .map_err(Into::into),
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            Err(DatabaseError::new(
 | 
				
			||||||
 | 
					                "User not authentificated, cannot proceed",
 | 
				
			||||||
 | 
					                "Unauthorized",
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .into())
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn new_language(
 | 
				
			||||||
 | 
					        context: &Context,
 | 
				
			||||||
 | 
					        language: NewLanguage,
 | 
				
			||||||
 | 
					    ) -> FieldResult<Language> {
 | 
				
			||||||
 | 
					        if let Some(owner) = &context.user_auth {
 | 
				
			||||||
 | 
					            language.insert_db(&context.db, owner).map_err(Into::into)
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            Err(DatabaseError::new(
 | 
				
			||||||
 | 
					                "User not authentificated, cannot create new language",
 | 
				
			||||||
 | 
					                "Unauthorized",
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .into())
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn delete_language(
 | 
				
			||||||
 | 
					        context: &Context,
 | 
				
			||||||
 | 
					        language: String,
 | 
				
			||||||
 | 
					    ) -> FieldResult<Option<Language>> {
 | 
				
			||||||
 | 
					        if context.user_auth.is_some() {
 | 
				
			||||||
 | 
					            match Uuid::from_str(&language) {
 | 
				
			||||||
 | 
					                Ok(uuid) => Language::delete(context, uuid)
 | 
				
			||||||
 | 
					                    .map(|_| None)
 | 
				
			||||||
 | 
					                    .map_err(Into::into),
 | 
				
			||||||
 | 
					                Err(e) => Err(DatabaseError::new(
 | 
				
			||||||
 | 
					                    format!(
 | 
				
			||||||
 | 
					                        "Could not parse {language} as a valid UUID: {e:?}"
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                    "Bad Request",
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					                .into()),
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            Err(DatabaseError::new(
 | 
				
			||||||
 | 
					                "User not authentificated, cannot create new language",
 | 
				
			||||||
 | 
					                "Unauthorized",
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .into())
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user