use serde::{Deserialize, Serialize}; use uuid::Uuid; use crate::error::AppError; use crate::models::channels::ChannelSlashCommand; use crate::service::ImService; use super::session::ImSession; #[derive(Deserialize, Serialize, Clone, Debug, utoipa::ToSchema)] pub struct CreateSlashCommandParams { pub command: String, pub description: Option, pub request_url: String, pub secret: Option, pub scopes: Vec, } #[derive(Deserialize, Serialize, Clone, Debug, utoipa::ToSchema)] pub struct UpdateSlashCommandParams { pub command: Option, pub description: Option, pub request_url: Option, pub secret: Option, pub scopes: Option>, pub enabled: Option, } impl ImService { pub async fn slash_command_list( &self, _ctx: &ImSession, channel_id: Uuid, ) -> Result, AppError> { sqlx::query_as::<_, ChannelSlashCommand>( "SELECT id, channel_id, workspace_id, command, description, request_url, \ secret_ciphertext, scopes, enabled, created_by, created_at, updated_at \ FROM channel_slash_command WHERE channel_id = $1 ORDER BY created_at", ) .bind(channel_id) .fetch_all(self.ctx.db.reader()) .await .map_err(AppError::Database) } pub async fn slash_command_create( &self, ctx: &ImSession, channel_id: Uuid, workspace_id: Uuid, params: CreateSlashCommandParams, ) -> Result { let now = chrono::Utc::now(); sqlx::query_as::<_, ChannelSlashCommand>( "INSERT INTO channel_slash_command \ (id, channel_id, workspace_id, command, description, request_url, \ secret_ciphertext, scopes, enabled, created_by, created_at, updated_at) \ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, true, $9, $10, $10) \ RETURNING id, channel_id, workspace_id, command, description, request_url, \ secret_ciphertext, scopes, enabled, created_by, created_at, updated_at", ) .bind(Uuid::now_v7()) .bind(channel_id) .bind(workspace_id) .bind(¶ms.command) .bind(params.description.as_deref()) .bind(¶ms.request_url) .bind(params.secret.as_deref()) .bind(¶ms.scopes) .bind(ctx.user) .bind(now) .fetch_one(self.ctx.db.writer()) .await .map_err(AppError::Database) } pub async fn slash_command_update( &self, _ctx: &ImSession, command_id: Uuid, params: UpdateSlashCommandParams, ) -> Result { let now = chrono::Utc::now(); sqlx::query_as::<_, ChannelSlashCommand>( "UPDATE channel_slash_command SET \ command = COALESCE($1, command), \ description = COALESCE($2, description), \ request_url = COALESCE($3, request_url), \ secret_ciphertext = COALESCE($4, secret_ciphertext), \ scopes = COALESCE($5, scopes), \ enabled = COALESCE($6, enabled), \ updated_at = $7 \ WHERE id = $8 \ RETURNING id, channel_id, workspace_id, command, description, request_url, \ secret_ciphertext, scopes, enabled, created_by, created_at, updated_at", ) .bind(params.command.as_deref()) .bind(params.description.as_deref()) .bind(params.request_url.as_deref()) .bind(params.secret.as_deref()) .bind(params.scopes.as_ref()) .bind(params.enabled) .bind(now) .bind(command_id) .fetch_one(self.ctx.db.writer()) .await .map_err(AppError::Database) } pub async fn slash_command_delete( &self, _ctx: &ImSession, command_id: Uuid, ) -> Result<(), AppError> { sqlx::query("DELETE FROM channel_slash_command WHERE id = $1") .bind(command_id) .execute(self.ctx.db.writer()) .await .map_err(AppError::Database)?; Ok(()) } }