mirror of
https://github.com/System-End/plura.git
synced 2026-04-19 20:55:11 +00:00
feat(models): update models to use error-stack types
This makes a bunch of errors more meaningful and also helps with typing
This commit is contained in:
parent
518a6864b7
commit
d58c6a69d6
12 changed files with 177 additions and 122 deletions
22
Cargo.lock
generated
22
Cargo.lock
generated
|
|
@ -574,6 +574,26 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_more"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678"
|
||||||
|
dependencies = [
|
||||||
|
"derive_more-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_more-impl"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.101",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.10.7"
|
version = "0.10.7"
|
||||||
|
|
@ -2409,10 +2429,10 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"axum",
|
"axum",
|
||||||
"clap",
|
"clap",
|
||||||
|
"derive_more",
|
||||||
"displaydoc",
|
"displaydoc",
|
||||||
"dotenvy 0.15.7 (git+https://github.com/allan2/dotenvy)",
|
"dotenvy 0.15.7 (git+https://github.com/allan2/dotenvy)",
|
||||||
"error-stack",
|
"error-stack",
|
||||||
"eyre",
|
|
||||||
"http-body-util",
|
"http-body-util",
|
||||||
"libsqlite3-sys",
|
"libsqlite3-sys",
|
||||||
"menv",
|
"menv",
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ error-stack = { version = "0.5.0", features = [
|
||||||
"serde",
|
"serde",
|
||||||
"spantrace",
|
"spantrace",
|
||||||
] }
|
] }
|
||||||
eyre = "0.6.12"
|
|
||||||
http-body-util = "0.1.3"
|
http-body-util = "0.1.3"
|
||||||
menv = "0.2.7"
|
menv = "0.2.7"
|
||||||
oauth2 = "5.0.0"
|
oauth2 = "5.0.0"
|
||||||
|
|
@ -42,6 +41,7 @@ dotenvy = { git = "https://github.com/allan2/dotenvy", features = ["macros"] }
|
||||||
url = "2.5.4"
|
url = "2.5.4"
|
||||||
serde_json = "1.0.140"
|
serde_json = "1.0.140"
|
||||||
tower-http = { version = "0.6.6", features = ["trace"] }
|
tower-http = { version = "0.6.6", features = ["trace"] }
|
||||||
|
derive_more = { version = "2.0.1", features = ["from"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
encrypt = ["libsqlite3-sys/bundled-sqlcipher"]
|
encrypt = ["libsqlite3-sys/bundled-sqlcipher"]
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use error_stack::{Result, ResultExt, report};
|
use error_stack::{Result, ResultExt};
|
||||||
use slack_morphism::prelude::*;
|
use slack_morphism::prelude::*;
|
||||||
use tracing::{debug, info, trace};
|
use tracing::{debug, info, trace};
|
||||||
|
|
||||||
|
|
@ -125,7 +125,7 @@ impl Member {
|
||||||
member::Id::new(member_id)
|
member::Id::new(member_id)
|
||||||
.validate_by_system(system.id, &user_state.db)
|
.validate_by_system(system.id, &user_state.db)
|
||||||
.await
|
.await
|
||||||
.ok()
|
.change_context(CommandError::Sqlx)?
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!(target_member_id = ?new_active_member_id, "Changing active member");
|
debug!(target_member_id = ?new_active_member_id, "Changing active member");
|
||||||
|
|
@ -143,13 +143,13 @@ impl Member {
|
||||||
info!("Successfully switched to base account");
|
info!("Successfully switched to base account");
|
||||||
"Switched to base account".into()
|
"Switched to base account".into()
|
||||||
}
|
}
|
||||||
Err(ChangeActiveMemberError::MemberNotFound) => {
|
Err(e) => match e.current_context() {
|
||||||
debug!("Requested member not found in system");
|
ChangeActiveMemberError::MemberNotFound => {
|
||||||
"The member you gave doesn't exist!".into()
|
debug!("Requested member not found in system");
|
||||||
}
|
"The member you gave doesn't exist!".into()
|
||||||
Err(ChangeActiveMemberError::Sqlx(err)) => {
|
}
|
||||||
return Err(report!(err).change_context(CommandError::Sqlx));
|
ChangeActiveMemberError::Sqlx => return Err(e.change_context(CommandError::Sqlx)),
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(SlackCommandEventResponse::new(
|
Ok(SlackCommandEventResponse::new(
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,15 @@ use std::sync::Arc;
|
||||||
mod member;
|
mod member;
|
||||||
mod system;
|
mod system;
|
||||||
mod trigger;
|
mod trigger;
|
||||||
|
|
||||||
use axum::{Extension, Json};
|
use axum::{Extension, Json};
|
||||||
use clap::{Parser, error::ErrorKind};
|
use clap::{Parser, error::ErrorKind};
|
||||||
use error_stack::ResultExt;
|
use error_stack::ResultExt;
|
||||||
use member::Member;
|
|
||||||
|
|
||||||
use slack_morphism::prelude::*;
|
use slack_morphism::prelude::*;
|
||||||
use system::System;
|
|
||||||
use tracing::{Level, debug, error, trace};
|
use tracing::{Level, debug, error, trace};
|
||||||
|
|
||||||
|
use member::Member;
|
||||||
|
use system::System;
|
||||||
use trigger::Trigger;
|
use trigger::Trigger;
|
||||||
|
|
||||||
use crate::fields;
|
use crate::fields;
|
||||||
|
|
|
||||||
|
|
@ -199,9 +199,10 @@ impl Trigger {
|
||||||
let member_id = member::Id::new(member_id);
|
let member_id = member::Id::new(member_id);
|
||||||
|
|
||||||
// Validate the member belongs to the user's system
|
// Validate the member belongs to the user's system
|
||||||
let Ok(member_id) = member_id
|
let Some(member_id) = member_id
|
||||||
.validate_by_system(system_id, &user_state.db)
|
.validate_by_system(system_id, &user_state.db)
|
||||||
.await
|
.await
|
||||||
|
.change_context(CommandError::Sqlx)?
|
||||||
else {
|
else {
|
||||||
debug!("Member not found");
|
debug!("Member not found");
|
||||||
return Ok(SlackCommandEventResponse::new(
|
return Ok(SlackCommandEventResponse::new(
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ use std::error::Error;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use axum::Extension;
|
use axum::Extension;
|
||||||
|
use error_stack::ResultExt;
|
||||||
use member::{create_member, edit_member};
|
use member::{create_member, edit_member};
|
||||||
use slack_morphism::prelude::*;
|
use slack_morphism::prelude::*;
|
||||||
use tracing::{debug, error};
|
use tracing::{debug, error};
|
||||||
|
|
@ -114,7 +115,8 @@ async fn handle_modal_view(
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
let Ok(trusted_member_id) = member_id.validate_by_user(&user_id, &user_state.db).await
|
let Some(trusted_member_id) =
|
||||||
|
member_id.validate_by_user(&user_id, &user_state.db).await?
|
||||||
else {
|
else {
|
||||||
error!(
|
error!(
|
||||||
id,
|
id,
|
||||||
|
|
@ -137,7 +139,8 @@ async fn handle_modal_view(
|
||||||
.map(models::member::Id::new)
|
.map(models::member::Id::new)
|
||||||
.expect("Failed to parse member id from external id");
|
.expect("Failed to parse member id from external id");
|
||||||
|
|
||||||
let Ok(trusted_member_id) = member_id.validate_by_user(&user_id, &user_state.db).await
|
let Some(trusted_member_id) =
|
||||||
|
member_id.validate_by_user(&user_id, &user_state.db).await?
|
||||||
else {
|
else {
|
||||||
error!(
|
error!(
|
||||||
id,
|
id,
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
use crate::id;
|
use crate::id;
|
||||||
|
|
||||||
use super::{Trustability, Trusted, Untrusted, member, system};
|
use super::{Trustability, Trusted, Untrusted, member, system};
|
||||||
use error_stack::ResultExt;
|
use error_stack::{Result, ResultExt};
|
||||||
use slack_morphism::prelude::*;
|
use slack_morphism::prelude::*;
|
||||||
use sqlx::{SqlitePool, prelude::*};
|
use sqlx::{SqlitePool, prelude::*, sqlite::SqliteQueryResult};
|
||||||
use tracing::{debug, warn};
|
use tracing::{debug, warn};
|
||||||
|
|
||||||
id!(
|
id!(
|
||||||
|
|
@ -26,30 +26,24 @@ impl Id<Untrusted> {
|
||||||
self,
|
self,
|
||||||
system_id: system::Id<Trusted>,
|
system_id: system::Id<Trusted>,
|
||||||
db: &SqlitePool,
|
db: &SqlitePool,
|
||||||
) -> Result<Id<Trusted>, Self> {
|
) -> Result<Option<Id<Trusted>>, sqlx::Error> {
|
||||||
let exists = sqlx::query!(
|
sqlx::query!(
|
||||||
"SELECT EXISTS(SELECT 1 FROM aliases WHERE id = $1 AND system_id = $2) AS 'exists: bool'",
|
"SELECT
|
||||||
|
id as 'id: Id<Trusted>'
|
||||||
|
FROM aliases
|
||||||
|
WHERE id = $1 AND system_id = $2",
|
||||||
self.id,
|
self.id,
|
||||||
system_id.id
|
system_id.id
|
||||||
)
|
)
|
||||||
.fetch_one(db)
|
.fetch_optional(db)
|
||||||
.await
|
.await
|
||||||
.ok()
|
.map(|res| res.map(|res| res.id))
|
||||||
.is_some_and(|record| record.exists);
|
.attach_printable("Failed to fetch alias id from database")
|
||||||
|
|
||||||
if exists {
|
|
||||||
Ok(Id {
|
|
||||||
id: self.id,
|
|
||||||
trusted: std::marker::PhantomData,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Err(self)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Id<Trusted> {
|
impl Id<Trusted> {
|
||||||
pub async fn delete(self, db_pool: &SqlitePool) -> Result<(), sqlx::Error> {
|
pub async fn delete(self, db_pool: &SqlitePool) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
r#"
|
r#"
|
||||||
DELETE FROM aliases
|
DELETE FROM aliases
|
||||||
|
|
@ -59,7 +53,7 @@ impl Id<Trusted> {
|
||||||
)
|
)
|
||||||
.execute(db_pool)
|
.execute(db_pool)
|
||||||
.await
|
.await
|
||||||
.map(|_| ())
|
.attach_printable("Failed to delete alias from database")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -99,6 +93,7 @@ impl Alias {
|
||||||
)
|
)
|
||||||
.fetch_optional(db)
|
.fetch_optional(db)
|
||||||
.await
|
.await
|
||||||
|
.attach_printable("Failed to fetch alias from database")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn fetch_by_system_id(
|
pub async fn fetch_by_system_id(
|
||||||
|
|
@ -122,12 +117,13 @@ impl Alias {
|
||||||
)
|
)
|
||||||
.fetch_all(db)
|
.fetch_all(db)
|
||||||
.await
|
.await
|
||||||
|
.attach_printable("Failed to fetch aliases from database")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn fetch_by_member_id(
|
pub async fn fetch_by_member_id(
|
||||||
db: &SqlitePool,
|
db: &SqlitePool,
|
||||||
member_id: member::Id<Trusted>,
|
member_id: member::Id<Trusted>,
|
||||||
) -> error_stack::Result<Vec<Self>, Error> {
|
) -> error_stack::Result<Vec<Self>, sqlx::Error> {
|
||||||
sqlx::query_as!(
|
sqlx::query_as!(
|
||||||
Self,
|
Self,
|
||||||
r#"
|
r#"
|
||||||
|
|
@ -144,7 +140,7 @@ impl Alias {
|
||||||
)
|
)
|
||||||
.fetch_all(db)
|
.fetch_all(db)
|
||||||
.await
|
.await
|
||||||
.change_context(Error::Sqlx)
|
.attach_printable("Failed to fetch aliases from database")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -188,7 +184,7 @@ impl View {
|
||||||
system_id: system::Id<Trusted>,
|
system_id: system::Id<Trusted>,
|
||||||
member_id: member::Id<Trusted>,
|
member_id: member::Id<Trusted>,
|
||||||
db_pool: &SqlitePool,
|
db_pool: &SqlitePool,
|
||||||
) -> error_stack::Result<Id<Trusted>, Error> {
|
) -> Result<Id<Trusted>, sqlx::Error> {
|
||||||
debug!(
|
debug!(
|
||||||
"Adding alias for {} (Member ID {}) to database",
|
"Adding alias for {} (Member ID {}) to database",
|
||||||
system_id, member_id
|
system_id, member_id
|
||||||
|
|
@ -207,7 +203,6 @@ impl View {
|
||||||
.fetch_one(db_pool)
|
.fetch_one(db_pool)
|
||||||
.await
|
.await
|
||||||
.attach_printable("Error adding alias to database")
|
.attach_printable("Error adding alias to database")
|
||||||
.change_context(Error::Sqlx)
|
|
||||||
.map(|row| Id {
|
.map(|row| Id {
|
||||||
id: row.id,
|
id: row.id,
|
||||||
trusted: std::marker::PhantomData,
|
trusted: std::marker::PhantomData,
|
||||||
|
|
@ -219,7 +214,7 @@ impl View {
|
||||||
&self,
|
&self,
|
||||||
alias_id: Id<Trusted>,
|
alias_id: Id<Trusted>,
|
||||||
db: &SqlitePool,
|
db: &SqlitePool,
|
||||||
) -> error_stack::Result<(), Error> {
|
) -> error_stack::Result<SqliteQueryResult, sqlx::Error> {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
r#"
|
r#"
|
||||||
UPDATE aliases
|
UPDATE aliases
|
||||||
|
|
@ -232,8 +227,6 @@ impl View {
|
||||||
.execute(db)
|
.execute(db)
|
||||||
.await
|
.await
|
||||||
.attach_printable("Error updating member alias in database")
|
.attach_printable("Error updating member alias in database")
|
||||||
.change_context(Error::Sqlx)
|
|
||||||
.map(|_| ())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_add_view(self, member_id: member::Id<Trusted>) -> SlackView {
|
pub fn create_add_view(self, member_id: member::Id<Trusted>) -> SlackView {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use error_stack::ResultExt;
|
use error_stack::{Result, ResultExt};
|
||||||
use slack_morphism::prelude::*;
|
use slack_morphism::prelude::*;
|
||||||
use sqlx::{SqlitePool, prelude::*, sqlite::SqliteQueryResult};
|
use sqlx::{SqlitePool, prelude::*, sqlite::SqliteQueryResult};
|
||||||
use tracing::{debug, warn};
|
use tracing::{debug, warn};
|
||||||
|
|
@ -7,14 +7,14 @@ use crate::id;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
Trusted, Untrusted, system,
|
Trusted, Untrusted, system,
|
||||||
trigger::{self, Trigger, Type},
|
trigger::{Trigger, Type},
|
||||||
user,
|
user,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(thiserror::Error, displaydoc::Display, Debug)]
|
#[derive(thiserror::Error, displaydoc::Display, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Error while calling the database
|
/// Error while calling the database
|
||||||
Sqlx,
|
Database,
|
||||||
/// A field was missing from the view
|
/// A field was missing from the view
|
||||||
MissingField(String),
|
MissingField(String),
|
||||||
}
|
}
|
||||||
|
|
@ -39,67 +39,95 @@ impl Id<Untrusted> {
|
||||||
self,
|
self,
|
||||||
system_id: system::Id<Trusted>,
|
system_id: system::Id<Trusted>,
|
||||||
db: &SqlitePool,
|
db: &SqlitePool,
|
||||||
) -> Result<Id<Trusted>, Self> {
|
) -> Result<Option<Id<Trusted>>, sqlx::Error> {
|
||||||
let exists = sqlx::query!(
|
sqlx::query!(
|
||||||
"SELECT EXISTS(SELECT 1 FROM members WHERE id = $1 AND system_id = $2) AS 'exists: bool'",
|
"SELECT
|
||||||
|
id as 'id: Id<Trusted>'
|
||||||
|
FROM members
|
||||||
|
WHERE id = $1 AND system_id = $2",
|
||||||
self.id,
|
self.id,
|
||||||
system_id.id
|
system_id.id
|
||||||
)
|
)
|
||||||
.fetch_one(db)
|
.fetch_optional(db)
|
||||||
.await
|
.await
|
||||||
.ok()
|
.attach_printable("Failed to validate member by system")
|
||||||
.is_some_and(|record| record.exists);
|
.map(|res| res.map(|res| res.id))
|
||||||
|
|
||||||
if exists {
|
|
||||||
Ok(Id {
|
|
||||||
id: self.id,
|
|
||||||
trusted: std::marker::PhantomData,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Err(self)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn validate_by_user(
|
pub async fn validate_by_user(
|
||||||
self,
|
self,
|
||||||
user_id: &user::Id<Trusted>,
|
user_id: &user::Id<Trusted>,
|
||||||
db: &SqlitePool,
|
db: &SqlitePool,
|
||||||
) -> Result<Id<Trusted>, Self> {
|
) -> Result<Option<Id<Trusted>>, sqlx::Error> {
|
||||||
let exists = sqlx::query!(
|
sqlx::query!(
|
||||||
"SELECT EXISTS(
|
"
|
||||||
SELECT 1
|
SELECT
|
||||||
|
members.id as 'id: Id<Trusted>'
|
||||||
FROM members
|
FROM members
|
||||||
JOIN systems ON members.system_id = systems.id
|
JOIN systems ON members.system_id = systems.id
|
||||||
WHERE members.id = $1 AND systems.owner_id = $2
|
WHERE members.id = $1 AND systems.owner_id = $2
|
||||||
) AS 'exists: bool'",
|
",
|
||||||
self.id,
|
self.id,
|
||||||
user_id
|
user_id
|
||||||
)
|
)
|
||||||
.fetch_one(db)
|
.fetch_optional(db)
|
||||||
.await
|
.await
|
||||||
.ok()
|
.attach_printable("Failed to validate member by user")
|
||||||
.is_some_and(|record| record.exists);
|
.map(|res| res.map(|res| res.id))
|
||||||
|
}
|
||||||
|
|
||||||
if exists {
|
pub async fn fetch_by_alias(
|
||||||
Ok(Id {
|
alias: &str,
|
||||||
id: self.id,
|
system_id: system::Id<Trusted>,
|
||||||
trusted: std::marker::PhantomData,
|
db: &SqlitePool,
|
||||||
})
|
) -> Result<Option<Id<Trusted>>, sqlx::Error> {
|
||||||
} else {
|
sqlx::query!(
|
||||||
Err(self)
|
"SELECT
|
||||||
}
|
member_id AS 'id: Id<Trusted>'
|
||||||
|
FROM aliases
|
||||||
|
WHERE alias = $1 AND system_id = $2",
|
||||||
|
alias,
|
||||||
|
system_id
|
||||||
|
)
|
||||||
|
.fetch_optional(db)
|
||||||
|
.await
|
||||||
|
.attach_printable("Failed to fetch member id by alias")
|
||||||
|
.map(|res| res.map(|res| res.id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Id<Trusted> {
|
impl Id<Trusted> {
|
||||||
pub async fn fetch_triggers(
|
pub async fn fetch_triggers(self, db: &SqlitePool) -> Result<Vec<Trigger>, sqlx::Error> {
|
||||||
self,
|
|
||||||
db: &SqlitePool,
|
|
||||||
) -> error_stack::Result<Vec<Trigger>, trigger::Error> {
|
|
||||||
Trigger::fetch_by_member_id(db, self).await
|
Trigger::fetch_by_member_id(db, self).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, derive_more::From)]
|
||||||
|
/// An untrusted member reference from an external source
|
||||||
|
pub enum MemberRef {
|
||||||
|
Id(Id<Untrusted>),
|
||||||
|
/// We were given a [`super::Alias`]
|
||||||
|
Alias(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemberRef {
|
||||||
|
pub async fn validate_by_system(
|
||||||
|
&self,
|
||||||
|
system_id: system::Id<Trusted>,
|
||||||
|
db: &SqlitePool,
|
||||||
|
) -> Result<Option<Id<Trusted>>, sqlx::Error> {
|
||||||
|
match self {
|
||||||
|
MemberRef::Id(id) => id
|
||||||
|
.validate_by_system(system_id, db)
|
||||||
|
.await
|
||||||
|
.attach_printable("Failed to validate member reference via id and system"),
|
||||||
|
MemberRef::Alias(alias) => Id::fetch_by_alias(alias, system_id, db)
|
||||||
|
.await
|
||||||
|
.attach_printable("Failed to validate member reference via alias and system"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TO-DO: move SQL to rust struct
|
// TO-DO: move SQL to rust struct
|
||||||
#[derive(FromRow, Debug)]
|
#[derive(FromRow, Debug)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
|
@ -149,6 +177,7 @@ impl Member {
|
||||||
)
|
)
|
||||||
.fetch_optional(db)
|
.fetch_optional(db)
|
||||||
.await
|
.await
|
||||||
|
.attach_printable("Failed to fetch member by id and trust by system")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch a member by their id
|
/// Fetch a member by their id
|
||||||
|
|
@ -177,6 +206,7 @@ impl Member {
|
||||||
)
|
)
|
||||||
.fetch_optional(db)
|
.fetch_optional(db)
|
||||||
.await
|
.await
|
||||||
|
.attach_printable("Failed to fetch member by id")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -315,7 +345,7 @@ impl View {
|
||||||
&self,
|
&self,
|
||||||
system_id: system::Id<Trusted>,
|
system_id: system::Id<Trusted>,
|
||||||
db: &SqlitePool,
|
db: &SqlitePool,
|
||||||
) -> error_stack::Result<i64, Error> {
|
) -> error_stack::Result<i64, sqlx::Error> {
|
||||||
debug!("Adding member {} to database", self.display_name);
|
debug!("Adding member {} to database", self.display_name);
|
||||||
sqlx::query!("
|
sqlx::query!("
|
||||||
INSERT INTO members (full_name, display_name, profile_picture_url, title, pronouns, name_pronunciation, name_recording_url, system_id)
|
INSERT INTO members (full_name, display_name, profile_picture_url, title, pronouns, name_pronunciation, name_recording_url, system_id)
|
||||||
|
|
@ -334,7 +364,6 @@ impl View {
|
||||||
.fetch_one(db)
|
.fetch_one(db)
|
||||||
.await
|
.await
|
||||||
.attach_printable("Error adding member to database")
|
.attach_printable("Error adding member to database")
|
||||||
.change_context(Error::Sqlx)
|
|
||||||
.map(|row| row.id)
|
.map(|row| row.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -345,7 +374,7 @@ impl View {
|
||||||
&self,
|
&self,
|
||||||
member_id: Id<Trusted>,
|
member_id: Id<Trusted>,
|
||||||
db: &SqlitePool,
|
db: &SqlitePool,
|
||||||
) -> error_stack::Result<Option<SqliteQueryResult>, Error> {
|
) -> error_stack::Result<SqliteQueryResult, sqlx::Error> {
|
||||||
sqlx::query!("
|
sqlx::query!("
|
||||||
UPDATE members
|
UPDATE members
|
||||||
SET full_name = $1, display_name = $2, profile_picture_url = $3, title = $4, pronouns = $5, name_pronunciation = $6, name_recording_url = $7
|
SET full_name = $1, display_name = $2, profile_picture_url = $3, title = $4, pronouns = $5, name_pronunciation = $6, name_recording_url = $7
|
||||||
|
|
@ -361,15 +390,13 @@ impl View {
|
||||||
member_id,
|
member_id,
|
||||||
).execute(db).await
|
).execute(db).await
|
||||||
.attach_printable("Error editing member in database")
|
.attach_printable("Error editing member in database")
|
||||||
.change_context(Error::Sqlx)
|
|
||||||
.map(Some)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<SlackViewState> for View {
|
impl TryFrom<SlackViewState> for View {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn try_from(value: SlackViewState) -> Result<Self, Self::Error> {
|
fn try_from(value: SlackViewState) -> std::result::Result<Self, Self::Error> {
|
||||||
let mut view = Self::default();
|
let mut view = Self::default();
|
||||||
for (_id, values) in value.values {
|
for (_id, values) in value.values {
|
||||||
for (id, content) in values {
|
for (id, content) in values {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
use crate::id;
|
use crate::id;
|
||||||
|
|
||||||
use super::{Trusted, member};
|
use super::{Trusted, member};
|
||||||
|
use error_stack::{Result, ResultExt};
|
||||||
use slack_morphism::SlackTs;
|
use slack_morphism::SlackTs;
|
||||||
use sqlx::{SqlitePool, prelude::*};
|
use sqlx::{SqlitePool, prelude::*, sqlite::SqliteQueryResult};
|
||||||
|
|
||||||
id!(
|
id!(
|
||||||
/// You cannot create a message id, as it is internal generated-only.
|
/// You cannot create a message id, as it is internal generated-only.
|
||||||
|
|
@ -28,7 +29,7 @@ impl MessageLog {
|
||||||
pub async fn delete_by_message_id(
|
pub async fn delete_by_message_id(
|
||||||
message_id: String,
|
message_id: String,
|
||||||
db: &SqlitePool,
|
db: &SqlitePool,
|
||||||
) -> Result<(), sqlx::Error> {
|
) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
r#"
|
r#"
|
||||||
DELETE FROM message_logs
|
DELETE FROM message_logs
|
||||||
|
|
@ -38,7 +39,7 @@ impl MessageLog {
|
||||||
)
|
)
|
||||||
.execute(db)
|
.execute(db)
|
||||||
.await
|
.await
|
||||||
.map(|_| ())
|
.attach_printable("Failed to delete message log")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetches a message log by the slack message ID.
|
/// Fetches a message log by the slack message ID.
|
||||||
|
|
@ -62,6 +63,7 @@ impl MessageLog {
|
||||||
)
|
)
|
||||||
.fetch_optional(db)
|
.fetch_optional(db)
|
||||||
.await
|
.await
|
||||||
|
.attach_printable("Failed to fetch message log")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetches all message logs by the member ID.
|
/// Fetches all message logs by the member ID.
|
||||||
|
|
@ -86,6 +88,7 @@ impl MessageLog {
|
||||||
)
|
)
|
||||||
.fetch_all(db)
|
.fetch_all(db)
|
||||||
.await
|
.await
|
||||||
|
.attach_printable("Failed to fetch message logs")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn insert(
|
pub async fn insert(
|
||||||
|
|
@ -108,5 +111,6 @@ impl MessageLog {
|
||||||
)
|
)
|
||||||
.fetch_one(db)
|
.fetch_one(db)
|
||||||
.await
|
.await
|
||||||
|
.attach_printable("Failed to insert message log")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ macro_rules! id {
|
||||||
($(#[$attr:meta])* => $name:ident) => {
|
($(#[$attr:meta])* => $name:ident) => {
|
||||||
#[derive(::sqlx::Type, Debug, PartialEq, Eq, Clone, Copy)]
|
#[derive(::sqlx::Type, Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
$(#[$attr])*
|
$(#[$attr])*
|
||||||
pub struct Id<T> {
|
pub struct Id<T: $crate::models::Trustability> {
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
trusted: ::std::marker::PhantomData<T>,
|
trusted: ::std::marker::PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
@ -46,7 +46,7 @@ macro_rules! id {
|
||||||
fn encode_by_ref(
|
fn encode_by_ref(
|
||||||
&self,
|
&self,
|
||||||
buf: &mut <DB as ::sqlx::Database>::ArgumentBuffer<'q>,
|
buf: &mut <DB as ::sqlx::Database>::ArgumentBuffer<'q>,
|
||||||
) -> Result<::sqlx::encode::IsNull, ::sqlx::error::BoxDynError> {
|
) -> ::std::result::Result<::sqlx::encode::IsNull, ::sqlx::error::BoxDynError> {
|
||||||
<i64 as ::sqlx::Encode<'_, DB>>::encode_by_ref(&self.id, buf)
|
<i64 as ::sqlx::Encode<'_, DB>>::encode_by_ref(&self.id, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -62,7 +62,7 @@ macro_rules! id {
|
||||||
{
|
{
|
||||||
fn decode(
|
fn decode(
|
||||||
value: <DB as ::sqlx::Database>::ValueRef<'q>,
|
value: <DB as ::sqlx::Database>::ValueRef<'q>,
|
||||||
) -> Result<Self, ::sqlx::error::BoxDynError> {
|
) -> ::std::result::Result<Self, ::sqlx::error::BoxDynError> {
|
||||||
let id = <i64 as ::sqlx::Decode<'_, DB>>::decode(value)?;
|
let id = <i64 as ::sqlx::Decode<'_, DB>>::decode(value)?;
|
||||||
Ok(Id {
|
Ok(Id {
|
||||||
id,
|
id,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
id,
|
fields, id,
|
||||||
models::member::{Member, TriggeredMember},
|
models::member::{Member, TriggeredMember},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -9,6 +9,7 @@ use super::{
|
||||||
trigger::Trigger,
|
trigger::Trigger,
|
||||||
user,
|
user,
|
||||||
};
|
};
|
||||||
|
use error_stack::{Result, ResultExt, bail};
|
||||||
use redact::Secret;
|
use redact::Secret;
|
||||||
use sqlx::{SqlitePool, prelude::*};
|
use sqlx::{SqlitePool, prelude::*};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
@ -72,7 +73,7 @@ pub struct System {
|
||||||
/// Error while changing the active member
|
/// Error while changing the active member
|
||||||
pub enum ChangeActiveMemberError {
|
pub enum ChangeActiveMemberError {
|
||||||
/// Error while calling the database
|
/// Error while calling the database
|
||||||
Sqlx(#[from] sqlx::Error),
|
Sqlx,
|
||||||
/// The member is not part of the system
|
/// The member is not part of the system
|
||||||
MemberNotFound,
|
MemberNotFound,
|
||||||
}
|
}
|
||||||
|
|
@ -106,6 +107,7 @@ impl System {
|
||||||
)
|
)
|
||||||
.fetch_optional(db)
|
.fetch_optional(db)
|
||||||
.await
|
.await
|
||||||
|
.attach_printable("Error fetching system")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn active_member(&self, db: &SqlitePool) -> Result<Option<Member>, sqlx::Error> {
|
pub async fn active_member(&self, db: &SqlitePool) -> Result<Option<Member>, sqlx::Error> {
|
||||||
|
|
@ -128,13 +130,19 @@ impl System {
|
||||||
let mut new_active_member = None;
|
let mut new_active_member = None;
|
||||||
|
|
||||||
if let Some(new_active_member_id) = new_active_member_id {
|
if let Some(new_active_member_id) = new_active_member_id {
|
||||||
let Some(member) = Member::fetch_by_id(new_active_member_id, db).await? else {
|
let Some(member) = Member::fetch_by_id(new_active_member_id, db)
|
||||||
return Err(ChangeActiveMemberError::MemberNotFound);
|
.await
|
||||||
|
.change_context(ChangeActiveMemberError::Sqlx)
|
||||||
|
.attach_printable("Failed to fetch member")?
|
||||||
|
else {
|
||||||
|
bail!(ChangeActiveMemberError::MemberNotFound);
|
||||||
};
|
};
|
||||||
|
|
||||||
new_active_member = Some(member);
|
new_active_member = Some(member);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fields!(new_active_member = ?&new_active_member);
|
||||||
|
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
r#"
|
r#"
|
||||||
UPDATE systems
|
UPDATE systems
|
||||||
|
|
@ -145,7 +153,9 @@ impl System {
|
||||||
self.id
|
self.id
|
||||||
)
|
)
|
||||||
.execute(db)
|
.execute(db)
|
||||||
.await?;
|
.await
|
||||||
|
.change_context(ChangeActiveMemberError::Sqlx)
|
||||||
|
.attach_printable("Failed to update system active member")?;
|
||||||
|
|
||||||
self.active_member_id = new_active_member_id;
|
self.active_member_id = new_active_member_id;
|
||||||
Ok(new_active_member)
|
Ok(new_active_member)
|
||||||
|
|
@ -174,6 +184,7 @@ impl System {
|
||||||
)
|
)
|
||||||
.fetch_all(db)
|
.fetch_all(db)
|
||||||
.await
|
.await
|
||||||
|
.attach_printable("Failed to fetch members")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn fetch_triggered_member(
|
pub async fn fetch_triggered_member(
|
||||||
|
|
@ -203,5 +214,6 @@ impl System {
|
||||||
)
|
)
|
||||||
.fetch_optional(db)
|
.fetch_optional(db)
|
||||||
.await
|
.await
|
||||||
|
.attach_printable("Failed to fetch triggered member")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@ use std::str::FromStr;
|
||||||
use crate::id;
|
use crate::id;
|
||||||
|
|
||||||
use super::{Trustability, Trusted, Untrusted, member, system};
|
use super::{Trustability, Trusted, Untrusted, member, system};
|
||||||
use error_stack::ResultExt;
|
use error_stack::{Result, ResultExt};
|
||||||
use slack_morphism::prelude::*;
|
use slack_morphism::prelude::*;
|
||||||
use sqlx::{SqlitePool, prelude::*};
|
use sqlx::{SqlitePool, prelude::*, sqlite::SqliteQueryResult};
|
||||||
use tracing::{debug, warn};
|
use tracing::{debug, warn};
|
||||||
|
|
||||||
id!(
|
id!(
|
||||||
|
|
@ -28,30 +28,24 @@ impl Id<Untrusted> {
|
||||||
self,
|
self,
|
||||||
system_id: system::Id<Trusted>,
|
system_id: system::Id<Trusted>,
|
||||||
db: &SqlitePool,
|
db: &SqlitePool,
|
||||||
) -> Result<Id<Trusted>, Self> {
|
) -> Result<Id<Trusted>, sqlx::Error> {
|
||||||
let exists = sqlx::query!(
|
sqlx::query!(
|
||||||
"SELECT EXISTS(SELECT 1 FROM triggers WHERE id = $1 AND system_id = $2) AS 'exists: bool'",
|
"SELECT
|
||||||
|
id as 'id: Id<Trusted>'
|
||||||
|
FROM triggers
|
||||||
|
WHERE id = $1 AND system_id = $2",
|
||||||
self.id,
|
self.id,
|
||||||
system_id.id
|
system_id.id
|
||||||
)
|
)
|
||||||
.fetch_one(db)
|
.fetch_one(db)
|
||||||
.await
|
.await
|
||||||
.ok()
|
.map(|record| record.id)
|
||||||
.is_some_and(|record| record.exists);
|
.attach_printable("Error validating trigger")
|
||||||
|
|
||||||
if exists {
|
|
||||||
Ok(Id {
|
|
||||||
id: self.id,
|
|
||||||
trusted: std::marker::PhantomData,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Err(self)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Id<Trusted> {
|
impl Id<Trusted> {
|
||||||
pub async fn delete(self, db_pool: &SqlitePool) -> Result<(), sqlx::Error> {
|
pub async fn delete(self, db_pool: &SqlitePool) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
r#"
|
r#"
|
||||||
DELETE FROM triggers
|
DELETE FROM triggers
|
||||||
|
|
@ -61,7 +55,7 @@ impl Id<Trusted> {
|
||||||
)
|
)
|
||||||
.execute(db_pool)
|
.execute(db_pool)
|
||||||
.await
|
.await
|
||||||
.map(|_| ())
|
.attach_printable("Error deleting trigger")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -99,7 +93,7 @@ pub struct UnknownType(String);
|
||||||
impl FromStr for Type {
|
impl FromStr for Type {
|
||||||
type Err = UnknownType;
|
type Err = UnknownType;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
||||||
match s {
|
match s {
|
||||||
"suffix" => Ok(Self::Suffix),
|
"suffix" => Ok(Self::Suffix),
|
||||||
"prefix" => Ok(Self::Prefix),
|
"prefix" => Ok(Self::Prefix),
|
||||||
|
|
@ -140,6 +134,7 @@ impl Trigger {
|
||||||
)
|
)
|
||||||
.fetch_optional(db)
|
.fetch_optional(db)
|
||||||
.await
|
.await
|
||||||
|
.attach_printable("Error fetching trigger")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn fetch_by_system_id(
|
pub async fn fetch_by_system_id(
|
||||||
|
|
@ -164,12 +159,13 @@ impl Trigger {
|
||||||
)
|
)
|
||||||
.fetch_all(db)
|
.fetch_all(db)
|
||||||
.await
|
.await
|
||||||
|
.attach_printable("Error fetching triggers")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn fetch_by_member_id(
|
pub async fn fetch_by_member_id(
|
||||||
db: &SqlitePool,
|
db: &SqlitePool,
|
||||||
member_id: member::Id<Trusted>,
|
member_id: member::Id<Trusted>,
|
||||||
) -> error_stack::Result<Vec<Self>, Error> {
|
) -> error_stack::Result<Vec<Self>, sqlx::Error> {
|
||||||
sqlx::query_as!(
|
sqlx::query_as!(
|
||||||
Trigger,
|
Trigger,
|
||||||
r#"
|
r#"
|
||||||
|
|
@ -187,7 +183,7 @@ impl Trigger {
|
||||||
)
|
)
|
||||||
.fetch_all(db)
|
.fetch_all(db)
|
||||||
.await
|
.await
|
||||||
.change_context(Error::Sqlx)
|
.attach_printable("Error fetching triggers")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -289,7 +285,7 @@ impl View {
|
||||||
&self,
|
&self,
|
||||||
trigger_id: Id<Trusted>,
|
trigger_id: Id<Trusted>,
|
||||||
db: &SqlitePool,
|
db: &SqlitePool,
|
||||||
) -> error_stack::Result<(), Error> {
|
) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
r#"
|
r#"
|
||||||
UPDATE triggers
|
UPDATE triggers
|
||||||
|
|
@ -303,8 +299,6 @@ impl View {
|
||||||
.execute(db)
|
.execute(db)
|
||||||
.await
|
.await
|
||||||
.attach_printable("Error updating trigger in database")
|
.attach_printable("Error updating trigger in database")
|
||||||
.change_context(Error::Sqlx)
|
|
||||||
.map(|_| ())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_add_view(self, member_id: member::Id<Trusted>) -> SlackView {
|
pub fn create_add_view(self, member_id: member::Id<Trusted>) -> SlackView {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue