gitdataai/lib/service/git/commit_status.rs

182 lines
5.2 KiB
Rust

use chrono::Utc;
use db::sqlx;
use model::repos::RepoCommitStatusModel;
use session::Session;
use uuid::Uuid;
use crate::AppService;
use crate::error::AppError;
#[derive(Debug, Clone, serde::Serialize, utoipa::ToSchema)]
pub struct CommitStatusResponse {
pub id: Uuid,
pub commit_sha: String,
pub state: String,
pub target_url: Option<String>,
pub description: Option<String>,
pub context: String,
pub creator: Uuid,
pub created_at: chrono::DateTime<Utc>,
}
#[derive(Debug, Clone, serde::Serialize, utoipa::ToSchema)]
pub struct CombinedCommitStatus {
pub sha: String,
pub state: String,
pub total_count: i64,
pub statuses: Vec<CommitStatusResponse>,
}
#[derive(Debug, Clone, serde::Deserialize, utoipa::ToSchema)]
pub struct CreateCommitStatus {
pub state: String,
pub target_url: Option<String>,
pub description: Option<String>,
pub context: Option<String>,
}
impl AppService {
pub async fn git_commit_status_list(
&self,
repo_id: Uuid,
commit_sha: &str,
) -> Result<Vec<CommitStatusResponse>, AppError> {
let rows = sqlx::query_as::<_, RepoCommitStatusModel>(
"SELECT id, repo, commit_sha, state, target_url, description, \
context, creator, created_at, updated_at \
FROM repo_commit_status \
WHERE repo = $1 AND commit_sha = $2 \
ORDER BY created_at DESC",
)
.bind(repo_id)
.bind(commit_sha)
.fetch_all(self.db.reader())
.await
.map_err(|e| AppError::DatabaseError(e.to_string()))?;
Ok(rows.into_iter().map(status_to_response).collect())
}
pub async fn git_commit_status_combined(
&self,
repo_id: Uuid,
commit_sha: &str,
) -> Result<CombinedCommitStatus, AppError> {
let statuses = self.git_commit_status_list(repo_id, commit_sha).await?;
let state = combined_state(&statuses);
Ok(CombinedCommitStatus {
sha: commit_sha.to_string(),
state,
total_count: statuses.len() as i64,
statuses,
})
}
pub async fn git_commit_status_create(
&self,
repo_id: Uuid,
user_id: Uuid,
commit_sha: &str,
params: CreateCommitStatus,
) -> Result<CommitStatusResponse, AppError> {
if !["pending", "success", "failure", "error"]
.contains(&params.state.as_str())
{
return Err(AppError::BadRequest(
"state must be one of: pending, success, failure, error"
.to_string(),
));
}
let id = Uuid::now_v7();
let now = Utc::now();
let context = params.context.unwrap_or_else(|| "default".to_string());
let row = sqlx::query_as::<_, RepoCommitStatusModel>(
"INSERT INTO repo_commit_status (id, repo, commit_sha, state, target_url, \
description, context, creator, created_at, updated_at) \
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$9) RETURNING *",
)
.bind(id)
.bind(repo_id)
.bind(commit_sha)
.bind(&params.state)
.bind(&params.target_url)
.bind(&params.description)
.bind(&context)
.bind(user_id)
.bind(now)
.fetch_one(self.db.writer())
.await
.map_err(|e| AppError::DatabaseError(e.to_string()))?;
Ok(status_to_response(row))
}
}
impl AppService {
pub async fn git_commit_status_list_by_name(
&self,
ctx: &Session,
wk: &str,
repo: &str,
sha: &str,
) -> Result<Vec<CommitStatusResponse>, AppError> {
let repo = self.git_require_member(ctx, wk, repo).await?;
self.git_commit_status_list(repo.id, sha).await
}
pub async fn git_commit_status_combined_by_name(
&self,
ctx: &Session,
wk: &str,
repo: &str,
sha: &str,
) -> Result<CombinedCommitStatus, AppError> {
let repo = self.git_require_member(ctx, wk, repo).await?;
self.git_commit_status_combined(repo.id, sha).await
}
pub async fn git_commit_status_create_by_name(
&self,
ctx: &Session,
user_id: Uuid,
wk: &str,
repo: &str,
sha: &str,
params: CreateCommitStatus,
) -> Result<CommitStatusResponse, AppError> {
let repo = self.git_require_member(ctx, wk, repo).await?;
self.git_commit_status_create(repo.id, user_id, sha, params)
.await
}
}
fn status_to_response(s: RepoCommitStatusModel) -> CommitStatusResponse {
CommitStatusResponse {
id: s.id,
commit_sha: s.commit_sha,
state: s.state,
target_url: s.target_url,
description: s.description,
context: s.context,
creator: s.creator,
created_at: s.created_at,
}
}
fn combined_state(statuses: &[CommitStatusResponse]) -> String {
if statuses.is_empty() {
return "pending".to_string();
}
let has = |s: &str| statuses.iter().any(|st| st.state == s);
(if has("error") {
"error"
} else if has("failure") {
"failure"
} else if has("pending") {
"pending"
} else {
"success"
})
.to_string()
}