test
This commit is contained in:
Peter 2025-02-05 16:38:56 +01:00
parent 974223249c
commit 580c2e7dc9
21 changed files with 246 additions and 75 deletions

View File

@ -2,3 +2,17 @@
resolver = "3" resolver = "3"
members = ["migration", "database", "main"] members = ["migration", "database", "main"]
[workspace.dependencies]
sea-orm = { version = "1.1.4", features = [
"sqlx-postgres",
"runtime-tokio-rustls",
"macros",
] }
axum = { version = "0.8.1", features = ["json"] }
serde = { version = "1.0.217", features = ["derive"] }
uuid = { version = "1.12.1", features = ["v4", "serde"] }
tokio = { version = "1.43.0", features = ["full"] }
thiserror = { version = "2.0.11" }
dotenvy = "0.15.7"

View File

@ -13,5 +13,5 @@ sea-orm-cli generate -h
sea-orm-cli generate entity -h sea-orm-cli generate entity -h
# Generate Seaography entities # Generate Seaography entities
sea-orm-cli generate entity --output-dir ./database/src/entities --with-serde both --with-copy-enums sea-orm-cli generate entity --output-dir ./database/src/entity/ --with-serde both --with-copy-enums
``` ```

View File

@ -4,3 +4,11 @@ version = "0.1.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
migration = { path = "../migration" }
sea-orm = { workspace = true }
serde = { workspace = true }
uuid = { workspace = true }
axum = { workspace = true }
thiserror = { workspace = true }
validator = { version = "0.20.0", features = ["derive"] }

View File

@ -12,7 +12,7 @@ pub struct Model {
pub e_mail: String, pub e_mail: String,
#[sea_orm(unique)] #[sea_orm(unique)]
pub kennung: String, pub kennung: String,
pub password_hash: String, pub password_hash: Option<String>,
pub nachname: String, pub nachname: String,
pub vorname: String, pub vorname: String,
pub erstellt_am: DateTimeWithTimeZone, pub erstellt_am: DateTimeWithTimeZone,

45
database/src/error.rs Normal file
View File

@ -0,0 +1,45 @@
use axum::{
http::StatusCode,
response::{IntoResponse, Response},
};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum AppError {
#[error("Authentication required")]
Unauthorized,
#[error("Forbidden")]
Forbidden,
#[error("User not found")]
UserNotFound,
#[error("Invalid credentials")]
InvalidCredentials,
#[error("Database error")]
DbError(#[from] sea_orm::DbErr),
#[error("Validierungsfehler: {0}")]
ValidationError(#[from] validator::ValidationErrors),
#[error("Fehler in .env")]
Err(#[from] std::env::VarError),
}
impl IntoResponse for AppError {
fn into_response(self) -> Response {
let status = match self {
AppError::Unauthorized => StatusCode::UNAUTHORIZED,
AppError::Forbidden => todo!(),
AppError::UserNotFound => todo!(),
AppError::InvalidCredentials => todo!(),
AppError::ValidationError(_) => todo!(),
AppError::Err(_) => todo!(),
AppError::DbError(db_err) => todo!(),
// ... weitere Matches
};
(status, self.to_string()).into_response()
}
}

View File

@ -1,18 +1,4 @@
pub fn add(left: u64, right: u64) -> u64 { mod entity;
left + right pub mod error;
} pub mod model;
pub mod repository;
pub fn sub(left: u64, right: u64) -> u64 {
left - right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}

View File

@ -0,0 +1,42 @@
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use validator::Validate;
#[derive(Debug, Serialize, Deserialize, Validate)]
pub struct ErstelleBenutzer {
#[validate(length(min = 10, max = 255))]
pub email: String,
#[validate(length(min = 8, max = 8))]
pub kennung: String,
#[validate(length(max = 100))]
pub nachname: String,
#[validate(length(max = 100))]
pub vorname: String,
}
#[derive(Debug, Serialize, Deserialize, Validate)]
pub struct AktualisiereBenutzer {
pub id: Uuid,
#[validate(length(min = 10, max = 255))]
pub email: Option<String>,
#[validate(length(min = 8, max = 8))]
pub kennung: Option<String>,
#[validate(length(max = 100))]
pub nachname: Option<String>,
#[validate(length(max = 100))]
pub vorname: Option<String>,
pub ist_akiv: Option<bool>,
}
#[derive(Debug, Deserialize)]
pub struct LoescheBenutzer {
pub id: Uuid,
}

View File

@ -1,38 +0,0 @@
#[derive(Debug, Serialize, Deserialize, Validate)]
pub struct ErstelleBenutzerDto {
#[validate(length(min = 10, max = 255))]
pub e_mail: String,
#[validate(length(min = 8, max = 8))]
pub kennung: String,
#[validate(length(max = 100))]
pub nachname: String,
#[validate(length(max = 100))]
pub vorname: String,
}
#[derive(Debug, Serialize)]
pub struct DeviceResponse {
pub id: Uuid,
pub name: String,
pub serial_number: String,
pub description: Option<String>,
pub status: String,
pub created_at: chrono::DateTime<chrono::Utc>,
}
// Conversion from Entity to Response Model
impl From<entities::devices::Model> for DeviceResponse {
fn from(entity: entities::devices::Model) -> Self {
Self {
id: entity.id,
name: entity.name,
serial_number: entity.serial_number,
description: entity.description,
status: entity.status,
created_at: entity.created_at,
}
}
}

View File

@ -0,0 +1,90 @@
use crate::entity::benutzer::{self};
use crate::error::AppError;
use crate::model::benutzer::ErstelleBenutzer;
use axum::Json;
use sea_orm::{prelude::*, ActiveValue, DatabaseConnection, DbErr};
use validator::Validate;
pub struct BenutzerRepository {
db: DatabaseConnection,
}
impl BenutzerRepository {
pub fn new(db: DatabaseConnection) -> Self {
Self { db }
}
// Benutzer nach ID suchen
pub async fn find_by_id(&self, id: uuid::Uuid) -> Result<Option<benutzer::Model>, DbErr> {
benutzer::Entity::find_by_id(id).one(&self.db).await
}
pub async fn create_user(
&self,
Json(response): Json<ErstelleBenutzer>,
) -> Result<Json<benutzer::Model>, AppError> {
response.validate()?;
let ersteller_benutzer = benutzer::ActiveModel {
id: ActiveValue::Set(uuid::Uuid::new_v4()),
kennung: ActiveValue::Set(response.kennung),
vorname: ActiveValue::Set(response.vorname),
nachname: ActiveValue::Set(response.nachname),
e_mail: ActiveValue::Set(response.email),
..Default::default()
}
.insert(&self.db)
.await?;
Ok(Json(ersteller_benutzer))
}
// // Benutzer erstellen
// pub async fn create_user(
// &self,
// kennung: String,
// nachname: String,
// vorname: String,
// email: String,
// ) -> Result<benutzer::Model, DbErr> {
// benutzer::ActiveModel {
// id: ActiveValue::Set(uuid::Uuid::new_v4()),
// kennung: ActiveValue::Set(kennung),
// vorname: ActiveValue::Set(vorname),
// nachname: ActiveValue::Set(nachname),
// e_mail: ActiveValue::Set(email),
// ..Default::default()
// }
// .insert(&self.db)
// .await
// }
// // Benutzer aktualisieren
// pub async fn update_user(
// &self,
// id: i32,
// name: Option<String>,
// email: Option<String>,
// ) -> Result<user::Model, DbErr> {
// let user = user::Entity::find_by_id(id)
// .one(&self.db)
// .await?
// .ok_or(DbErr::Custom("User not found.".to_owned()))?;
//
// let mut user: user::ActiveModel = user.into();
// if let Some(name) = name {
// user.name = ActiveValue::Set(name);
// }
// if let Some(email) = email {
// user.email = ActiveValue::Set(email);
// }
//
// user.update(&self.db).await
// }
//
// // Benutzer löschen
// pub async fn delete_user(&self, id: i32) -> Result<(), DbErr> {
// user::Entity::delete_by_id(id).exec(&self.db).await?;
// Ok(())
// }
}

View File

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

View File

@ -5,3 +5,9 @@ edition = "2021"
[dependencies] [dependencies]
database = { path = "../database" } database = { path = "../database" }
sea-orm = { workspace = true }
tokio = { workspace = true }
thiserror = { workspace = true }
axum = { workspace = true }
dotenvy = { workspace = true }

View File

@ -1,10 +1,31 @@
use database::add; use std::env;
use database::sub;
fn main() { use axum::Json;
let test = add(2, 5); use database::repository::benutzer::BenutzerRepository;
let sub = sub(10, 5); use database::{error::AppError, model::benutzer::ErstelleBenutzer};
use sea_orm::Database;
println!("{test}"); #[tokio::main]
println!("{sub}"); async fn main() -> Result<(), AppError> {
dotenvy::dotenv().unwrap();
// Datenbankverbindung herstellen
// let root = Path::new("/");
// env::set_current_dir(root);
//
let db = Database::connect(env::var("DATABASE_URL").unwrap()).await?;
// Service initialisieren
let user_service = BenutzerRepository::new(db);
let benutzer = ErstelleBenutzer {
kennung: "12345243".into(),
nachname: "Doe".into(),
vorname: "John".into(),
email: "222H2@example.com".into(),
};
let b = user_service.create_user(Json(benutzer)).await?;
println!("{:?}", b);
Ok(())
} }

View File

@ -9,13 +9,9 @@ name = "migration"
path = "src/lib.rs" path = "src/lib.rs"
[dependencies] [dependencies]
async-std = { version = "1", features = ["attributes", "tokio1"] } async-std = { version = "1.13.0", features = ["attributes", "tokio1"] }
chrono = { version = "0.4", features = ["serde"] } chrono = { version = "0.4.39", features = ["serde"] }
[dependencies.sea-orm-migration] [dependencies.sea-orm-migration]
version = "1.1.4" version = "1.1.4"
features = [ features = ["runtime-tokio-rustls", "sqlx-postgres", "with-chrono"]
"runtime-tokio-rustls", # `ASYNC_RUNTIME` feature
"sqlx-postgres", # `DATABASE_DRIVER` feature
"with-chrono",
]

View File

@ -13,9 +13,9 @@ impl MigrationTrait for Migration {
.table(Benutzer::Table) .table(Benutzer::Table)
.if_not_exists() .if_not_exists()
.col(pk_uuid(Benutzer::Id)) .col(pk_uuid(Benutzer::Id))
.col(string(Benutzer::EMail)) .col(string(Benutzer::EMail).not_null())
.col(string(Benutzer::Kennung)) .col(string(Benutzer::Kennung))
.col(string(Benutzer::PasswordHash)) .col(string_null(Benutzer::PasswordHash))
.col(string(Benutzer::Nachname)) .col(string(Benutzer::Nachname))
.col(string(Benutzer::Vorname)) .col(string(Benutzer::Vorname))
.col( .col(
@ -28,7 +28,7 @@ impl MigrationTrait for Migration {
.not_null() .not_null()
.default(Utc::now()), .default(Utc::now()),
) )
.col(boolean(Benutzer::IstAktiv)) .col(boolean(Benutzer::IstAktiv).default(true))
.to_owned(), .to_owned(),
) )
.await?; .await?;