This commit is contained in:
Peter Schiwy
2024-06-20 14:50:36 +02:00
parent 1d60fc5a3f
commit 44931afbe7
23 changed files with 431 additions and 25 deletions

53
src/config.rs Normal file
View File

@@ -0,0 +1,53 @@
use anyhow::Error;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
pub http: Http,
pub database: Database,
pub graphql: Grapthql,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Http {
pub host: String,
pub port: u16,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Database {
pub url: String,
pub pool_size: u32,
pub max_lifetime: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Grapthql {
pub schema_location: String,
}
impl Config {
pub fn load() -> Result<Self, Error> {
dotenv::dotenv().ok();
let http = envy::prefixed("HTTP_")
.from_env::<Http>()
.expect("Failed to load http config");
let database = envy::prefixed("DATABASE_")
.from_env::<Database>()
.expect("Failed to load database config");
let graphql = envy::prefixed("GRAPHQL_")
.from_env::<Grapthql>()
.expect("Failed to load graphql config");
let config = Self {
http,
database,
graphql,
};
Ok(config)
}
}

23
src/database.rs Normal file
View File

@@ -0,0 +1,23 @@
use std::time::Duration;
use anyhow::Error;
use sqlx::{self, postgres::PgPoolOptions, Executor, Pool, Postgres};
use crate::config;
pub type DB = Pool<Postgres>;
pub trait Queryer<'c>: Executor<'c, Database = sqlx::Postgres> {}
impl<'c> Queryer<'c> for &Pool<Postgres> {}
pub async fn connect(database: &config::Database) -> Result<DB, Error> {
PgPoolOptions::new()
.max_connections(database.pool_size)
.max_lifetime(Duration::from_secs(database.max_lifetime))
.connect(&database.url)
.await
.map_err(|err| {
tracing::error!("{}", err);
err.into()
})
}

1
src/domain.rs Normal file
View File

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

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

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

View File

@@ -0,0 +1,2 @@
pub mod rolle;
pub use rolle::Rolle;

View File

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

View File

@@ -0,0 +1,5 @@
mod create_rolle_input;
mod rolle;
mod update_rolle_input;
pub use create_rolle_input::CreateRolleInput;

View File

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

View File

@@ -0,0 +1,15 @@
use async_graphql::SimpleObject;
use crate::scalar::{Id, Time};
#[derive(Debug, SimpleObject)]
pub struct Rolle {
/// Die ID einer Rolle
pub id: Id,
/// Wann die Rolle erstellt wurde
pub created_at: Time,
/// Der Name einer Rolle
pub name: String,
}

View File

@@ -0,0 +1,12 @@
use async_graphql::InputObject;
use crate::scalar::Id;
#[derive(InputObject)]
pub struct UpdateRolleInput {
/// Die ID einer Rolle
pub id: Id,
/// Der Name einer Rolle
pub name: String,
}

View File

@@ -0,0 +1,20 @@
mod create_rolle;
mod delete_rolle;
mod find_all_rolle;
mod find_rolle;
mod update_rolle;
#[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,30 @@
use anyhow::Error;
use super::Repository;
use crate::{database::Queryer, domain::rolle::entity};
impl Repository {
pub async fn create_rolle<'c, C: Queryer<'c>>(
&self,
db: C,
rolle: &entity::Rolle,
) -> Result<entity::Rolle, Error> {
const QUERY: &str = "insert into rolle (id, created_at, updated_at,
name) values ($1, $2, $3, $4) returning *";
match sqlx::query_as::<_, entity::Rolle>(QUERY)
.bind(rolle.id)
.bind(rolle.created_at)
.bind(rolle.updated_at)
.bind(&rolle.name)
.fetch_one(db)
.await
{
Err(err) => {
tracing::error!("{}", &err);
Err(err.into())
}
Ok(user) => Ok(user),
}
}
}

View File

@@ -0,0 +1,17 @@
mod create_user;
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,26 @@
use anyhow::Error;
use chrono::Utc;
use ulid::Ulid;
use super::Service;
use crate::domain::rolle::entity;
use crate::domain::rolle::model::CreateRolleInput;
impl Service {
pub async fn create_rolle(&self, input: CreateRolleInput) -> Result<entity::Rolle, Error> {
// let username_exists = self.check_username_exists(&self.db, &input.name).await?;
// if username_exists {
// return Err(Error::UsernameAlreadyExists.into());
// }
let rolle_input = entity::Rolle {
id: Ulid::new().into(),
name: input.name,
created_at: Utc::now(),
updated_at: Utc::now(),
};
let rolle = self.repo.create_rolle(&self.db, &rolle_input).await?;
Ok(rolle)
}
}

View File

@@ -8,6 +8,7 @@ use axum::{
routing::get,
Router,
};
use config::Config;
use dotenv::dotenv;
use mutations::Mutation;
use queries::Query;
@@ -15,9 +16,13 @@ use sqlx::postgres::PgPool;
use std::env;
use tokio::net::TcpListener;
mod config;
mod database;
mod domain;
mod models;
mod mutations;
mod queries;
mod scalar;
async fn ping() -> String {
format!(
@@ -36,6 +41,8 @@ async fn main() -> Result<()> {
dotenv().ok();
env_logger::init();
Config::load();
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL is not set");
let db_pool = PgPool::connect(&database_url).await?;

View File

@@ -5,9 +5,10 @@ use sqlx::{FromRow, PgPool, Type};
#[derive(SimpleObject, Debug, FromRow, Deserialize, Serialize, Type)]
pub struct Typ {
/// Die ID eines Geräte-Typs
pub id: i32,
/// Name eines Typs
/// Der Name eines Geräte-Typs
pub name: String,
}

10
src/scalar.rs Normal file
View File

@@ -0,0 +1,10 @@
/// It is easier to track each type alias if this file is located on the top-level directory (here)
/// than in each domain. Also, separating them will create a lot of duplicate code.
use uuid::Uuid;
pub type Time = chrono::DateTime<chrono::Utc>;
/// The ID scalar type represents a unique identifier, often used to refetch an object or as key for a cache.
/// The ID type appears in a JSON response as a String; however, it is not intended to be human-readable.
/// When expected as an input type, any string (such as "4") or integer (such as 4) input value will be accepted as an ID.
pub type Id = Uuid;