use diesel::prelude::*; use juniper::{graphql_object, EmptySubscription, FieldResult, IntoFieldError, RootNode}; use std::fs; use uuidv7::Uuid; use crate::{db, prune_many, prune_single, CONFIG}; pub mod context; pub mod error; pub mod loaders; pub mod models; pub mod scalars; pub use context::Context; pub use error::{Error, QueryResultIntoFieldResult}; use loaders::TryOptionLoad; pub struct Query; #[graphql_object(context = Context)] impl Query { fn ping() -> &'static str { "pong" } async fn directories(context: &Context) -> FieldResult> { let db_conn = &mut context.get_db_conn()?; let ids: Vec = db::schema::directories::table .select(db::schema::directories::id) .filter(db::schema::directories::active) .load(db_conn) .into_field_result()?; context .loaders .directory .try_load_many(ids) .await .map_or_else( |_| Err(Error::Internal.into_field_error()), |x| Ok(x.into_values().collect()), ) } async fn directory( context: &Context, id: scalars::Uuid, ) -> FieldResult> { context .loaders .directory .try_option_load(*id) .await .map_err(|_| Error::Internal.into_field_error()) } } pub struct Mutation; #[graphql_object(context = Context)] impl Mutation { async fn create_directory(context: &Context) -> FieldResult { let db_conn = &mut context.get_db_conn()?; let id = diesel::insert_into(db::schema::directories::table) .values(db::models::NewDirectory { active: true }) .returning(db::schema::directories::id) .get_result::(db_conn) .into_field_result()?; fs::create_dir(format!("{}/{}", CONFIG.data_dir, id)) .map_err(|_| Error::Internal.into_field_error())?; context .loaders .directory .try_load(id) .await .map_err(|_| Error::Internal.into_field_error()) } async fn create_directories( context: &Context, count: i32, ) -> FieldResult> { match count { _ if count < 0 => return Err(Error::CountNegative.into_field_error()), 0 => return Ok(vec![]), _ => {} } let db_conn = &mut context.get_db_conn()?; let input = vec![db::models::NewDirectory { active: true }]; let ids = diesel::insert_into(db::schema::directories::table) .values( input .iter() .cycle() .take(count as usize) .collect::>(), ) .returning(db::schema::directories::id) .load::(db_conn) .into_field_result()?; for id in ids.iter() { fs::create_dir(format!("{}/{}", CONFIG.data_dir, id)) .map_err(|_| Error::Internal.into_field_error())?; } context .loaders .directory .try_load_many(ids) .await .map_or_else( |_| Err(Error::Internal.into_field_error()), |x| Ok(x.into_values().collect()), ) } async fn delete_directory( context: &Context, id: scalars::Uuid, immediate: Option, ) -> FieldResult { let db_conn = &mut context.get_db_conn()?; if diesel::select(diesel::dsl::not(diesel::dsl::exists( db::schema::directories::table.filter(db::schema::directories::id.eq(*id)), ))) .get_result::(db_conn) .into_field_result()? { return Err(Error::DoesNotExist.into_field_error()); } if immediate.unwrap_or(false) { prune_single(&id.to_string()).map_err(|_| Error::Internal.into_field_error())?; diesel::delete( db::schema::directories::table.filter(db::schema::directories::id.eq(*id)), ) .execute(db_conn) .into_field_result()?; } else { diesel::update( db::schema::directories::table.filter(db::schema::directories::id.eq(*id)), ) .set(db::schema::directories::active.eq(false)) .execute(db_conn) .into_field_result()?; } Ok(true) } async fn delete_directories( context: &Context, ids: Vec, immediate: Option, ) -> FieldResult { let db_conn = &mut context.get_db_conn()?; let ids: Vec = ids.into_iter().map(|id| *id).collect(); let count: i64 = db::schema::directories::table .filter(db::schema::directories::id.eq_any(&ids)) .count() .get_result::(db_conn) .into_field_result()?; dbg!(&count); if count == ids.len() as i64 { if immediate.unwrap_or(false) { prune_many(&ids.iter().map(|id| id.to_string()).collect::>())?; diesel::delete( db::schema::directories::table.filter(db::schema::directories::id.eq_any(ids)), ) .execute(db_conn) .into_field_result()?; } else { diesel::update( db::schema::directories::table.filter(db::schema::directories::id.eq_any(ids)), ) .set(db::schema::directories::active.eq(false)) .execute(db_conn) .into_field_result()?; } } else { return Err(Error::DoesNotExist.into_field_error()); } Ok(true) } async fn prune(context: &Context) -> FieldResult { let db_conn = &mut context.get_db_conn()?; crate::prune(db_conn).map_err(|_| Error::Internal.into_field_error())?; Ok(true) } } pub type Schema = RootNode<'static, Query, Mutation, EmptySubscription>; pub fn schema() -> Schema { Schema::new(Query, Mutation, EmptySubscription::new()) }