ulid als scalar hinzugefügt
This commit is contained in:
1
migrations/20240530183519_init.down.sql
Normal file
1
migrations/20240530183519_init.down.sql
Normal file
@@ -0,0 +1 @@
|
||||
DROP DOMAIN ULID CASCADE;
|
||||
1
migrations/20240530183519_init.up.sql
Normal file
1
migrations/20240530183519_init.up.sql
Normal file
@@ -0,0 +1 @@
|
||||
CREATE DOMAIN ULID AS CHAR(26);
|
||||
@@ -1,6 +1,7 @@
|
||||
CREATE TABLE IF NOT EXISTS typen (
|
||||
typ_id UUID PRIMARY KEY,
|
||||
id CHAR(26) UNIQUE NOT NULL,
|
||||
-- id CHAR(26) UNIQUE NOT NULL,
|
||||
id ULID UNIQUE NOT NULL,
|
||||
typname VARCHAR NOT NULL,
|
||||
erstellt_am TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
geaendert_am TIMESTAMP WITH TIME ZONE NOT NULL
|
||||
|
||||
@@ -2,8 +2,15 @@ use async_graphql::{ComplexObject, SimpleObject};
|
||||
|
||||
use crate::scalar::{Time, Ulid};
|
||||
|
||||
#[derive(sqlx::FromRow, SimpleObject, Clone, Debug)]
|
||||
#[graphql(complex)]
|
||||
// #[derive(Debug, sqlx::FromRow)]
|
||||
// pub struct TypDb {
|
||||
// pub id: String,
|
||||
// pub typname: String,
|
||||
// pub erstellt_am: Time,
|
||||
// pub geaendert_am: Time,
|
||||
// }
|
||||
|
||||
#[derive(SimpleObject, Clone, sqlx::FromRow)]
|
||||
pub struct Typ {
|
||||
/// Die Ulid eines Gerätetypen
|
||||
pub id: Ulid,
|
||||
@@ -18,5 +25,16 @@ pub struct Typ {
|
||||
pub geaendert_am: Time,
|
||||
}
|
||||
|
||||
// impl From<TypDb> for Typ {
|
||||
// fn from(db: TypDb) -> Self {
|
||||
// Self {
|
||||
// id: Ulid(ulid::Ulid::from_string(&db.id).expect("stored ULID is valid")),
|
||||
// typname: db.typname,
|
||||
// erstellt_am: db.erstellt_am,
|
||||
// geaendert_am: db.geaendert_am,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
#[ComplexObject]
|
||||
impl Typ {}
|
||||
|
||||
@@ -11,6 +11,8 @@ impl Repository {
|
||||
|
||||
let typen = sqlx::query_as::<_, model::Typ>(QUERY).fetch_all(db).await?;
|
||||
|
||||
// let typen: Vec<model::Typ> = typen.into_iter().map(model::Typ::from).collect();
|
||||
|
||||
Ok(typen)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ impl Repository {
|
||||
.bind(id)
|
||||
.fetch_one(db)
|
||||
.await?;
|
||||
// .map(model::Typ::from)?;
|
||||
|
||||
Ok(row)
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ impl Repository {
|
||||
typen: &[entity::TypErstellen], // input: &[Typ],
|
||||
) -> Result<Vec<model::Typ>, Error> {
|
||||
let typ_id: Vec<Id> = typen.iter().map(|t| t.typ_id).collect();
|
||||
let id: Vec<Ulid> = typen.iter().map(|t| t.id.to_string()).collect();
|
||||
let id: Vec<Ulid> = typen.iter().map(|t| t.id).collect();
|
||||
let typnamen: Vec<String> = typen.iter().map(|t| t.typname.clone()).collect();
|
||||
let erstellt_am: Vec<Time> = typen.iter().map(|t| t.erstellt_am).collect();
|
||||
let geaendert_am: Vec<Time> = typen.iter().map(|t| t.geaendert_am).collect();
|
||||
@@ -38,6 +38,8 @@ impl Repository {
|
||||
.fetch_all(db)
|
||||
.await?;
|
||||
|
||||
// let rows: Vec<model::Typ> = rows.into_iter().map(model::Typ::from).collect();
|
||||
|
||||
Ok(rows)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,13 @@ impl Repository {
|
||||
|
||||
let typ = sqlx::query_as::<_, model::Typ>(QUERY)
|
||||
.bind(typ.typ_id)
|
||||
.bind(typ.id.to_string())
|
||||
.bind(typ.id)
|
||||
.bind(typ.erstellt_am)
|
||||
.bind(typ.geaendert_am)
|
||||
.bind(&typ.typname)
|
||||
.fetch_one(db)
|
||||
.await?;
|
||||
// .map(model::Typ::from)?;
|
||||
|
||||
Ok(typ)
|
||||
}
|
||||
|
||||
@@ -15,9 +15,10 @@ impl Repository {
|
||||
"#;
|
||||
|
||||
let typ = sqlx::query_as::<_, model::Typ>(QUERY)
|
||||
.bind(typ.id.to_string())
|
||||
.bind(typ.id)
|
||||
.fetch_one(db)
|
||||
.await?;
|
||||
// .map(model::Typ::from)?;
|
||||
|
||||
Ok(typ)
|
||||
}
|
||||
|
||||
@@ -17,11 +17,12 @@ impl Repository {
|
||||
"#;
|
||||
|
||||
let typ = sqlx::query_as::<_, model::Typ>(QUERY)
|
||||
.bind(typ.id.to_string())
|
||||
.bind(typ.id)
|
||||
.bind(typ.geaendert_am)
|
||||
.bind(&typ.typname)
|
||||
.fetch_one(db)
|
||||
.await?;
|
||||
// .map(model::Typ::from)?;
|
||||
|
||||
Ok(typ)
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
use anyhow::Error;
|
||||
use chrono::Utc;
|
||||
use ulid::Ulid;
|
||||
|
||||
use super::Service;
|
||||
use crate::domain::typ::{
|
||||
entity,
|
||||
model::{self, TypErstelleInput},
|
||||
};
|
||||
use crate::scalar::Ulid;
|
||||
|
||||
impl Service {
|
||||
pub async fn typ_erstellen(&self, input: TypErstelleInput) -> Result<model::Typ, Error> {
|
||||
let typ_erstellen = entity::TypErstellen {
|
||||
typ_id: Ulid::new().into(),
|
||||
id: Ulid::new().to_string(),
|
||||
typ_id: ulid::Ulid::new().into(),
|
||||
id: Ulid(ulid::Ulid::new()),
|
||||
typname: input.typname,
|
||||
erstellt_am: Utc::now(),
|
||||
geaendert_am: Utc::now(),
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
use anyhow::Error;
|
||||
use chrono::Utc;
|
||||
use ulid::Ulid;
|
||||
|
||||
use super::Service;
|
||||
use crate::domain::typ::{
|
||||
entity,
|
||||
model::{self, TypErstelleInput},
|
||||
use crate::{
|
||||
domain::typ::{
|
||||
entity,
|
||||
model::{self, TypErstelleInput},
|
||||
},
|
||||
scalar::Ulid,
|
||||
};
|
||||
|
||||
impl Service {
|
||||
@@ -16,8 +18,8 @@ impl Service {
|
||||
let typen_erstellen: Vec<entity::TypErstellen> = input
|
||||
.iter()
|
||||
.map(|t| entity::TypErstellen {
|
||||
typ_id: Ulid::new().into(),
|
||||
id: Ulid::new().to_string(),
|
||||
typ_id: ulid::Ulid::new().into(),
|
||||
id: Ulid(ulid::Ulid::new()),
|
||||
typname: t.typname.clone(),
|
||||
erstellt_am: Utc::now(),
|
||||
geaendert_am: Utc::now(),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use async_graphql::*;
|
||||
/// 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;
|
||||
@@ -9,7 +10,95 @@ pub type Time = chrono::DateTime<chrono::Utc>;
|
||||
/// 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;
|
||||
|
||||
/// ödsklfjsdf
|
||||
/// debug_assert_eq!(ödsklfjsdfasdfas
|
||||
/// ödsklfjsdfasdfasdfsa,
|
||||
pub type Ulid = String;
|
||||
// pub type Ulid = String;
|
||||
|
||||
// pub type Ulid = ulid::Ulid;
|
||||
|
||||
// #[derive(Clone, Copy, Eq, PartialEq)]
|
||||
// pub struct Ulid(pub ulid::Ulid);
|
||||
//
|
||||
// /// The ULID scalar type represents a Universally Unique Lexicographically Sortable Identifier as defined by the ULID specification.
|
||||
// /// ULIDs are 26-character strings that are URL-safe, case-insensitive, and lexicographically sortable,
|
||||
// /// making them ideal for distributed systems requiring time-ordered unique identifiers.
|
||||
// #[Scalar]
|
||||
// impl ScalarType for Ulid {
|
||||
// fn parse(value: Value) -> InputValueResult<Self> {
|
||||
// match value {
|
||||
// Value::String(s) => {
|
||||
// let ulid = ulid::Ulid::from_string(&s).map_err(InputValueError::custom)?;
|
||||
// Ok(Ulid(ulid))
|
||||
// }
|
||||
// _ => Err(InputValueError::expected_type(value)),
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// fn to_value(&self) -> Value {
|
||||
// Value::String(self.0.to_string())
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl fmt::Display for Ulid {
|
||||
// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// write!(f, "{}", self.0) // delegiert an Ulid::to_string()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
//
|
||||
use sqlx::{
|
||||
postgres::{PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueRef},
|
||||
Decode, Encode, Postgres, Type,
|
||||
};
|
||||
|
||||
// #[derive(sqlx::Type)]
|
||||
// #[sqlx(type_name = "ULID")]
|
||||
// #[sqlx(no_pg_array)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Ulid(pub ulid::Ulid);
|
||||
|
||||
#[Scalar]
|
||||
impl ScalarType for Ulid {
|
||||
fn parse(value: Value) -> InputValueResult<Self> {
|
||||
match value {
|
||||
Value::String(s) => {
|
||||
let ulid = ulid::Ulid::from_string(&s).map_err(InputValueError::custom)?;
|
||||
Ok(Ulid(ulid))
|
||||
}
|
||||
_ => Err(InputValueError::expected_type(value)),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_value(&self) -> Value {
|
||||
Value::String(self.0.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl Type<Postgres> for Ulid {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::with_name("ULID")
|
||||
}
|
||||
fn compatible(ty: &PgTypeInfo) -> bool {
|
||||
<String as Type<Postgres>>::compatible(ty)
|
||||
}
|
||||
}
|
||||
impl<'r> Decode<'r, Postgres> for Ulid {
|
||||
fn decode(value: PgValueRef<'r>) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let s = <String as Decode<Postgres>>::decode(value)?;
|
||||
|
||||
Ok(Ulid(ulid::Ulid::from_string(s.trim())?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'q> Encode<'q, Postgres> for Ulid {
|
||||
fn encode_by_ref(
|
||||
&self,
|
||||
buf: &mut PgArgumentBuffer,
|
||||
) -> Result<sqlx::encode::IsNull, Box<dyn std::error::Error + Send + Sync>> {
|
||||
<String as Encode<Postgres>>::encode(self.0.to_string(), buf)
|
||||
}
|
||||
}
|
||||
impl PgHasArrayType for Ulid {
|
||||
fn array_type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::with_name("_ULID")
|
||||
}
|
||||
}
|
||||
|
||||
23
src/sqlx_datatype.rs
Normal file
23
src/sqlx_datatype.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
// // SQLx sieht den Typ als Text (PostgreSQL: CHAR, VARCHAR, TEXT) impl Type<Postgres> for Ulid {
|
||||
// fn type_info() -> sqlx::postgres::PgTypeInfo {
|
||||
// // CHAR(26) ist in PostgreSQL intern ein Text-Typ
|
||||
// <String as Type<Postgres>>::type_info()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl<'r> Decode<'r, Postgres> for MyUlid {
|
||||
// fn decode(
|
||||
// value: sqlx::postgres::PgValueRef<'r>,
|
||||
// ) -> Result<Self, Box<dyn std::error::Error + 'static + Send + Sync>> {
|
||||
// let s = <String as Decode<Postgres>>::decode(value)?;
|
||||
// Ulid::from_string(&s)
|
||||
// .map(MyUlid)
|
||||
// .map_err(|e| format!("Invalid ULID: {}", e).into())
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl<'q> Encode<'q, Postgres> for MyUlid {
|
||||
// fn encode_by_ref(&self, buf: &mut sqlx::postgres::PgArgumentBuffer) -> sqlx::encode::IsNull {
|
||||
// <String as Encode<Postgres>>::encode(self.0.to_string(), buf)
|
||||
// }
|
||||
// }
|
||||
Reference in New Issue
Block a user