mirror of
https://github.com/System-End/plura.git
synced 2026-04-19 16:28:21 +00:00
feat(triggers): change triggers to edit entirely through commands
This commit is contained in:
parent
dcf078f3fa
commit
e2e9b05845
6 changed files with 89 additions and 466 deletions
|
|
@ -49,7 +49,7 @@ impl Command {
|
|||
.await
|
||||
.change_context(CommandError::System),
|
||||
Self::Triggers(triggers) => triggers
|
||||
.run(event, client, state)
|
||||
.run(event, state)
|
||||
.await
|
||||
.change_context(CommandError::Triggers),
|
||||
Self::Aliases(aliases) => aliases
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use error_stack::{Result, ResultExt};
|
||||
use slack_morphism::prelude::*;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::{
|
||||
BOT_TOKEN, fetch_member, fetch_system, fields,
|
||||
fetch_member, fetch_system, fields,
|
||||
models::{self, Untrusted, member::MemberRef, trigger, user},
|
||||
};
|
||||
|
||||
|
|
@ -15,6 +13,11 @@ pub enum Trigger {
|
|||
Add {
|
||||
/// The member to add the trigger for.
|
||||
member: MemberRef,
|
||||
/// The type of trigger
|
||||
#[clap(name = "type")]
|
||||
typ: trigger::Type,
|
||||
/// The trigger content
|
||||
content: String,
|
||||
},
|
||||
/// Deletes a trigger
|
||||
Delete {
|
||||
|
|
@ -30,13 +33,17 @@ pub enum Trigger {
|
|||
Edit {
|
||||
/// The trigger to edit. Use the trigger id from /trigger list
|
||||
id: trigger::Id<Untrusted>,
|
||||
/// The type of trigger
|
||||
#[clap(name = "type", long = "type", short)]
|
||||
typ: Option<trigger::Type>,
|
||||
/// The trigger content
|
||||
#[clap(long, short)]
|
||||
content: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, displaydoc::Display, Debug)]
|
||||
pub enum CommandError {
|
||||
/// Error while calling the Slack API
|
||||
Slack,
|
||||
/// Error while calling the database
|
||||
Sqlx,
|
||||
}
|
||||
|
|
@ -46,64 +53,43 @@ impl Trigger {
|
|||
pub async fn run(
|
||||
self,
|
||||
event: SlackCommandEvent,
|
||||
client: Arc<SlackHyperClient>,
|
||||
state: SlackClientEventsUserState,
|
||||
) -> Result<SlackCommandEventResponse, CommandError> {
|
||||
match self {
|
||||
Self::Add { member } => {
|
||||
let token = &BOT_TOKEN;
|
||||
let session = client.open_session(token);
|
||||
Self::create_trigger(event, &state, session, member).await
|
||||
}
|
||||
Self::Add {
|
||||
member,
|
||||
typ,
|
||||
content,
|
||||
} => Self::create_trigger(event, &state, member, typ, content).await,
|
||||
Self::Delete { id } => Self::delete_trigger(event, &state, id).await,
|
||||
Self::List { member } => Self::list_triggers(event, &state, member).await,
|
||||
Self::Edit { id } => {
|
||||
let token = &BOT_TOKEN;
|
||||
let session = client.open_session(token);
|
||||
Self::edit_trigger(event, &state, session, id).await
|
||||
Self::Edit { id, typ, content } => {
|
||||
Self::edit_trigger(event, &state, id, typ, content).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(event, state, session), fields(system_id, member_id))]
|
||||
#[tracing::instrument(skip(event, state), fields(system_id, member_id))]
|
||||
async fn create_trigger(
|
||||
event: SlackCommandEvent,
|
||||
state: &SlackClientEventsUserState,
|
||||
session: SlackClientSession<'_, SlackClientHyperHttpsConnector>,
|
||||
member_id: MemberRef,
|
||||
typ: trigger::Type,
|
||||
content: String,
|
||||
) -> Result<SlackCommandEventResponse, CommandError> {
|
||||
let states = state.read().await;
|
||||
let user_state = states.get_user_state::<user::State>().unwrap();
|
||||
|
||||
fetch_system!(event, user_state => system_id);
|
||||
fetch_member!(member_id, user_state, system_id => member_id);
|
||||
|
||||
let Some(member_id) = member_id
|
||||
.validate_by_system(system_id, &user_state.db)
|
||||
models::Trigger::insert(member_id, system_id, typ, content, &user_state.db)
|
||||
.await
|
||||
.change_context(CommandError::Sqlx)?
|
||||
else {
|
||||
debug!("Member not found");
|
||||
return Ok(SlackCommandEventResponse::new(
|
||||
SlackMessageContent::new()
|
||||
.with_text("Member not found. Make sure you used the correct ID".into()),
|
||||
));
|
||||
};
|
||||
.change_context(CommandError::Sqlx)?;
|
||||
|
||||
fields!(member_id = %member_id);
|
||||
|
||||
let view = trigger::View::default().create_add_view(member_id);
|
||||
let view = session
|
||||
.views_open(&SlackApiViewsOpenRequest::new(
|
||||
event.trigger_id.clone(),
|
||||
view,
|
||||
))
|
||||
.await
|
||||
.attach_printable("Error opening view")
|
||||
.change_context(CommandError::Slack)?;
|
||||
|
||||
debug!(?view, "Opened view");
|
||||
|
||||
Ok(SlackCommandEventResponse::new(SlackMessageContent::new()))
|
||||
Ok(SlackCommandEventResponse::new(
|
||||
SlackMessageContent::new().with_text("Trigger created!".into()),
|
||||
))
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(event, state), fields(system_id))]
|
||||
|
|
@ -142,14 +128,13 @@ impl Trigger {
|
|||
));
|
||||
};
|
||||
|
||||
// Fetch the trigger to delete it
|
||||
trigger_id
|
||||
.delete(&user_state.db)
|
||||
.await
|
||||
.change_context(CommandError::Sqlx)?;
|
||||
|
||||
Ok(SlackCommandEventResponse::new(
|
||||
SlackMessageContent::new().with_text("Successfully deleted trigger!".into()),
|
||||
SlackMessageContent::new().with_text("Deleted trigger!".into()),
|
||||
))
|
||||
}
|
||||
|
||||
|
|
@ -191,13 +176,12 @@ impl Trigger {
|
|||
.into_iter()
|
||||
.map(|trigger| {
|
||||
let fields = vec![
|
||||
md!("Trigger ID: {}", trigger.id),
|
||||
md!("Member ID: {}", trigger.member_id),
|
||||
md!("{}: {}", trigger.typ, trigger.text),
|
||||
];
|
||||
|
||||
SlackSectionBlock::new()
|
||||
.with_text(md!("**Trigger {}**", trigger.id))
|
||||
.with_text(md!("*Trigger {}*", trigger.id))
|
||||
.with_fields(fields)
|
||||
})
|
||||
.map(Into::into)
|
||||
|
|
@ -208,12 +192,13 @@ impl Trigger {
|
|||
))
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(event, state, session), fields(system_id))]
|
||||
#[tracing::instrument(skip(event, state), fields(system_id))]
|
||||
pub async fn edit_trigger(
|
||||
event: SlackCommandEvent,
|
||||
state: &SlackClientEventsUserState,
|
||||
session: SlackClientSession<'_, SlackClientHyperHttpsConnector>,
|
||||
trigger_id: trigger::Id<Untrusted>,
|
||||
typ: Option<trigger::Type>,
|
||||
text: Option<String>,
|
||||
) -> Result<SlackCommandEventResponse, CommandError> {
|
||||
let states = state.read().await;
|
||||
let user_state = states.get_user_state::<user::State>().unwrap();
|
||||
|
|
@ -233,32 +218,13 @@ impl Trigger {
|
|||
|
||||
fields!(trigger_id = %trigger_id);
|
||||
|
||||
// Fetch the trigger to edit
|
||||
let trigger = models::Trigger::fetch_by_id(trigger_id, &user_state.db)
|
||||
trigger_id
|
||||
.update(typ, text, &user_state.db)
|
||||
.await
|
||||
.change_context(CommandError::Sqlx)?;
|
||||
|
||||
let Some(trigger) = trigger else {
|
||||
debug!("Trigger not found");
|
||||
return Ok(SlackCommandEventResponse::new(
|
||||
SlackMessageContent::new()
|
||||
.with_text("Trigger not found. Make sure you used the correct ID".into()),
|
||||
));
|
||||
};
|
||||
|
||||
let view = trigger::View::from(trigger).create_edit_view(trigger_id);
|
||||
|
||||
let view = session
|
||||
.views_open(&SlackApiViewsOpenRequest::new(
|
||||
event.trigger_id.clone(),
|
||||
view,
|
||||
))
|
||||
.await
|
||||
.attach_printable("Error opening view")
|
||||
.change_context(CommandError::Slack)?;
|
||||
|
||||
debug!(?view, "Opened view");
|
||||
|
||||
Ok(SlackCommandEventResponse::new(SlackMessageContent::new()))
|
||||
Ok(SlackCommandEventResponse::new(
|
||||
SlackMessageContent::new().with_text("Updated trigger!".into()),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ async fn push_event_callback(
|
|||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[tracing::instrument(skip(client, state))]
|
||||
async fn handle_message(
|
||||
message_event: SlackMessageEvent,
|
||||
client: &SlackHyperClient,
|
||||
|
|
@ -156,6 +156,7 @@ async fn handle_message(
|
|||
.await
|
||||
.change_context(PushEventError::MemberFetch)?
|
||||
else {
|
||||
debug!("Member not triggered");
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
mod member;
|
||||
mod trigger;
|
||||
use std::error::Error;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
|
@ -8,10 +7,8 @@ use error_stack::Report;
|
|||
use member::{create_member, edit_member};
|
||||
use slack_morphism::prelude::*;
|
||||
use tracing::{debug, error};
|
||||
use trigger::{create_trigger, edit_trigger};
|
||||
|
||||
use crate::BOT_TOKEN;
|
||||
use crate::models::system::System;
|
||||
use crate::models::{self, Trusted, user};
|
||||
|
||||
#[tracing::instrument(skip(event, environment))]
|
||||
|
|
@ -141,84 +138,6 @@ async fn handle_modal_view(
|
|||
handle_user_error(error, user_id.into(), client).await;
|
||||
}
|
||||
}
|
||||
Some(id) if id.starts_with("create_trigger_") => {
|
||||
debug!("Creating trigger");
|
||||
|
||||
let member_id = id
|
||||
.strip_prefix("create_trigger_")
|
||||
.expect("Failed to parse member id from external id")
|
||||
.parse::<i64>()
|
||||
.map(models::member::Id::new)
|
||||
.expect("Failed to parse member id from external id");
|
||||
|
||||
// TO-DO: Better handling of Err case
|
||||
let Ok(Some(trusted_member_id)) =
|
||||
member_id.validate_by_user(&user_id, &user_state.db).await
|
||||
else {
|
||||
error!(
|
||||
id,
|
||||
"Failed to validate member id from external id. Bailing in case this was a malicious call",
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
if let Err(error) = create_trigger(
|
||||
view_state,
|
||||
&client,
|
||||
user_state,
|
||||
user_id.clone(),
|
||||
trusted_member_id,
|
||||
)
|
||||
.await
|
||||
{
|
||||
handle_user_error(error, user_id.into(), client).await;
|
||||
};
|
||||
}
|
||||
Some(id) if id.starts_with("edit_trigger_") => {
|
||||
debug!("Editing trigger");
|
||||
|
||||
let trigger_id = id
|
||||
.strip_prefix("edit_trigger_")
|
||||
.expect("Failed to parse member id from external id")
|
||||
.parse::<i64>()
|
||||
.map(models::trigger::Id::new)
|
||||
.expect("Failed to parse member id from external id");
|
||||
|
||||
let Some(system) = System::fetch_by_user_id(&user_state.db, &user_id)
|
||||
.await
|
||||
.ok()
|
||||
.flatten()
|
||||
else {
|
||||
error!(
|
||||
%user_id,
|
||||
"Failed to fetch system id for user id. Bailing in case this was a malicious call"
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
let Ok(trusted_trigger_id) = trigger_id
|
||||
.validate_by_system(system.id, &user_state.db)
|
||||
.await
|
||||
else {
|
||||
error!(
|
||||
"Failed to validate member id from external id {}. Bailing in case this was a malicious call",
|
||||
id
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
if let Err(error) = edit_trigger(
|
||||
view_state,
|
||||
&client,
|
||||
user_state,
|
||||
user_id.clone(),
|
||||
trusted_trigger_id,
|
||||
)
|
||||
.await
|
||||
{
|
||||
handle_user_error(error, user_id.into(), client.clone()).await;
|
||||
}
|
||||
}
|
||||
Some(id) => {
|
||||
error!("receieved unknown external id: {id}");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,109 +0,0 @@
|
|||
use error_stack::{Result, ResultExt, bail};
|
||||
use slack_morphism::prelude::*;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::{
|
||||
BOT_TOKEN, fields,
|
||||
models::{
|
||||
Trusted, member,
|
||||
system::System,
|
||||
trigger,
|
||||
user::{self, State},
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(thiserror::Error, displaydoc::Display, Debug)]
|
||||
pub enum Error {
|
||||
/// Error while calling the database
|
||||
Sqlx,
|
||||
/// Error while calling the Slack API
|
||||
Slack,
|
||||
/// No system found for the user
|
||||
NoSystem,
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(view_state, client, user_state), fields(system_id))]
|
||||
pub async fn create_trigger(
|
||||
view_state: SlackViewState,
|
||||
client: &SlackHyperClient,
|
||||
user_state: &State,
|
||||
user_id: user::Id<Trusted>,
|
||||
member_id: member::Id<Trusted>,
|
||||
) -> Result<(), Error> {
|
||||
let data = trigger::View::from(view_state);
|
||||
|
||||
let Some(system_id) = System::fetch_by_user_id(&user_state.db, &user_id)
|
||||
.await
|
||||
.attach_printable("Error checking if system exists")
|
||||
.change_context(Error::Sqlx)?
|
||||
.map(|system| system.id)
|
||||
else {
|
||||
bail!(Error::NoSystem);
|
||||
};
|
||||
|
||||
fields!(system_id = %system_id);
|
||||
|
||||
let id = data
|
||||
.add(system_id, member_id, &user_state.db)
|
||||
.await
|
||||
.change_context(Error::Sqlx)?;
|
||||
|
||||
fields!(id = %id);
|
||||
debug!("Trigger created");
|
||||
|
||||
let session = client.open_session(&BOT_TOKEN);
|
||||
let user: SlackUserId = user_id.into();
|
||||
|
||||
let conversation = session
|
||||
.conversations_open(&SlackApiConversationsOpenRequest::new().with_users(vec![user.clone()]))
|
||||
.await
|
||||
.change_context(Error::Slack)?
|
||||
.channel;
|
||||
|
||||
session
|
||||
.chat_post_ephemeral(&SlackApiChatPostEphemeralRequest::new(
|
||||
conversation.id,
|
||||
user,
|
||||
SlackMessageContent::new().with_text("Successfully added trigger!".into()),
|
||||
))
|
||||
.await
|
||||
.change_context(Error::Slack)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(view_state, client, user_state))]
|
||||
pub async fn edit_trigger(
|
||||
view_state: SlackViewState,
|
||||
client: &SlackHyperClient,
|
||||
user_state: &State,
|
||||
user_id: user::Id<Trusted>,
|
||||
trigger_id: trigger::Id<Trusted>,
|
||||
) -> Result<(), Error> {
|
||||
let trigger_view = trigger::View::from(view_state);
|
||||
|
||||
trigger_view
|
||||
.update(trigger_id, &user_state.db)
|
||||
.await
|
||||
.change_context(Error::Sqlx)?;
|
||||
|
||||
let session = client.open_session(&BOT_TOKEN);
|
||||
let user: SlackUserId = user_id.into();
|
||||
|
||||
let conversation = session
|
||||
.conversations_open(&SlackApiConversationsOpenRequest::new().with_users(vec![user.clone()]))
|
||||
.await
|
||||
.change_context(Error::Slack)?
|
||||
.channel;
|
||||
|
||||
session
|
||||
.chat_post_ephemeral(&SlackApiChatPostEphemeralRequest::new(
|
||||
conversation.id,
|
||||
user,
|
||||
SlackMessageContent::new().with_text("Successfully edited trigger!".into()),
|
||||
))
|
||||
.await
|
||||
.change_context(Error::Slack)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -2,11 +2,9 @@ use std::str::FromStr;
|
|||
|
||||
use crate::id;
|
||||
|
||||
use super::{Trustability, Trusted, Untrusted, member, system};
|
||||
use super::{Trusted, Untrusted, member, system};
|
||||
use error_stack::{Result, ResultExt};
|
||||
use slack_morphism::prelude::*;
|
||||
use sqlx::{SqlitePool, prelude::*, sqlite::SqliteQueryResult};
|
||||
use tracing::{debug, warn};
|
||||
|
||||
id!(
|
||||
/// For an ID to be trusted, it must
|
||||
|
|
@ -17,13 +15,6 @@ id!(
|
|||
);
|
||||
|
||||
impl Id<Untrusted> {
|
||||
pub const fn new(id: i64) -> Self {
|
||||
Self {
|
||||
id,
|
||||
trusted: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(db))]
|
||||
pub async fn validate_by_system(
|
||||
self,
|
||||
|
|
@ -59,15 +50,36 @@ impl Id<Trusted> {
|
|||
.await
|
||||
.attach_printable("Error deleting trigger")
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(db))]
|
||||
pub async fn update(
|
||||
self,
|
||||
typ: Option<Type>,
|
||||
content: Option<String>,
|
||||
db: &SqlitePool,
|
||||
) -> error_stack::Result<Self, sqlx::Error> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
UPDATE triggers
|
||||
SET
|
||||
typ = coalesce($2, typ),
|
||||
text = coalesce($3, text)
|
||||
WHERE id = $1
|
||||
RETURNING
|
||||
id as "id: Id<Trusted>"
|
||||
"#,
|
||||
self,
|
||||
typ,
|
||||
content
|
||||
)
|
||||
.fetch_one(db)
|
||||
.await
|
||||
.attach_printable("Failed to update trigger")
|
||||
.map(|record| record.id)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, displaydoc::Display, Debug)]
|
||||
pub enum Error {
|
||||
/// Error while calling the database
|
||||
Sqlx,
|
||||
}
|
||||
|
||||
#[derive(Debug, sqlx::Type, displaydoc::Display, PartialEq, Eq)]
|
||||
#[derive(Debug, sqlx::Type, displaydoc::Display, PartialEq, Eq, clap::ValueEnum, Clone, Copy)]
|
||||
#[repr(i64)]
|
||||
pub enum Type {
|
||||
/// Suffix
|
||||
|
|
@ -115,30 +127,6 @@ pub struct Trigger {
|
|||
}
|
||||
|
||||
impl Trigger {
|
||||
pub async fn fetch_by_id<T>(id: Id<T>, db: &SqlitePool) -> Result<Option<Self>, sqlx::Error>
|
||||
where
|
||||
T: Trustability,
|
||||
{
|
||||
sqlx::query_as!(
|
||||
Trigger,
|
||||
r#"
|
||||
SELECT
|
||||
id as "id: Id<Trusted>",
|
||||
member_id as "member_id: member::Id<Trusted>",
|
||||
system_id as "system_id: system::Id<Trusted>",
|
||||
text,
|
||||
typ
|
||||
FROM
|
||||
triggers
|
||||
WHERE id = $1
|
||||
"#,
|
||||
id.id,
|
||||
)
|
||||
.fetch_optional(db)
|
||||
.await
|
||||
.attach_printable("Error fetching trigger")
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(db))]
|
||||
pub async fn fetch_by_system_id(
|
||||
system_id: system::Id<Trusted>,
|
||||
|
|
@ -189,176 +177,34 @@ impl Trigger {
|
|||
.await
|
||||
.attach_printable("Error fetching triggers")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct View {
|
||||
pub text: String,
|
||||
pub typ: Type,
|
||||
}
|
||||
|
||||
impl Default for View {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
text: String::new(),
|
||||
typ: Type::Prefix,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl View {
|
||||
pub fn create_blocks(self) -> Vec<SlackBlock> {
|
||||
let prefix_choice = SlackBlockChoiceItem::new(
|
||||
SlackBlockText::Plain(Type::Prefix.to_string().into()),
|
||||
Type::Prefix.to_string(),
|
||||
);
|
||||
let suffix_choice = SlackBlockChoiceItem::new(
|
||||
SlackBlockText::Plain(Type::Suffix.to_string().into()),
|
||||
Type::Suffix.to_string(),
|
||||
);
|
||||
|
||||
slack_blocks!(
|
||||
some_into(
|
||||
SlackHeaderBlock::new("Trigger settings".into())
|
||||
.with_block_id("trigger_settings".into())
|
||||
),
|
||||
some_into(
|
||||
SlackInputBlock::new(
|
||||
"Trigger Text".into(),
|
||||
SlackBlockPlainTextInputElement::new("trigger_text".into())
|
||||
.with_initial_value(self.text)
|
||||
.into(),
|
||||
)
|
||||
.with_optional(false)
|
||||
),
|
||||
some_into(
|
||||
SlackInputBlock::new(
|
||||
"Trigger Type".into(),
|
||||
SlackBlockRadioButtonsElement::new(
|
||||
"type".into(),
|
||||
vec![prefix_choice, suffix_choice]
|
||||
)
|
||||
.with_initial_option(SlackBlockChoiceItem::new(
|
||||
SlackBlockText::Plain(Type::Prefix.to_string().into()),
|
||||
Type::Prefix.to_string(),
|
||||
))
|
||||
.into(),
|
||||
)
|
||||
.with_optional(false)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/// Add a trigger to the database
|
||||
///
|
||||
/// Returns the id of the new trigger
|
||||
#[tracing::instrument(skip(db))]
|
||||
pub async fn add(
|
||||
&self,
|
||||
system_id: system::Id<Trusted>,
|
||||
pub async fn insert(
|
||||
member_id: member::Id<Trusted>,
|
||||
system_id: system::Id<Trusted>,
|
||||
typ: Type,
|
||||
content: String,
|
||||
db: &SqlitePool,
|
||||
) -> error_stack::Result<Id<Trusted>, Error> {
|
||||
debug!(
|
||||
"Adding trigger for {} (Member ID {}) to database",
|
||||
system_id, member_id
|
||||
);
|
||||
|
||||
sqlx::query!(
|
||||
) -> error_stack::Result<Self, sqlx::Error> {
|
||||
sqlx::query_as!(
|
||||
Self,
|
||||
r#"
|
||||
INSERT INTO triggers (system_id, member_id, text, typ)
|
||||
INSERT INTO triggers (member_id, system_id, typ, text)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING id
|
||||
RETURNING
|
||||
id as "id: Id<Trusted>",
|
||||
member_id as "member_id: member::Id<Trusted>",
|
||||
system_id as "system_id: system::Id<Trusted>",
|
||||
typ,
|
||||
text
|
||||
"#,
|
||||
system_id.id,
|
||||
member_id.id,
|
||||
self.text,
|
||||
self.typ
|
||||
member_id,
|
||||
system_id,
|
||||
typ,
|
||||
content
|
||||
)
|
||||
.fetch_one(db)
|
||||
.await
|
||||
.attach_printable("Error adding trigger to database")
|
||||
.change_context(Error::Sqlx)
|
||||
.map(|row| Id {
|
||||
id: row.id,
|
||||
trusted: std::marker::PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
/// Update a trigger in the database to match this view
|
||||
#[tracing::instrument(skip(db))]
|
||||
pub async fn update(
|
||||
&self,
|
||||
trigger_id: Id<Trusted>,
|
||||
db: &SqlitePool,
|
||||
) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
UPDATE triggers
|
||||
SET text = $1, typ = $2
|
||||
WHERE id = $3
|
||||
"#,
|
||||
self.text,
|
||||
self.typ,
|
||||
trigger_id.id,
|
||||
)
|
||||
.execute(db)
|
||||
.await
|
||||
.attach_printable("Error updating trigger in database")
|
||||
}
|
||||
|
||||
pub fn create_add_view(self, member_id: member::Id<Trusted>) -> SlackView {
|
||||
SlackView::Modal(
|
||||
SlackModalView::new("Add a new trigger".into(), self.create_blocks())
|
||||
.with_submit("Add".into())
|
||||
.with_external_id(format!("create_trigger_{}", member_id.id)),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn create_edit_view(self, trigger_id: Id<Trusted>) -> SlackView {
|
||||
SlackView::Modal(
|
||||
SlackModalView::new("Edit trigger".into(), self.create_blocks())
|
||||
.with_submit("Save".into())
|
||||
.with_external_id(format!("edit_trigger_{}", trigger_id.id)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SlackViewState> for View {
|
||||
fn from(value: SlackViewState) -> Self {
|
||||
let mut view = Self::default();
|
||||
for (_id, values) in value.values {
|
||||
for (id, content) in values {
|
||||
match &*id.0 {
|
||||
"trigger_text" => {
|
||||
if let Some(text) = content.value {
|
||||
view.text = text;
|
||||
}
|
||||
}
|
||||
"typ" => {
|
||||
if let Some(option) = content.selected_option {
|
||||
match option.value.parse::<Type>() {
|
||||
Ok(typ) => view.typ = typ,
|
||||
Err(error) => warn!(?error, "Error parsing trigger type"),
|
||||
}
|
||||
}
|
||||
}
|
||||
other => {
|
||||
warn!("Unknown field in view when parsing a trigger::View: {other}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
view
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Trigger> for View {
|
||||
fn from(trigger: Trigger) -> Self {
|
||||
Self {
|
||||
text: trigger.text,
|
||||
typ: trigger.typ,
|
||||
}
|
||||
.attach_printable("Failed to insert trigger into database")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue