use diesel::prelude::*; use juniper::{graphql_object, EmptyMutation, EmptySubscription, FieldResult, RootNode}; use r2d2_redis::redis; use std::ops::DerefMut; use crate::{cache, db}; #[derive(Clone)] pub struct Context; impl juniper::Context for Context {} pub struct Schoolyear { pub model: db::models::Schoolyear, } #[graphql_object] impl Schoolyear { fn id(&self) -> i32 { self.model.id } fn untis_id(&self) -> i32 { self.model.untis_id } fn name(&self) -> &str { self.model.name.as_str() } fn start_date(&self) -> chrono::NaiveDate { self.model.start_date } fn end_date(&self) -> chrono::NaiveDate { self.model.end_date } } pub struct Teacher { pub model: db::models::Teacher, } #[graphql_object] impl Teacher { fn id(&self) -> i32 { self.model.id } fn untis_id(&self) -> i32 { self.model.untis_id } fn schoolyear(&self) -> FieldResult { let db_conn = &mut db::POOL.get()?; Ok(Schoolyear { model: db::schema::schoolyears::table .filter(db::schema::schoolyears::id.eq(self.model.schoolyear_id)) .first::(db_conn)?, }) } fn name(&self) -> &str { &self.model.name } fn forename(&self) -> Option<&str> { self.model.forename.as_deref() } fn display_name(&self) -> &str { &self.model.display_name } } pub struct Class { pub model: db::models::Class, } #[graphql_object] impl Class { fn id(&self) -> i32 { self.model.id } fn untis_id(&self) -> i32 { self.model.untis_id } fn schoolyear(&self) -> FieldResult { let db_conn = &mut db::POOL.get()?; Ok(Schoolyear { model: db::schema::schoolyears::table .filter(db::schema::schoolyears::id.eq(self.model.schoolyear_id)) .first::(db_conn)?, }) } fn name(&self) -> &str { &self.model.name } fn long_name(&self) -> &str { &self.model.long_name } fn active(&self) -> bool { self.model.active } } pub struct SubstitutionClass { pub model: db::models::SubstitutionClass, } #[graphql_object] impl SubstitutionClass { fn id(&self) -> i32 { self.model.id } fn substitution(&self) -> FieldResult { let db_conn = &mut db::POOL.get()?; Ok(Substitution { model: db::schema::substitutions::table .filter(db::schema::substitutions::id.eq(self.model.substitution_id)) .first::(db_conn)?, }) } fn position(&self) -> i32 { self.model.position as i32 } fn class(&self) -> FieldResult { let db_conn = &mut db::POOL.get()?; Ok(Class { model: db::schema::classes::table .filter(db::schema::classes::id.eq(self.model.class_id)) .first::(db_conn)?, }) } } pub struct SubstitutionTeacher { pub model: db::models::SubstitutionTeacher, } #[graphql_object] impl SubstitutionTeacher { fn id(&self) -> i32 { self.model.id } fn substitution(&self) -> FieldResult { let db_conn = &mut db::POOL.get()?; Ok(Substitution { model: db::schema::substitutions::table .filter(db::schema::substitutions::id.eq(self.model.substitution_id)) .first::(db_conn)?, }) } fn position(&self) -> i32 { self.model.position as i32 } fn teacher(&self) -> FieldResult> { if let Some(teacher_id) = self.model.teacher_id { let db_conn = &mut db::POOL.get()?; Ok(Some(Teacher { model: db::schema::teachers::table .filter(db::schema::teachers::id.eq(teacher_id)) .first::(db_conn)?, })) } else { Ok(None) } } } pub struct Substitution { pub model: db::models::Substitution, } #[graphql_object] impl Substitution { fn id(&self) -> i32 { self.model.id } fn substitution_query(&self) -> FieldResult { let db_conn = &mut db::POOL.get()?; Ok(SubstitutionQuery { model: db::schema::substitution_queries::table .filter(db::schema::substitution_queries::id.eq(self.model.substitution_query_id)) .first::(db_conn)?, }) } fn subst_type(&self) -> db::models::SubstitutionType { self.model.subst_type.to_owned() } fn lesson_id(&self) -> i32 { self.model.lesson_id } fn start_time(&self) -> chrono::NaiveTime { self.model.start_time } fn end_time(&self) -> chrono::NaiveTime { self.model.end_time } fn text(&self) -> Option<&str> { self.model.text.as_deref() } fn classes(&self) -> FieldResult> { let db_conn = &mut db::POOL.get()?; Ok(db::schema::substitution_classes::table .filter(db::schema::substitution_classes::substitution_id.eq(self.model.id)) .load::(db_conn)? .into_iter() .map(|x| SubstitutionClass { model: x }) .collect()) } } pub struct SubstitutionQuery { pub model: db::models::SubstitutionQuery, } #[graphql_object] impl SubstitutionQuery { fn id(&self) -> i32 { self.model.id } fn schoolyear(&self) -> FieldResult { let db_conn = &mut db::POOL.get()?; Ok(Schoolyear { model: db::schema::schoolyears::table .filter(db::schema::schoolyears::id.eq(self.model.schoolyear_id)) .first::(db_conn)?, }) } fn date(&self) -> chrono::NaiveDate { self.model.date } fn week_type(&self) -> db::models::WeekType { self.model.week_type.to_owned() } fn queried_at(&self) -> chrono::NaiveDateTime { self.model.queried_at } fn substitutions(&self) -> FieldResult> { let db_conn = &mut db::POOL.get()?; Ok(db::schema::substitutions::table .filter(db::schema::substitutions::substitution_query_id.eq(self.model.id)) .load::(db_conn)? .into_iter() .map(|x| Substitution { model: x }) .collect()) } } pub struct Query; #[graphql_object(context = Context)] impl Query { fn ping() -> &'static str { "pong" } fn last_substitution_query() -> FieldResult { let db_conn = &mut db::POOL.get()?; let redis_conn = &mut cache::POOL.get()?; let id = redis::cmd("GET") .arg(cache::keys::LAST_SUBSTITUTION_QUERY_ID) .query::>(redis_conn.deref_mut())? .map_or_else( || { db::schema::substitution_queries::table .select(db::schema::substitution_queries::id) .order(db::schema::substitution_queries::queried_at.desc()) .first::(db_conn) }, |x| Ok(x), )?; let last_query = db::schema::substitution_queries::table .filter(db::schema::substitution_queries::id.eq(id)) .first::(db_conn)?; Ok(SubstitutionQuery { model: last_query }) } } pub type Schema = RootNode<'static, Query, EmptyMutation, EmptySubscription>; pub fn schema() -> Schema { Schema::new( Query, EmptyMutation::::new(), EmptySubscription::::new(), ) }