diff --git a/migrations/20240621222501_gruppen.down.sql b/migrations/20240621222501_gruppen.down.sql index 52ee5f4..1f4224b 100644 --- a/migrations/20240621222501_gruppen.down.sql +++ b/migrations/20240621222501_gruppen.down.sql @@ -1 +1,2 @@ +DROP TYPE gruppen_herkunft; DROP TABLE gruppen; diff --git a/migrations/20240621222501_gruppen.up.sql b/migrations/20240621222501_gruppen.up.sql index bc134b2..aa762ee 100644 --- a/migrations/20240621222501_gruppen.up.sql +++ b/migrations/20240621222501_gruppen.up.sql @@ -4,3 +4,9 @@ CREATE TABLE IF NOT EXISTS gruppen ( erstellt_am TIMESTAMP WITH TIME ZONE NOT NULL, geaendert_am TIMESTAMP WITH TIME ZONE NOT NULL ) + +CREATE TYPE gruppen_herkunft AS ENUM ( + 'direkt', + 'indirekt', + 'beides' +); diff --git a/migrations/20241202221730_liegenschften.down.sql b/migrations/20241202221730_liegenschften.down.sql index 8367fba..e69de29 100644 --- a/migrations/20241202221730_liegenschften.down.sql +++ b/migrations/20241202221730_liegenschften.down.sql @@ -1,2 +0,0 @@ --- Add down migration script here -DROP TYPE gruppen_herkunft;; diff --git a/migrations/20241202221730_liegenschften.up.sql b/migrations/20241202221730_liegenschften.up.sql index c8850aa..e69de29 100644 --- a/migrations/20241202221730_liegenschften.up.sql +++ b/migrations/20241202221730_liegenschften.up.sql @@ -1,5 +0,0 @@ -CREATE TYPE gruppen_herkunft AS ENUM ( - 'direkt', - 'indirekt', - 'beides' -); diff --git a/src/database.rs b/src/database.rs index 79ca7c8..349687d 100644 --- a/src/database.rs +++ b/src/database.rs @@ -8,7 +8,8 @@ use crate::config; pub type DB = Pool; pub trait Queryer<'c>: Executor<'c, Database = sqlx::Postgres> {} -impl<'c> Queryer<'c> for &Pool {} +// impl<'c> Queryer<'c> for &Pool {} +impl<'c> Queryer<'c> for &DB {} pub async fn connect(database: &config::Database) -> Result { PgPoolOptions::new() diff --git a/src/dataloader/benutzer_gruppen_kumulativ.rs b/src/dataloader/benutzer_gruppen_kumulativ.rs index af4c56d..b23be02 100644 --- a/src/dataloader/benutzer_gruppen_kumulativ.rs +++ b/src/dataloader/benutzer_gruppen_kumulativ.rs @@ -25,10 +25,10 @@ impl Loader for BenutzerGruppenKumulativLoader { g.erstellt_am, g.geaendert_am, CASE WHEN br.benutzer_id IS NOT null THEN r.rollenname END AS rollenname, - CASE - WHEN bg.benutzer_id IS NOT NULL AND br.benutzer_id IS NOT NULL THEN 'beides'::gruppen_herkunft + CASE + WHEN bg.benutzer_id IS NOT NULL AND br.benutzer_id IS NOT NULL THEN 'beides'::gruppen_herkunft WHEN bg.benutzer_id IS NOT NULL THEN 'direkt'::gruppen_herkunft - WHEN br.benutzer_id IS NOT NULL THEN 'indirekt'::gruppen_herkunft + WHEN br.benutzer_id IS NOT NULL THEN 'indirekt'::gruppen_herkunft END AS "herkunft: Herkunft" FROM gruppen AS g LEFT JOIN @@ -41,11 +41,11 @@ impl Loader for BenutzerGruppenKumulativLoader { benutzer_rollen AS br ON rg.rolle_id = br.rolle_id - AND br.benutzer_id = ANY($1) + AND br.benutzer_id = ANY($1) LEFT JOIN rollen AS r ON rg.rolle_id = r.id WHERE - bg.benutzer_id = ANY($1) - OR br.benutzer_id = ANY($1); + bg.benutzer_id = ANY($1) + OR br.benutzer_id = ANY($1); "#, keys ) diff --git a/src/dataloader/benutzer_rollen.rs b/src/dataloader/benutzer_rollen.rs index a093e0d..ade87ab 100644 --- a/src/dataloader/benutzer_rollen.rs +++ b/src/dataloader/benutzer_rollen.rs @@ -4,7 +4,7 @@ use itertools::Itertools; use std::collections::HashMap; use std::sync::Arc; -use crate::models::rolle::Rolle; +use crate::domain::rolle::model::Rolle; use crate::scalar::Id; pub struct BenutzerRollenLoader { @@ -19,11 +19,11 @@ impl Loader for BenutzerRollenLoader { let rows = sqlx::query!( r#" SELECT - br.benutzer_id, - r.id, - r.rollenname, - r.erstellt_am, - r.geaendert_am + br.benutzer_id, + r.id, + r.rollenname, + r.erstellt_am, + r.geaendert_am FROM rollen AS r LEFT JOIN benutzer_rollen AS br ON r.id = br.rolle_id WHERE br.benutzer_id = ANY($1); diff --git a/src/dataloader/mod.rs b/src/dataloader/mod.rs index b28fc43..eade08d 100644 --- a/src/dataloader/mod.rs +++ b/src/dataloader/mod.rs @@ -1,37 +1,27 @@ -mod benutzer_gruppen; mod benutzer_gruppen_kumulativ; mod benutzer_rollen; mod rollen_gruppen; -pub use benutzer_gruppen::BenutzerGruppenLoader; +use crate::domain::{ + gruppe::dataloader::gruppen::GruppenLoader, rolle::dataloader::rollen::RollenLoader, +}; pub use benutzer_gruppen_kumulativ::BenutzerGruppenKumulativLoader; -pub use benutzer_rollen::BenutzerRollenLoader; -pub use rollen_gruppen::RollenGruppenLoader; use async_graphql::dataloader::DataLoader; pub struct LoaderContext { - pub benutzer_gruppen: DataLoader, - pub benutzer_rollen: DataLoader, - pub rollen_gruppen: DataLoader, + pub gruppen: DataLoader, + // pub benutzer_rollen: DataLoader, + pub rollen: DataLoader, pub benutzer_gruppen_kumulativ: DataLoader, } impl LoaderContext { pub fn new(pool: sqlx::PgPool) -> Self { Self { - benutzer_gruppen: DataLoader::new( - BenutzerGruppenLoader { pool: pool.clone() }, - tokio::spawn, - ), - benutzer_rollen: DataLoader::new( - BenutzerRollenLoader { pool: pool.clone() }, - tokio::spawn, - ), - rollen_gruppen: DataLoader::new( - RollenGruppenLoader { pool: pool.clone() }, - tokio::spawn, - ), + gruppen: DataLoader::new(GruppenLoader { pool: pool.clone() }, tokio::spawn), + rollen: DataLoader::new(RollenLoader { pool: pool.clone() }, tokio::spawn), + benutzer_gruppen_kumulativ: DataLoader::new( BenutzerGruppenKumulativLoader { pool: pool.clone() }, tokio::spawn, diff --git a/src/dataloader/rollen_gruppen.rs b/src/dataloader/rollen_gruppen.rs index 21ddeee..9eba755 100644 --- a/src/dataloader/rollen_gruppen.rs +++ b/src/dataloader/rollen_gruppen.rs @@ -4,7 +4,7 @@ use itertools::Itertools; use std::collections::HashMap; use std::sync::Arc; -use crate::models::gruppe::Gruppe; +use crate::domain::gruppe::model::Gruppe; use crate::scalar::Id; pub struct RollenGruppenLoader { @@ -19,11 +19,11 @@ impl Loader for RollenGruppenLoader { let rows = sqlx::query!( r#" SELECT - rg.rolle_id, - g.id, - g.gruppenname, - g.erstellt_am, - g.geaendert_am + rg.rolle_id, + g.id, + g.gruppenname, + g.erstellt_am, + g.geaendert_am FROM gruppen AS g LEFT JOIN rollen_gruppen AS rg ON g.id = rg.gruppe_id WHERE rg.rolle_id = ANY($1); diff --git a/src/domain.rs b/src/domain.rs index 6c5482c..b1f6cc1 100644 --- a/src/domain.rs +++ b/src/domain.rs @@ -1 +1,3 @@ +pub mod benutzer; +pub mod gruppe; pub mod rolle; diff --git a/src/domain/benutzer.rs b/src/domain/benutzer.rs new file mode 100644 index 0000000..03fa54a --- /dev/null +++ b/src/domain/benutzer.rs @@ -0,0 +1,5 @@ +pub mod dataloader; +pub mod entity; +pub mod model; +pub mod repository; +pub mod service; diff --git a/src/domain/benutzer/dataloader.rs b/src/domain/benutzer/dataloader.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/domain/benutzer/entity.rs b/src/domain/benutzer/entity.rs new file mode 100644 index 0000000..7191943 --- /dev/null +++ b/src/domain/benutzer/entity.rs @@ -0,0 +1,3 @@ +pub mod benutzer; + +pub use benutzer::Benutzer; diff --git a/src/domain/benutzer/entity/benutzer.rs b/src/domain/benutzer/entity/benutzer.rs new file mode 100644 index 0000000..2cb8b00 --- /dev/null +++ b/src/domain/benutzer/entity/benutzer.rs @@ -0,0 +1,8 @@ +use crate::scalar::Id; + +pub struct Benutzer { + pub id: Id, + pub kennung: String, + pub nachname: String, + pub vorname: String, +} diff --git a/src/domain/benutzer/model.rs b/src/domain/benutzer/model.rs new file mode 100644 index 0000000..8e78a54 --- /dev/null +++ b/src/domain/benutzer/model.rs @@ -0,0 +1,5 @@ +pub mod benutzer; +pub mod benutzer_create_input; + +pub use benutzer::Benutzer; +pub use benutzer_create_input::BenutzerCreateInput; diff --git a/src/models/benutzer.rs b/src/domain/benutzer/model/benutzer.rs similarity index 81% rename from src/models/benutzer.rs rename to src/domain/benutzer/model/benutzer.rs index cc2066a..3abfa8c 100644 --- a/src/models/benutzer.rs +++ b/src/domain/benutzer/model/benutzer.rs @@ -1,8 +1,11 @@ use async_graphql::{ComplexObject, Context, FieldResult, SimpleObject}; -use crate::{dataloader::LoaderContext, models::gruppe::Gruppe, models::rolle::Rolle, scalar::Id}; - -use super::gruppe_ansicht::GruppeAnsicht; +use crate::{ + dataloader::LoaderContext, + domain::{gruppe::model::Gruppe, rolle::model::Rolle}, + models::gruppe_ansicht::GruppeAnsicht, + scalar::Id, +}; #[derive(sqlx::FromRow, SimpleObject)] #[graphql(complex)] @@ -25,14 +28,15 @@ impl Benutzer { /// Die Rollen des Benutzers pub async fn rollen<'ctx>(&self, ctx: &Context<'ctx>) -> FieldResult>> { let loader = ctx.data::()?; - Ok(loader.benutzer_rollen.load_one(self.id).await?) + Ok(loader.rollen.load_one(self.id).await?) } /// Die Gruppen des Benutzers pub async fn gruppen<'ctx>(&self, ctx: &Context<'ctx>) -> FieldResult>> { let loader = ctx.data::()?; - Ok(loader.benutzer_gruppen.load_one(self.id).await?) + Ok(loader.gruppen.load_one(self.id).await?) } + /// Die Gruppen eines Benutzer kumulativ mit den Gruppen aus den Rollen pub async fn gruppen_kumulativ<'ctx>( &self, diff --git a/src/domain/benutzer/model/benutzer_create_input.rs b/src/domain/benutzer/model/benutzer_create_input.rs new file mode 100644 index 0000000..3cf69d7 --- /dev/null +++ b/src/domain/benutzer/model/benutzer_create_input.rs @@ -0,0 +1,12 @@ +use crate::scalar::Id; +use async_graphql::InputObject; + +#[derive(InputObject)] +pub struct BenutzerCreateInput { + // #[graphql(validator(min_length = 6, max_length = 8))] + pub kennung: String, + pub vorname: String, + pub nachname: String, + pub rollen: Option>, + pub gruppen: Option>, +} diff --git a/src/domain/benutzer/repository.rs b/src/domain/benutzer/repository.rs new file mode 100644 index 0000000..f919d6b --- /dev/null +++ b/src/domain/benutzer/repository.rs @@ -0,0 +1,18 @@ +mod alle_benutzer; +pub mod create_benutzer; +pub mod find_gruppen; + +#[derive(Debug, Clone)] +pub struct Repository {} + +impl Repository { + pub fn new() -> Repository { + Repository {} + } +} + +impl Default for Repository { + fn default() -> Self { + Self::new() + } +} diff --git a/src/domain/benutzer/repository/alle_benutzer.rs b/src/domain/benutzer/repository/alle_benutzer.rs new file mode 100644 index 0000000..665c961 --- /dev/null +++ b/src/domain/benutzer/repository/alle_benutzer.rs @@ -0,0 +1,23 @@ +use anyhow::Error; + +use super::Repository; +use crate::database::Queryer; +use crate::domain::benutzer::model; + +impl Repository { + pub async fn alle_benutzer<'c, C: Queryer<'c>>( + &self, + db: C, + ) -> Result, Error> { + const QUERY: &str = r#" + SELECT * + FROM benutzer + "#; + + let benutzer = sqlx::query_as::<_, model::Benutzer>(QUERY) + .fetch_all(db) + .await?; + + Ok(benutzer) + } +} diff --git a/src/domain/benutzer/repository/create_benutzer.rs b/src/domain/benutzer/repository/create_benutzer.rs new file mode 100644 index 0000000..de9e911 --- /dev/null +++ b/src/domain/benutzer/repository/create_benutzer.rs @@ -0,0 +1,31 @@ +use anyhow::Error; + +use super::Repository; +use crate::database::Queryer; +use crate::domain::benutzer::entity; +use crate::domain::benutzer::model; +use crate::scalar::Id; + +impl Repository { + pub async fn create_benutzer<'c, C: Queryer<'c>>( + &self, + db: C, + benutzer: &entity::Benutzer, + ) -> Result { + const QUERY: &str = r#" + INSERT INTO benutzer (id, kennung, nachname, vorname) + VALUES ($1, $2, $3, $4) + RETURNING id, kennung, nachname, vorname; + "#; + + let benutzer = sqlx::query_as::<_, model::Benutzer>(QUERY) + .bind::(benutzer.id) + .bind(&benutzer.kennung) + .bind(&benutzer.nachname) + .bind(&benutzer.vorname) + .fetch_one(db) + .await?; + + Ok(benutzer) + } +} diff --git a/src/domain/benutzer/repository/find_gruppen.rs b/src/domain/benutzer/repository/find_gruppen.rs new file mode 100644 index 0000000..0f2e1a3 --- /dev/null +++ b/src/domain/benutzer/repository/find_gruppen.rs @@ -0,0 +1,40 @@ +use anyhow::Error; +use async_graphql::Context; + +use crate::domain::gruppe::model::Gruppe; + +use super::Repository; + +impl Repository { + pub async fn find_gruppen<'a>(&self, ctx: &'a Context<'_>) -> Result, Error> { + // let rows = sqlx::query!( + // r#" + // SELECT + // bg.benutzer_id, + // g.id, + // g.gruppenname, + // g.erstellt_am + // LEFT JOIN benutzer_gruppen AS bg ON g.id = bg.gruppe_id + // WHERE bg.benutzer_id = ANY($1); + // "#, + // keys + // ) + // .fetch_all(&self.pool) + // .await? + // .into_iter() + // .map(|row| { + // ( + // row.benutzer_id, + // Gruppe { + // id: row.id, + // gruppenname: row.gruppenname, + // erstellt_am: row.erstellt_am, + // geaendert_am: row.geaendert_am, + // }, + // ) + // }) + // .into_group_map(); + // } + todo!() + } +} diff --git a/src/domain/benutzer/service.rs b/src/domain/benutzer/service.rs new file mode 100644 index 0000000..a16b7f7 --- /dev/null +++ b/src/domain/benutzer/service.rs @@ -0,0 +1,18 @@ +mod alle_benutzer; +pub mod create_benutzer; + +use super::repository::Repository; +use crate::database::DB; + +#[derive(Debug)] +pub struct Service { + repo: Repository, + pub db: DB, +} + +impl Service { + pub fn new(db: DB) -> Self { + let repo = Repository::new(); + Self { repo, db } + } +} diff --git a/src/domain/benutzer/service/alle_benutzer.rs b/src/domain/benutzer/service/alle_benutzer.rs new file mode 100644 index 0000000..c375674 --- /dev/null +++ b/src/domain/benutzer/service/alle_benutzer.rs @@ -0,0 +1,11 @@ +use async_graphql::FieldResult; + +use crate::domain::benutzer::model::Benutzer; + +use super::Service; + +impl Service { + pub async fn alle_benutzer(&self) -> FieldResult> { + Ok(self.repo.alle_benutzer(&self.db).await?) + } +} diff --git a/src/domain/benutzer/service/create_benutzer.rs b/src/domain/benutzer/service/create_benutzer.rs new file mode 100644 index 0000000..a5b233e --- /dev/null +++ b/src/domain/benutzer/service/create_benutzer.rs @@ -0,0 +1,25 @@ +use async_graphql::FieldResult; +use ulid::Ulid; + +use crate::domain::benutzer::{ + entity, + model::{Benutzer, BenutzerCreateInput}, +}; + +use super::Service; + +impl Service { + pub async fn create_benutzer(&self, input: BenutzerCreateInput) -> FieldResult { + let benutzer_entity = entity::Benutzer { + id: Ulid::new().into(), + kennung: input.kennung, + nachname: input.nachname, + vorname: input.vorname, + }; + + Ok(self + .repo + .create_benutzer(&self.db, &benutzer_entity) + .await?) + } +} diff --git a/src/domain/gruppe.rs b/src/domain/gruppe.rs new file mode 100644 index 0000000..03fa54a --- /dev/null +++ b/src/domain/gruppe.rs @@ -0,0 +1,5 @@ +pub mod dataloader; +pub mod entity; +pub mod model; +pub mod repository; +pub mod service; diff --git a/src/domain/gruppe/dataloader.rs b/src/domain/gruppe/dataloader.rs new file mode 100644 index 0000000..977d4e7 --- /dev/null +++ b/src/domain/gruppe/dataloader.rs @@ -0,0 +1 @@ +pub mod gruppen; diff --git a/src/domain/gruppe/dataloader/gruppen.rs b/src/domain/gruppe/dataloader/gruppen.rs new file mode 100644 index 0000000..ee6600f --- /dev/null +++ b/src/domain/gruppe/dataloader/gruppen.rs @@ -0,0 +1,24 @@ +use async_graphql::dataloader::*; +use async_graphql::*; +use std::collections::HashMap; +use std::sync::Arc; + +use crate::domain::gruppe::model::Gruppe; +use crate::domain::gruppe::service::Service; +use crate::scalar::Id; + +pub struct GruppenLoader { + pub pool: sqlx::PgPool, +} + +impl Loader for GruppenLoader { + type Value = Vec; + type Error = Arc; + + async fn load(&self, keys: &[Id]) -> Result, Self::Error> { + let rows = Service::new(self.pool.clone()) + .gruppe_dataloader(keys) + .await?; + Ok(rows) + } +} diff --git a/src/domain/gruppe/entity.rs b/src/domain/gruppe/entity.rs new file mode 100644 index 0000000..f351ba0 --- /dev/null +++ b/src/domain/gruppe/entity.rs @@ -0,0 +1,2 @@ +pub mod gruppe; +pub use gruppe::Gruppe; diff --git a/src/domain/gruppe/entity/gruppe.rs b/src/domain/gruppe/entity/gruppe.rs new file mode 100644 index 0000000..a6b4678 --- /dev/null +++ b/src/domain/gruppe/entity/gruppe.rs @@ -0,0 +1,8 @@ +use crate::scalar::{Id, Time}; + +pub struct Gruppe { + pub id: Id, + pub gruppenname: String, + pub erstellt_am: Option