create gruppen, rollen, etc...

This commit is contained in:
Peter Schiwy 2024-12-06 14:06:27 +01:00
parent bfce29c8ee
commit 623d28f77b
63 changed files with 735 additions and 185 deletions

View File

@ -1 +1,2 @@
DROP TYPE gruppen_herkunft;
DROP TABLE gruppen; DROP TABLE gruppen;

View File

@ -4,3 +4,9 @@ CREATE TABLE IF NOT EXISTS gruppen (
erstellt_am TIMESTAMP WITH TIME ZONE NOT NULL, erstellt_am TIMESTAMP WITH TIME ZONE NOT NULL,
geaendert_am TIMESTAMP WITH TIME ZONE NOT NULL geaendert_am TIMESTAMP WITH TIME ZONE NOT NULL
) )
CREATE TYPE gruppen_herkunft AS ENUM (
'direkt',
'indirekt',
'beides'
);

View File

@ -1,2 +0,0 @@
-- Add down migration script here
DROP TYPE gruppen_herkunft;;

View File

@ -1,5 +0,0 @@
CREATE TYPE gruppen_herkunft AS ENUM (
'direkt',
'indirekt',
'beides'
);

View File

@ -8,7 +8,8 @@ use crate::config;
pub type DB = Pool<Postgres>; pub type DB = Pool<Postgres>;
pub trait Queryer<'c>: Executor<'c, Database = sqlx::Postgres> {} pub trait Queryer<'c>: Executor<'c, Database = sqlx::Postgres> {}
impl<'c> Queryer<'c> for &Pool<Postgres> {} // impl<'c> Queryer<'c> for &Pool<Postgres> {}
impl<'c> Queryer<'c> for &DB {}
pub async fn connect(database: &config::Database) -> Result<DB, Error> { pub async fn connect(database: &config::Database) -> Result<DB, Error> {
PgPoolOptions::new() PgPoolOptions::new()

View File

@ -4,7 +4,7 @@ use itertools::Itertools;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use crate::models::rolle::Rolle; use crate::domain::rolle::model::Rolle;
use crate::scalar::Id; use crate::scalar::Id;
pub struct BenutzerRollenLoader { pub struct BenutzerRollenLoader {

View File

@ -1,37 +1,27 @@
mod benutzer_gruppen;
mod benutzer_gruppen_kumulativ; mod benutzer_gruppen_kumulativ;
mod benutzer_rollen; mod benutzer_rollen;
mod rollen_gruppen; 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_gruppen_kumulativ::BenutzerGruppenKumulativLoader;
pub use benutzer_rollen::BenutzerRollenLoader;
pub use rollen_gruppen::RollenGruppenLoader;
use async_graphql::dataloader::DataLoader; use async_graphql::dataloader::DataLoader;
pub struct LoaderContext { pub struct LoaderContext {
pub benutzer_gruppen: DataLoader<BenutzerGruppenLoader>, pub gruppen: DataLoader<GruppenLoader>,
pub benutzer_rollen: DataLoader<BenutzerRollenLoader>, // pub benutzer_rollen: DataLoader<BenutzerRollenLoader>,
pub rollen_gruppen: DataLoader<RollenGruppenLoader>, pub rollen: DataLoader<RollenLoader>,
pub benutzer_gruppen_kumulativ: DataLoader<BenutzerGruppenKumulativLoader>, pub benutzer_gruppen_kumulativ: DataLoader<BenutzerGruppenKumulativLoader>,
} }
impl LoaderContext { impl LoaderContext {
pub fn new(pool: sqlx::PgPool) -> Self { pub fn new(pool: sqlx::PgPool) -> Self {
Self { Self {
benutzer_gruppen: DataLoader::new( gruppen: DataLoader::new(GruppenLoader { pool: pool.clone() }, tokio::spawn),
BenutzerGruppenLoader { pool: pool.clone() }, rollen: DataLoader::new(RollenLoader { pool: pool.clone() }, tokio::spawn),
tokio::spawn,
),
benutzer_rollen: DataLoader::new(
BenutzerRollenLoader { pool: pool.clone() },
tokio::spawn,
),
rollen_gruppen: DataLoader::new(
RollenGruppenLoader { pool: pool.clone() },
tokio::spawn,
),
benutzer_gruppen_kumulativ: DataLoader::new( benutzer_gruppen_kumulativ: DataLoader::new(
BenutzerGruppenKumulativLoader { pool: pool.clone() }, BenutzerGruppenKumulativLoader { pool: pool.clone() },
tokio::spawn, tokio::spawn,

View File

@ -4,7 +4,7 @@ use itertools::Itertools;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use crate::models::gruppe::Gruppe; use crate::domain::gruppe::model::Gruppe;
use crate::scalar::Id; use crate::scalar::Id;
pub struct RollenGruppenLoader { pub struct RollenGruppenLoader {

View File

@ -1 +1,3 @@
pub mod benutzer;
pub mod gruppe;
pub mod rolle; pub mod rolle;

5
src/domain/benutzer.rs Normal file
View File

@ -0,0 +1,5 @@
pub mod dataloader;
pub mod entity;
pub mod model;
pub mod repository;
pub mod service;

View File

View File

@ -0,0 +1,3 @@
pub mod benutzer;
pub use benutzer::Benutzer;

View File

@ -0,0 +1,8 @@
use crate::scalar::Id;
pub struct Benutzer {
pub id: Id,
pub kennung: String,
pub nachname: String,
pub vorname: String,
}

View File

@ -0,0 +1,5 @@
pub mod benutzer;
pub mod benutzer_create_input;
pub use benutzer::Benutzer;
pub use benutzer_create_input::BenutzerCreateInput;

View File

@ -1,8 +1,11 @@
use async_graphql::{ComplexObject, Context, FieldResult, SimpleObject}; use async_graphql::{ComplexObject, Context, FieldResult, SimpleObject};
use crate::{dataloader::LoaderContext, models::gruppe::Gruppe, models::rolle::Rolle, scalar::Id}; use crate::{
dataloader::LoaderContext,
use super::gruppe_ansicht::GruppeAnsicht; domain::{gruppe::model::Gruppe, rolle::model::Rolle},
models::gruppe_ansicht::GruppeAnsicht,
scalar::Id,
};
#[derive(sqlx::FromRow, SimpleObject)] #[derive(sqlx::FromRow, SimpleObject)]
#[graphql(complex)] #[graphql(complex)]
@ -25,14 +28,15 @@ impl Benutzer {
/// Die Rollen des Benutzers /// Die Rollen des Benutzers
pub async fn rollen<'ctx>(&self, ctx: &Context<'ctx>) -> FieldResult<Option<Vec<Rolle>>> { pub async fn rollen<'ctx>(&self, ctx: &Context<'ctx>) -> FieldResult<Option<Vec<Rolle>>> {
let loader = ctx.data::<LoaderContext>()?; let loader = ctx.data::<LoaderContext>()?;
Ok(loader.benutzer_rollen.load_one(self.id).await?) Ok(loader.rollen.load_one(self.id).await?)
} }
/// Die Gruppen des Benutzers /// Die Gruppen des Benutzers
pub async fn gruppen<'ctx>(&self, ctx: &Context<'ctx>) -> FieldResult<Option<Vec<Gruppe>>> { pub async fn gruppen<'ctx>(&self, ctx: &Context<'ctx>) -> FieldResult<Option<Vec<Gruppe>>> {
let loader = ctx.data::<LoaderContext>()?; let loader = ctx.data::<LoaderContext>()?;
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 /// Die Gruppen eines Benutzer kumulativ mit den Gruppen aus den Rollen
pub async fn gruppen_kumulativ<'ctx>( pub async fn gruppen_kumulativ<'ctx>(
&self, &self,

View File

@ -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<Vec<Id>>,
pub gruppen: Option<Vec<Id>>,
}

View File

@ -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()
}
}

View File

@ -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<Vec<model::Benutzer>, Error> {
const QUERY: &str = r#"
SELECT *
FROM benutzer
"#;
let benutzer = sqlx::query_as::<_, model::Benutzer>(QUERY)
.fetch_all(db)
.await?;
Ok(benutzer)
}
}

View File

@ -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<model::Benutzer, Error> {
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::<Id>(benutzer.id)
.bind(&benutzer.kennung)
.bind(&benutzer.nachname)
.bind(&benutzer.vorname)
.fetch_one(db)
.await?;
Ok(benutzer)
}
}

View File

@ -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<Vec<Gruppe>, 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!()
}
}

View File

@ -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 }
}
}

View File

@ -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<Vec<Benutzer>> {
Ok(self.repo.alle_benutzer(&self.db).await?)
}
}

View File

@ -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<Benutzer> {
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?)
}
}

5
src/domain/gruppe.rs Normal file
View File

@ -0,0 +1,5 @@
pub mod dataloader;
pub mod entity;
pub mod model;
pub mod repository;
pub mod service;

View File

@ -0,0 +1 @@
pub mod gruppen;

View File

@ -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<Id> for GruppenLoader {
type Value = Vec<Gruppe>;
type Error = Arc<sqlx::Error>;
async fn load(&self, keys: &[Id]) -> Result<HashMap<Id, Self::Value>, Self::Error> {
let rows = Service::new(self.pool.clone())
.gruppe_dataloader(keys)
.await?;
Ok(rows)
}
}

View File

@ -0,0 +1,2 @@
pub mod gruppe;
pub use gruppe::Gruppe;

View File

@ -0,0 +1,8 @@
use crate::scalar::{Id, Time};
pub struct Gruppe {
pub id: Id,
pub gruppenname: String,
pub erstellt_am: Option<Time>,
pub geaendert_am: Option<Time>,
}

View File

@ -0,0 +1,7 @@
pub mod gruppe;
pub mod gruppe_erstelle_input;
pub mod gruppe_update_input;
pub use gruppe::Gruppe;
pub use gruppe_erstelle_input::GruppeErstelleInput;
pub use gruppe_update_input::GruppeUpdateInput;

View File

@ -0,0 +1,7 @@
use async_graphql::InputObject;
#[derive(InputObject)]
pub struct GruppeErstelleInput {
/// Der Name einer Gruppe
pub gruppenname: String,
}

View File

@ -0,0 +1,7 @@
use async_graphql::InputObject;
#[derive(InputObject)]
pub struct GruppeUpdateInput {
/// Der Name einer Gruppe
pub gruppenname: String,
}

View File

@ -0,0 +1,19 @@
mod gruppe_alle;
mod gruppe_dataloader;
mod gruppe_erstellen;
mod gruppe_update;
#[derive(Debug, Clone)]
pub struct Repository {}
impl Repository {
pub fn new() -> Repository {
Repository {}
}
}
impl Default for Repository {
fn default() -> Self {
Self::new()
}
}

View File

@ -0,0 +1,21 @@
use anyhow::Error;
use super::Repository;
use crate::{database::Queryer, domain::gruppe::model};
impl Repository {
pub async fn gruppe_alle<'c, C: Queryer<'c>>(
&self,
db: C,
) -> Result<Vec<model::Gruppe>, Error> {
const QUERY: &str = r#"
SELECT * FROM gruppen
"#;
let gruppen = sqlx::query_as::<_, model::Gruppe>(QUERY)
.fetch_all(db)
.await?;
Ok(gruppen)
}
}

View File

@ -0,0 +1,49 @@
use std::collections::HashMap;
use std::sync::Arc;
use itertools::Itertools;
use super::Repository;
use crate::database::Queryer;
use crate::domain::gruppe::model::Gruppe;
use crate::scalar::Id;
impl Repository {
pub async fn gruppe_dataloader<'c, C: Queryer<'c>>(
&self,
db: C,
keys: &[Id],
) -> Result<HashMap<Id, Vec<Gruppe>>, Arc<sqlx::Error>> {
let rows = sqlx::query!(
r#"
SELECT
bg.benutzer_id,
g.id,
g.gruppenname,
g.erstellt_am,
g.geaendert_am
FROM gruppen AS g
LEFT JOIN benutzer_gruppen AS bg ON g.id = bg.gruppe_id
WHERE bg.benutzer_id = ANY($1);
"#,
keys
)
.fetch_all(db)
.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();
Ok(rows)
}
}

View File

@ -0,0 +1,29 @@
use anyhow::Error;
use super::Repository;
use crate::database::Queryer;
use crate::domain::gruppe::{entity, model};
impl Repository {
pub async fn gruppe_erstellen<'c, C: Queryer<'c>>(
&self,
db: C,
gruppe: &entity::Gruppe,
) -> Result<model::Gruppe, Error> {
const QUERY: &str = r#"
INSERT INTO gruppen (id, erstellt_am, geaendert_am, gruppenname) VALUES (
$1, $2, $3, $4
) RETURNING id, erstellt_am, geaendert_am, gruppenname;
"#;
let gruppe = sqlx::query_as::<_, model::Gruppe>(QUERY)
.bind(gruppe.id)
.bind(gruppe.erstellt_am)
.bind(gruppe.geaendert_am)
.bind(&gruppe.gruppenname)
.fetch_one(db)
.await?;
Ok(gruppe)
}
}

View File

@ -0,0 +1,28 @@
use anyhow::Error;
use super::Repository;
use crate::database::Queryer;
use crate::domain::gruppe::{entity, model};
impl Repository {
pub async fn gruppe_update<'c, C: Queryer<'c>>(
&self,
db: C,
gruppe: &entity::Gruppe,
) -> Result<model::Gruppe, Error> {
const QUERY: &str = r#"
UPDATE gruppen
SET geaendert_am = $2, ruppenname = $3 WHERE gruppeid = $1
RETURNING id, geaendert_am, gruppenname, gruppenname;
"#;
let gruppe = sqlx::query_as::<_, model::Gruppe>(QUERY)
.bind(gruppe.id)
.bind(gruppe.geaendert_am)
.bind(&gruppe.gruppenname)
.fetch_one(db)
.await?;
Ok(gruppe)
}
}

View File

@ -0,0 +1,20 @@
mod gruppe_alle;
mod gruppe_dataloader;
mod gruppe_erstellen;
mod gruppe_update;
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 }
}
}

View File

@ -0,0 +1,12 @@
use anyhow::Error;
use crate::domain::gruppe::model;
use super::Service;
impl Service {
pub async fn gruppe_alle(&self) -> Result<Vec<model::Gruppe>, Error> {
let gruppe = self.repo.gruppe_alle(&self.db).await?;
Ok(gruppe)
}
}

View File

@ -0,0 +1,15 @@
use std::{collections::HashMap, sync::Arc};
use crate::{domain::gruppe::model::Gruppe, scalar::Id};
use super::Service;
impl Service {
pub async fn gruppe_dataloader(
&self,
keys: &[Id],
) -> Result<HashMap<Id, Vec<Gruppe>>, Arc<sqlx::Error>> {
let grupen_dataloader = self.repo.gruppe_dataloader(&self.db, keys).await?;
Ok(grupen_dataloader)
}
}

View File

@ -0,0 +1,26 @@
use anyhow::Error;
use chrono::Utc;
use ulid::Ulid;
use super::Service;
use crate::domain::gruppe::{
entity,
model::{self, GruppeErstelleInput},
};
impl Service {
pub async fn gruppe_erstellen(
&self,
input: GruppeErstelleInput,
) -> Result<model::Gruppe, Error> {
let gruppe_input = entity::Gruppe {
id: Ulid::new().into(),
gruppenname: input.gruppenname,
erstellt_am: Some(Utc::now()),
geaendert_am: Some(Utc::now()),
};
let gruppe = self.repo.gruppe_erstellen(&self.db, &gruppe_input).await?;
Ok(gruppe)
}
}

View File

@ -0,0 +1,23 @@
use anyhow::Error;
use chrono::Utc;
use ulid::Ulid;
use super::Service;
use crate::domain::gruppe::{
entity,
model::{self, GruppeUpdateInput},
};
impl Service {
pub async fn gruppe_update(&self, input: GruppeUpdateInput) -> Result<model::Gruppe, Error> {
let gruppe_input = entity::Gruppe {
id: Ulid::new().into(),
gruppenname: input.gruppenname,
erstellt_am: None,
geaendert_am: Some(Utc::now()),
};
let gruppe = self.repo.gruppe_erstellen(&self.db, &gruppe_input).await?;
Ok(gruppe)
}
}

View File

@ -1,5 +1,5 @@
pub mod dataloader;
pub mod entity; pub mod entity;
pub mod model; pub mod model;
pub mod mutation;
pub mod repository; pub mod repository;
pub mod service; pub mod service;

View File

@ -0,0 +1 @@
pub mod rollen;

View File

@ -4,29 +4,29 @@ use itertools::Itertools;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use crate::models::gruppe::Gruppe; use crate::domain::rolle::model::Rolle;
use crate::scalar::Id; use crate::scalar::Id;
pub struct BenutzerGruppenLoader { pub struct RollenLoader {
pub pool: sqlx::PgPool, pub pool: sqlx::PgPool,
} }
impl Loader<Id> for BenutzerGruppenLoader { impl Loader<Id> for RollenLoader {
type Value = Vec<Gruppe>; type Value = Vec<Rolle>;
type Error = Arc<sqlx::Error>; type Error = Arc<sqlx::Error>;
async fn load(&self, keys: &[Id]) -> Result<HashMap<Id, Self::Value>, Self::Error> { async fn load(&self, keys: &[Id]) -> Result<HashMap<Id, Self::Value>, Self::Error> {
let rows = sqlx::query!( let rows = sqlx::query!(
r#" r#"
SELECT SELECT
bg.benutzer_id, br.benutzer_id,
g.id, r.id,
g.gruppenname, r.rollenname,
g.erstellt_am, r.erstellt_am,
g.geaendert_am r.geaendert_am
FROM gruppen AS g FROM rollen AS r
LEFT JOIN benutzer_gruppen AS bg ON g.id = bg.gruppe_id LEFT JOIN benutzer_rollen AS br ON r.id = br.rolle_id
WHERE bg.benutzer_id = ANY($1); WHERE br.benutzer_id = ANY($1);
"#, "#,
keys keys
) )
@ -36,9 +36,9 @@ impl Loader<Id> for BenutzerGruppenLoader {
.map(|row| { .map(|row| {
( (
row.benutzer_id, row.benutzer_id,
Gruppe { Rolle {
id: row.id, id: row.id,
gruppenname: row.gruppenname, rollenname: row.rollenname,
erstellt_am: row.erstellt_am, erstellt_am: row.erstellt_am,
geaendert_am: row.geaendert_am, geaendert_am: row.geaendert_am,
}, },

View File

@ -1,9 +1,8 @@
use crate::scalar::{Id, Time}; use crate::scalar::{Id, Time};
#[derive(sqlx::FromRow)]
pub struct Rolle { pub struct Rolle {
pub id: Id, pub id: Id,
pub created_at: Time,
pub updated_at: Time,
pub rollenname: String, pub rollenname: String,
pub erstellt_am: Time,
pub geaendert_am: Time,
} }

View File

@ -2,6 +2,5 @@ mod rolle;
mod rolle_create_input; mod rolle_create_input;
mod rolle_update_input; mod rolle_update_input;
pub use rolle::Gruppe;
pub use rolle::Rolle; pub use rolle::Rolle;
pub use rolle_create_input::RolleCreateInput; pub use rolle_create_input::RolleCreateInput;

View File

@ -1,36 +1,33 @@
use async_graphql::SimpleObject; use async_graphql::{ComplexObject, Context, FieldResult, SimpleObject};
use crate::scalar::{Id, Time}; use crate::{
dataloader::LoaderContext,
domain::gruppe::model::Gruppe,
scalar::{Id, Time},
};
#[derive(Debug, SimpleObject, sqlx::FromRow)] /// Um die Administration zu erleichtern werden Gruppen in die Rollen hinzugefuegt
#[derive(sqlx::FromRow, SimpleObject, Debug, Clone)]
#[graphql(complex)]
pub struct Rolle { pub struct Rolle {
/// Die ID einer Rolle /// Die uuid einer Rolle
pub id: Id, pub id: Id,
/// Zeit wann die Rolle erstellt wurde /// Der Rollenname
pub created_at: Time,
/// Zeit wann die Rolle geändert wurde
pub updated_at: Time,
/// Der Name einer Rolle
pub rollenname: String, pub rollenname: String,
pub gruppen: Vec<Gruppe>, /// Wann die Rolle erstellt wurde
pub erstellt_am: Time,
/// Wann die Rolle geaendert wurde
pub geaendert_am: Time,
} }
/// TODO: Migration in ein separates Modul #[ComplexObject]
#[derive(Debug, SimpleObject, sqlx::Type)] impl Rolle {
pub struct Gruppe { /// Die Gruppen in einer Rolle
/// Die ID einer Gruppe pub async fn gruppen<'ctx>(&self, ctx: &Context<'ctx>) -> FieldResult<Option<Vec<Gruppe>>> {
pub id: Id, let loader = ctx.data::<LoaderContext>()?;
Ok(loader.gruppen.load_one(self.id).await?)
/// Zeit wann die Gruppe erstellt wurde }
pub created_at: Time,
///Zeit wann die Gruppe geändert wurde
pub updated_at: Time,
/// Der Name einer Gruppe
pub gruppenname: String,
} }

View File

@ -2,6 +2,7 @@ mod create_rolle;
mod delete_rolle; mod delete_rolle;
mod find_all_rolle; mod find_all_rolle;
mod find_rolle_by_id; mod find_rolle_by_id;
mod rollen_dataloader;
mod update_rolle; mod update_rolle;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View File

@ -16,8 +16,8 @@ impl Repository {
match sqlx::query_scalar::<_, Id>(QUERY) match sqlx::query_scalar::<_, Id>(QUERY)
.bind(rolle.id) .bind(rolle.id)
.bind(rolle.created_at) .bind(rolle.erstellt_am)
.bind(rolle.updated_at) .bind(rolle.geaendert_am)
.bind(&rolle.rollenname) .bind(&rolle.rollenname)
.fetch_one(db) .fetch_one(db)
.await .await

View File

@ -0,0 +1,49 @@
use std::collections::HashMap;
use std::sync::Arc;
use itertools::Itertools;
use super::Repository;
use crate::database::Queryer;
use crate::domain::rolle::model::Rolle;
use crate::scalar::Id;
impl Repository {
pub async fn rollen_dataloader<'c, C: Queryer<'c>>(
&self,
db: C,
keys: &[Id],
) -> Result<HashMap<Id, Vec<Rolle>>, Arc<sqlx::Error>> {
let rows = sqlx::query!(
r#"
SELECT
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);
"#,
keys
)
.fetch_all(db)
.await?
.into_iter()
.map(|row| {
(
row.benutzer_id,
Rolle {
id: row.id,
rollenname: row.rollenname,
erstellt_am: row.erstellt_am,
geaendert_am: row.geaendert_am,
},
)
})
.into_group_map();
Ok(rows)
}
}

View File

@ -16,8 +16,8 @@ impl Service {
let rolle_input = entity::Rolle { let rolle_input = entity::Rolle {
id: Ulid::new().into(), id: Ulid::new().into(),
rollenname: input.rollenname, rollenname: input.rollenname,
created_at: Utc::now(), erstellt_am: Utc::now(),
updated_at: Utc::now(), geaendert_am: Utc::now(),
}; };
let created_id = self.repo.create_rolle(&self.db, &rolle_input).await?; let created_id = self.repo.create_rolle(&self.db, &rolle_input).await?;

View File

@ -1,24 +0,0 @@
SELECT DISTINCT
COALESCE(bg.benutzer_id, br.benutzer_id) AS benutzer_id,
g.id AS gruppe_id,
g.gruppenname,
g.erstellt_am,
g.geaendert_am,
CASE WHEN br.benutzer_id IS NOT null THEN r.rollenname END
AS rollenname
FROM gruppen AS g
LEFT JOIN
benutzer_gruppen AS bg
ON
g.id = bg.gruppe_id
AND bg.benutzer_id = ANY($1)
LEFT JOIN rollen_gruppen AS rg ON g.id = rg.gruppe_id
LEFT JOIN
benutzer_rollen AS br
ON
rg.rolle_id = br.rolle_id
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);

View File

@ -1,7 +1,4 @@
pub mod benutzer;
pub mod gruppe;
pub mod gruppe_ansicht; pub mod gruppe_ansicht;
pub mod hersteller; pub mod hersteller;
pub mod modell; pub mod modell;
pub mod rolle;
pub mod typ; pub mod typ;

View File

@ -1,54 +0,0 @@
use async_graphql::{ComplexObject, Context, FieldResult, SimpleObject};
use crate::{
dataloader::LoaderContext,
models::gruppe::Gruppe,
scalar::{Id, Time},
};
/// Um die Administration zu erleichtern werden Gruppen in die Rollen hinzugefuegt
#[derive(sqlx::FromRow, SimpleObject, Debug, Clone)]
#[graphql(complex)]
pub struct Rolle {
/// Die uuid einer Rolle
pub id: Id,
/// Der Rollenname
pub rollenname: String,
/// Wann die Rolle erstellt wurde
pub erstellt_am: Time,
/// Wann die Rolle geaendert wurde
pub geaendert_am: Time,
}
#[ComplexObject]
impl Rolle {
/// Die Gruppen in einer Rolle
pub async fn gruppen<'ctx>(&self, ctx: &Context<'ctx>) -> FieldResult<Option<Vec<Gruppe>>> {
let loader = ctx.data::<LoaderContext>()?;
Ok(loader.rollen_gruppen.load_one(self.id).await?)
//
// let pool = ctx.data::<PgPool>()?;
//
// let rows = sqlx::query_as!(
// Gruppe,
// r#"
// SELECT
// 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 = $1;
// "#,
// &self.id
// )
// .fetch_all(pool)
// .await?;
//
// Ok(rows)
}
}

36
src/mutations/benutzer.rs Normal file
View File

@ -0,0 +1,36 @@
use crate::domain::benutzer::{model::Benutzer, model::BenutzerCreateInput, service::Service};
use async_graphql::{Context, FieldResult};
use sqlx::postgres::PgPool;
#[derive(Default)]
pub struct BenutzerMutation;
#[async_graphql::Object]
impl BenutzerMutation {
async fn create_benutzer(
&self,
ctx: &Context<'_>,
input: BenutzerCreateInput,
) -> FieldResult<Benutzer> {
let pool = ctx.data::<PgPool>()?;
let row = Service::new(pool.clone()).create_benutzer(input).await?;
Ok(row)
}
// async fn update_hersteller(
// &self,
// ctx: &Context<'_>,
// input: HerstellerUpdateInput,
// ) -> FieldResult<Hersteller> {
// let pool = ctx.data::<PgPool>()?;
// let row = Hersteller::update(pool, &input).await?;
// Ok(row)
// }
// async fn delete_hersteller(&self, ctx: &Context<'_>, id: Id) -> FieldResult<bool> {
// let pool = ctx.data::<PgPool>()?;
// Ok(Hersteller::delete(pool, &id).await?)
// }
}

23
src/mutations/gruppe.rs Normal file
View File

@ -0,0 +1,23 @@
use crate::domain::gruppe::{
model::{Gruppe, GruppeErstelleInput},
service::Service,
};
use async_graphql::{Context, FieldResult};
use sqlx::postgres::PgPool;
#[derive(Default)]
pub struct GruppeMutation;
#[async_graphql::Object]
impl GruppeMutation {
async fn gruppe_ersellen(
&self,
ctx: &Context<'_>,
input: GruppeErstelleInput,
) -> FieldResult<Gruppe> {
let pool = ctx.data::<PgPool>()?;
let gruppe = Service::new(pool.clone()).gruppe_erstellen(input).await?;
Ok(gruppe)
}
}

View File

@ -1,3 +1,5 @@
pub mod benutzer;
pub mod gruppe;
pub mod hersteller; pub mod hersteller;
pub mod modell; pub mod modell;
pub mod typ; pub mod typ;
@ -9,4 +11,6 @@ pub struct Mutation(
typ::TypMutation, typ::TypMutation,
hersteller::HerstellerMutation, hersteller::HerstellerMutation,
modell::ModellMutation, modell::ModellMutation,
benutzer::BenutzerMutation,
gruppe::GruppeMutation,
); );

View File

@ -1,7 +1,7 @@
use async_graphql::{Context, FieldResult, Object}; use async_graphql::{Context, FieldResult, Object};
use sqlx::postgres::PgPool; use sqlx::postgres::PgPool;
use crate::models::benutzer::Benutzer; use crate::domain::benutzer::{model::Benutzer, service::Service};
#[derive(Default)] #[derive(Default)]
pub struct BenutzerQuery {} pub struct BenutzerQuery {}
@ -17,9 +17,7 @@ impl BenutzerQuery {
async fn benutzer_alle<'a>(&self, ctx: &'a Context<'_>) -> FieldResult<Vec<Benutzer>> { async fn benutzer_alle<'a>(&self, ctx: &'a Context<'_>) -> FieldResult<Vec<Benutzer>> {
let pool = ctx.data::<PgPool>()?; let pool = ctx.data::<PgPool>()?;
let benutzer = sqlx::query_as!(Benutzer, "SELECT * FROM benutzer") let benutzer = Service::new(pool.clone()).alle_benutzer().await?;
.fetch_all(pool)
.await?;
Ok(benutzer) Ok(benutzer)
} }

23
src/queries/gruppe.rs Normal file
View File

@ -0,0 +1,23 @@
use async_graphql::{Context, FieldResult, Object};
use sqlx::postgres::PgPool;
use crate::domain::gruppe::{model::Gruppe, service::Service};
#[derive(Default)]
pub struct GruppeQuery {}
#[Object(extends)]
impl GruppeQuery {
async fn gruppe<'a>(&self, ctx: &'a Context<'_>, id: uuid::Uuid) -> FieldResult<Gruppe> {
todo!();
// let pool = ctx.data::<PgPool>()?;
//
// Ok(row)
}
async fn gruppen<'a>(&self, ctx: &'a Context<'_>) -> FieldResult<Vec<Gruppe>> {
let pool = ctx.data::<PgPool>()?;
let rows = Service::new(pool.clone()).gruppe_alle().await?;
Ok(rows)
}
}

View File

@ -1,4 +1,5 @@
pub mod benutzer; pub mod benutzer;
pub mod gruppe;
pub mod hersteller; pub mod hersteller;
pub mod modell; pub mod modell;
pub mod rolle; pub mod rolle;
@ -13,4 +14,5 @@ pub struct Query(
hersteller::HerstellerQuery, hersteller::HerstellerQuery,
benutzer::BenutzerQuery, benutzer::BenutzerQuery,
rolle::RolleQuery, rolle::RolleQuery,
gruppe::GruppeQuery,
); );

View File

@ -1,7 +1,7 @@
use async_graphql::{Context, FieldResult, Object}; use async_graphql::{Context, FieldResult, Object};
use sqlx::postgres::PgPool; use sqlx::postgres::PgPool;
use crate::models::rolle::Rolle; use crate::domain::rolle::model::Rolle;
#[derive(Default)] #[derive(Default)]
pub struct RolleQuery {} pub struct RolleQuery {}