Skip to content
Clone
main
134 lines 4.4 KB Rust
use models::issue::issue_relation_repo;
use models::repo::repos;
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, QueryOrder, 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::authz;
use crate::share::lookup::{lookup_board, lookup_issue, require_issue_access, space_context};
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct RepoRelDto {
pub id: Uuid,
pub issue_id: Uuid,
pub repo_id: Uuid,
pub created_at: OffsetDateTime,
}
impl From<issue_relation_repo::Model> for RepoRelDto {
fn from(m: issue_relation_repo::Model) -> Self {
Self {
id: m.id,
issue_id: m.issue_id,
repo_id: m.repo_id,
created_at: m.created_at,
}
}
}
#[derive(Debug, Clone, Deserialize, ToSchema)]
pub struct CreateRepoRelCmd {
pub repo_id: Uuid,
}
#[derive(Debug, Clone, Deserialize, IntoParams, ToSchema)]
pub struct RepoRelPath {
pub space_name: String,
pub board_id: Uuid,
pub issue_id: String,
pub rel_id: Uuid,
}
impl IssueService {
#[tracing::instrument(name = "issues.create_repo_relation", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, issue_id = %issue_id))]
pub async fn create_repo_relation(
&self,
caller: i64,
space_name: &str,
board_id: Uuid,
issue_id: Uuid,
cmd: CreateRepoRelCmd,
) -> Result<RepoRelDto, IssueError> {
let space = space_context(self, caller, space_name).await?;
authz::require_board_manage(self, &space, caller).await?;
let board = lookup_board(self, &space, board_id, caller).await?;
let _ = lookup_issue(self, &board, issue_id).await?;
let _ = repos::Entity::find_by_id(cmd.repo_id)
.one(self.database.read())
.await?
.ok_or(IssueError::NotFound)?;
let existing = issue_relation_repo::Entity::find()
.filter(issue_relation_repo::Column::IssueId.eq(issue_id))
.filter(issue_relation_repo::Column::RepoId.eq(cmd.repo_id))
.one(self.database.read())
.await?;
if existing.is_some() {
return Err(IssueError::Conflict(
"repo relation already exists".to_string(),
));
}
let now = OffsetDateTime::now_utc();
let row = issue_relation_repo::ActiveModel {
id: Set(Uuid::now_v7()),
issue_id: Set(issue_id),
repo_id: Set(cmd.repo_id),
created_at: Set(now),
};
let inserted = row.insert(self.database.write()).await?;
Ok(RepoRelDto::from(inserted))
}
pub async fn list_repo_relations(
&self,
caller: i64,
space_name: &str,
board_id: Uuid,
issue_id: Uuid,
) -> Result<Vec<RepoRelDto>, IssueError> {
require_issue_access(self, caller, space_name, board_id, issue_id).await?;
let rows = issue_relation_repo::Entity::find()
.filter(issue_relation_repo::Column::IssueId.eq(issue_id))
.order_by_asc(issue_relation_repo::Column::CreatedAt)
.all(self.database.read())
.await?;
Ok(rows.into_iter().map(RepoRelDto::from).collect())
}
#[tracing::instrument(name = "issues.delete_repo_relation", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, issue_id = %issue_id, rel_id = %rel_id))]
pub async fn delete_repo_relation(
&self,
caller: i64,
space_name: &str,
board_id: Uuid,
issue_id: Uuid,
rel_id: Uuid,
) -> Result<(), IssueError> {
let space = space_context(self, caller, space_name).await?;
authz::require_board_manage(self, &space, caller).await?;
let board = lookup_board(self, &space, board_id, caller).await?;
let _ = lookup_issue(self, &board, issue_id).await?;
let r = issue_relation_repo::Entity::find_by_id(rel_id)
.filter(issue_relation_repo::Column::IssueId.eq(issue_id))
.one(self.database.read())
.await?
.ok_or(IssueError::NotFound)?;
issue_relation_repo::Entity::delete_by_id(r.id)
.exec(self.database.write())
.await?;
Ok(())
}
}