generated from phundrak/rust-poem-openapi-template
feat: add new crud macro for easier entity manipulation in DB
All checks were successful
CI / tests (push) Successful in 7m52s
All checks were successful
CI / tests (push) Successful in 7m52s
This commit is contained in:
@@ -9,8 +9,9 @@ serde = "1.0.215"
|
||||
tracing = "0.1.40"
|
||||
tracing-subscriber = { version = "0.3.18", features = ["fmt", "std", "env-filter", "registry", "json", "tracing-log"] }
|
||||
uuid = { version = "1.11.0", features = ["v4", "serde"] }
|
||||
gejdr-macros = { path = "../gejdr-macros" }
|
||||
|
||||
[dependencies.sqlx]
|
||||
version = "0.8.2"
|
||||
version = "0.8.3"
|
||||
default-features = false
|
||||
features = ["postgres", "uuid", "chrono", "migrate", "runtime-tokio", "macros"]
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use super::Crud;
|
||||
use sqlx::PgPool;
|
||||
|
||||
type Timestampz = chrono::DateTime<chrono::Utc>;
|
||||
@@ -17,13 +18,15 @@ impl RemoteUser {
|
||||
pub async fn refresh_in_database(self, pool: &PgPool) -> Result<User, sqlx::Error> {
|
||||
match User::find(pool, &self.id).await? {
|
||||
Some(local_user) => local_user.update_from_remote(self).update(pool).await,
|
||||
None => User::from(self).save(pool).await,
|
||||
None => User::from(self).create(pool).await,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize, Debug, PartialEq, Eq, Default, Clone)]
|
||||
#[derive(serde::Deserialize, serde::Serialize, Debug, PartialEq, Eq, Default, Clone, Crud)]
|
||||
#[crud(table = "users")]
|
||||
pub struct User {
|
||||
#[crud(id)]
|
||||
pub id: String,
|
||||
pub username: String,
|
||||
pub email: Option<String>,
|
||||
@@ -79,68 +82,6 @@ impl User {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn find(pool: &PgPool, id: &String) -> Result<Option<Self>, sqlx::Error> {
|
||||
sqlx::query_as!(Self, r#"SELECT * FROM users WHERE id = $1"#, id)
|
||||
.fetch_optional(pool)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn save(&self, pool: &PgPool) -> Result<Self, sqlx::Error> {
|
||||
sqlx::query_as!(
|
||||
Self,
|
||||
r#"
|
||||
INSERT INTO users (id, username, email, avatar, name, created_at, last_updated)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
RETURNING *
|
||||
"#,
|
||||
self.id,
|
||||
self.username,
|
||||
self.email,
|
||||
self.avatar,
|
||||
self.name,
|
||||
self.created_at,
|
||||
self.last_updated
|
||||
)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn update(&self, pool: &PgPool) -> Result<Self, sqlx::Error> {
|
||||
sqlx::query_as!(
|
||||
Self,
|
||||
r#"
|
||||
UPDATE users
|
||||
SET username = $1, email = $2, avatar = $3, name = $4, last_updated = $5
|
||||
WHERE id = $6
|
||||
RETURNING *
|
||||
"#,
|
||||
self.username,
|
||||
self.email,
|
||||
self.avatar,
|
||||
self.name,
|
||||
self.last_updated,
|
||||
self.id
|
||||
)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn save_or_update(&self, pool: &PgPool) -> Result<Self, sqlx::Error> {
|
||||
if Self::find(pool, &self.id).await?.is_some() {
|
||||
self.update(pool).await
|
||||
} else {
|
||||
self.save(pool).await
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn delete(pool: &PgPool, id: &String) -> Result<u64, sqlx::Error> {
|
||||
let rows_affected = sqlx::query!("DELETE FROM users WHERE id = $1", id)
|
||||
.execute(pool)
|
||||
.await?
|
||||
.rows_affected();
|
||||
Ok(rows_affected)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -287,7 +228,7 @@ mod tests {
|
||||
username: "user1".into(),
|
||||
..Default::default()
|
||||
};
|
||||
user.save(&pool).await?;
|
||||
user.create(&pool).await?;
|
||||
let users = sqlx::query_as!(User, "SELECT * FROM users")
|
||||
.fetch_all(&pool)
|
||||
.await?;
|
||||
@@ -328,7 +269,7 @@ mod tests {
|
||||
username: "user1".into(),
|
||||
..Default::default()
|
||||
};
|
||||
user.save_or_update(&pool).await?;
|
||||
user.create_or_update(&pool).await?;
|
||||
let rows = sqlx::query_as!(User, "SELECT * FROM users")
|
||||
.fetch_all(&pool)
|
||||
.await?;
|
||||
@@ -350,7 +291,7 @@ mod tests {
|
||||
name: Some("Cool Nam".into()),
|
||||
..Default::default()
|
||||
};
|
||||
user.save_or_update(&pool).await?;
|
||||
user.create_or_update(&pool).await?;
|
||||
let rows = sqlx::query_as!(User, "SELECT * FROM users")
|
||||
.fetch_all(&pool)
|
||||
.await?;
|
||||
@@ -369,7 +310,7 @@ mod tests {
|
||||
.await?;
|
||||
assert_eq!(2, rows.len());
|
||||
let id = "id1".to_string();
|
||||
let deletions = User::delete(&pool, &id).await?;
|
||||
let deletions = User::delete_by_id(&pool, &id).await?;
|
||||
assert_eq!(1, deletions);
|
||||
let rows = sqlx::query_as!(User, "SELECT * FROM users")
|
||||
.fetch_all(&pool)
|
||||
@@ -385,7 +326,7 @@ mod tests {
|
||||
.await?;
|
||||
assert_eq!(2, rows.len());
|
||||
let id = "invalid".to_string();
|
||||
let deletions = User::delete(&pool, &id).await?;
|
||||
let deletions = User::delete_by_id(&pool, &id).await?;
|
||||
assert_eq!(0, deletions);
|
||||
let rows = sqlx::query_as!(User, "SELECT * FROM users")
|
||||
.fetch_all(&pool)
|
||||
|
||||
@@ -1 +1,73 @@
|
||||
pub mod accounts;
|
||||
pub use gejdr_macros::Crud;
|
||||
|
||||
pub trait Crud<Id> {
|
||||
/// Find the entiy in the database based on its identifier.
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns any error Postgres may have encountered
|
||||
fn find(
|
||||
pool: &sqlx::PgPool,
|
||||
id: &Id,
|
||||
) -> impl std::future::Future<Output = sqlx::Result<Option<Self>>> + Send
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
/// Create the entity in the database.
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns any error Postgres may have encountered
|
||||
fn create(
|
||||
&self,
|
||||
pool: &sqlx::PgPool,
|
||||
) -> impl std::future::Future<Output = sqlx::Result<Self>> + Send
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
/// Update an entity with a matching identifier in the database.
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns any error Postgres may have encountered
|
||||
fn update(
|
||||
&self,
|
||||
pool: &sqlx::PgPool,
|
||||
) -> impl std::future::Future<Output = sqlx::Result<Self>> + Send
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
/// Update an entity with a matching identifier in the database if
|
||||
/// it exists, create it otherwise.
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns any error Postgres may have encountered
|
||||
fn create_or_update(
|
||||
&self,
|
||||
pool: &sqlx::PgPool,
|
||||
) -> impl std::future::Future<Output = sqlx::Result<Self>> + Send
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
/// Delete the entity from the database if it exists.
|
||||
///
|
||||
/// # Returns
|
||||
/// Returns the amount of rows affected by the deletion.
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns any error Postgres may have encountered
|
||||
fn delete(
|
||||
&self,
|
||||
pool: &sqlx::PgPool,
|
||||
) -> impl std::future::Future<Output = sqlx::Result<u64>> + Send;
|
||||
|
||||
/// Delete any entity with the identifier `id`.
|
||||
///
|
||||
/// # Returns
|
||||
/// Returns the amount of rows affected by the deletion.
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns any error Postgres may have encountered
|
||||
fn delete_by_id(
|
||||
pool: &sqlx::PgPool,
|
||||
id: &Id,
|
||||
) -> impl std::future::Future<Output = sqlx::Result<u64>> + Send;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user