main
use api::users::UserDto;
use models::common::watch_mode::WatchMode;
use models::issue::issue_watch;
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set};
use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use utoipa::{IntoParams, ToSchema};
use uuid::Uuid;
use crate::errors::IssueError;
use crate::services::service::IssueService;
use crate::share::lookup;
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct WatchDto {
pub id: Uuid,
pub issue_id: Option<Uuid>,
pub user: Option<UserDto>,
pub mode: WatchMode,
pub created_at: OffsetDateTime,
pub updated_at: Option<OffsetDateTime>,
}
#[derive(Debug, Clone, Deserialize, ToSchema)]
pub struct UpsertWatchCmd {
pub mode: WatchMode,
}
#[derive(Debug, Clone, Deserialize, IntoParams, ToSchema)]
pub struct WatchPath {
pub space_name: String,
pub board_id: Uuid,
pub issue_id: String,
}
impl IssueService {
#[tracing::instrument(name = "issues.upsert_watch", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, issue_id = %issue_id))]
pub async fn upsert_watch(
&self,
caller: i64,
space_name: &str,
board_id: Uuid,
issue_id: Uuid,
cmd: UpsertWatchCmd,
) -> Result<WatchDto, IssueError> {
lookup::require_issue_access(self, caller, space_name, board_id, issue_id).await?;
let existing = issue_watch::Entity::find()
.filter(issue_watch::Column::IssueId.eq(Some(issue_id)))
.filter(issue_watch::Column::UserId.eq(caller))
.one(self.database.read())
.await?;
let now = OffsetDateTime::now_utc();
let (id, created_at, updated_at) = if let Some(w) = existing {
let mut am: issue_watch::ActiveModel = w.into();
am.mode = Set(cmd.mode);
am.updated_at = Set(Some(now));
let u = am.update(self.database.write()).await?;
(u.id, u.created_at, u.updated_at)
} else {
let row = issue_watch::ActiveModel {
id: Set(Uuid::now_v7()),
issue_id: Set(Some(issue_id)),
project_id: Set(None),
user_id: Set(caller),
mode: Set(cmd.mode),
created_at: Set(now),
updated_at: Set(None),
};
let ins = row.insert(self.database.write()).await?;
(ins.id, ins.created_at, ins.updated_at)
};
let user_map = self.audience.load_user_briefs(&[caller]).await?;
let user = user_map.get(&caller).cloned();
Ok(WatchDto {
id,
issue_id: Some(issue_id),
user,
mode: cmd.mode,
created_at,
updated_at,
})
}
#[tracing::instrument(name = "issues.delete_watch", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, issue_id = %issue_id))]
pub async fn delete_watch(
&self,
caller: i64,
space_name: &str,
board_id: Uuid,
issue_id: Uuid,
) -> Result<(), IssueError> {
lookup::require_issue_access(self, caller, space_name, board_id, issue_id).await?;
let res = issue_watch::Entity::delete_many()
.filter(issue_watch::Column::IssueId.eq(Some(issue_id)))
.filter(issue_watch::Column::UserId.eq(caller))
.exec(self.database.write())
.await?;
if res.rows_affected == 0 {
return Err(IssueError::NotFound);
}
Ok(())
}
}