eeff292
use api::pagination::{Page, PageQuery};
use api::users::UserDto;
use models::pull_request::{pull_request_event_line, pull_requests};
use sea_orm::ActiveValue::Set;
use sea_orm::{
ActiveModelTrait, ColumnTrait, ConnectionTrait, EntityTrait, PaginatorTrait, QueryFilter,
QueryOrder, QuerySelect,
};
use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use uuid::Uuid;
use crate::errors::PullError;
use crate::services::service::PullService;
pub const EVENT_CREATED: &str = "created";
pub const EVENT_UPDATED: &str = "updated";
pub const EVENT_CLOSED: &str = "closed";
pub const EVENT_REOPENED: &str = "reopened";
pub const EVENT_MERGED: &str = "merged";
pub const EVENT_COMMENTED: &str = "commented";
pub const EVENT_REVIEW_SUBMITTED: &str = "review_submitted";
pub const EVENT_REVIEW_DISMISSED: &str = "review_dismissed";
pub const EVENT_REVIEW_REQUESTED: &str = "review_requested";
pub const EVENT_REVIEW_REQUEST_CANCELLED: &str = "review_request_cancelled";
pub const EVENT_HEAD_UPDATED: &str = "head_updated";
pub const EVENT_LABELED: &str = "labeled";
pub const EVENT_UNLABELED: &str = "unlabeled";
pub const EVENT_ASSIGNED: &str = "assigned";
pub const EVENT_UNASSIGNED: &str = "unassigned";
pub const EVENT_LOCKED: &str = "locked";
pub const EVENT_UNLOCKED: &str = "unlocked";
pub const EVENT_AUTO_MERGE_ENABLED: &str = "auto_merge_enabled";
pub const EVENT_AUTO_MERGE_DISABLED: &str = "auto_merge_disabled";
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct PullEventDto {
pub id: Uuid,
pub pull_request_id: Uuid,
pub event_type: String,
pub actor: Option<UserDto>,
pub commit_sha: Option<String>,
pub content: Option<String>,
pub created_at: OffsetDateTime,
}
pub type EventPage = Page<PullEventDto>;
pub async fn write_event(
db: &impl ConnectionTrait,
pull: &pull_requests::Model,
actor: Option<i64>,
event_type: &str,
commit_sha: Option<String>,
content: Option<String>,
) -> Result<(), sea_orm::DbErr> {
let actor = match actor {
Some(actor) => actor,
None => pull.author_id,
};
let payload = serde_json::json!({
"pull_request_id": pull.id,
"user_id": actor,
"event_type": event_type,
"commit_sha": &commit_sha,
"content": &content,
"pull_request": {
"id": pull.id,
"repo_id": pull.repo_id,
"issue_id": pull.issue_id,
"number": pull.autoincrement_id,
"author_id": pull.author_id,
"title": &pull.title,
"body": &pull.body,
"head_branch": &pull.head_branch,
"head_sha": &pull.head_sha,
"head_label": &pull.head_label,
"base_branch": &pull.base_branch,
"base_sha": &pull.base_sha,
"is_draft": pull.is_draft,
"merge_method": pull.merge_method,
"has_merged": pull.has_merged,
"merged_commit_sha": &pull.merged_commit_sha,
"squash_commit_sha": &pull.squash_commit_sha,
"auto_merge": pull.auto_merge,
"additions": pull.additions,
"deletions": pull.deletions,
"files_changed": pull.files_changed,
"merger_id": pull.merger_id,
"merged_at": pull.merged_at,
"is_locked": pull.is_locked,
"created_at": pull.created_at,
"updated_at": pull.updated_at,
"closed_at": pull.closed_at,
},
});
let row = pull_request_event_line::ActiveModel {
id: Set(Uuid::now_v7()),
pull_request_id: Set(pull.id),
user_id: Set(actor),
event_type: Set(event_type.to_string()),
commit_sha: Set(commit_sha),
content: Set(content),
created_at: Set(OffsetDateTime::now_utc()),
};
row.insert(db).await?;
let Some(kind) = event_kind(event_type) else {
return Ok(());
};
let labels = queue::Labels::new()
.with("pull_id", pull.id)
.with("user_id", actor);
events::emit(db, kind, labels, payload).await.map(|_| ())
}
fn event_kind(event_type: &str) -> Option<events::EventKind> {
Some(match event_type {
EVENT_CREATED => events::EventKind::PullPullCreate,
EVENT_UPDATED => events::EventKind::PullPullUpdate,
EVENT_CLOSED => events::EventKind::PullPullClose,
EVENT_REOPENED => events::EventKind::PullPullReopen,
EVENT_MERGED => events::EventKind::PullPullMerge,
EVENT_COMMENTED => events::EventKind::PullCommentCreate,
EVENT_REVIEW_SUBMITTED => events::EventKind::PullReviewCreate,
EVENT_REVIEW_DISMISSED => events::EventKind::PullReviewDelete,
EVENT_REVIEW_REQUESTED => events::EventKind::PullReviewRequestCreate,
EVENT_REVIEW_REQUEST_CANCELLED => events::EventKind::PullReviewRequestDelete,
EVENT_HEAD_UPDATED => events::EventKind::PullHeadUpdate,
EVENT_LABELED => events::EventKind::PullLabelCreate,
EVENT_UNLABELED => events::EventKind::PullLabelDelete,
EVENT_ASSIGNED => events::EventKind::PullAssigneeCreate,
EVENT_UNASSIGNED => events::EventKind::PullAssigneeDelete,
EVENT_LOCKED => events::EventKind::PullLockCreate,
EVENT_UNLOCKED => events::EventKind::PullLockDelete,
EVENT_AUTO_MERGE_ENABLED => events::EventKind::PullAutoMergeCreate,
EVENT_AUTO_MERGE_DISABLED => events::EventKind::PullAutoMergeDelete,
_ => return None,
})
}
impl PullService {
pub async fn record_event(
&self,
pull: &pull_requests::Model,
actor: Option<i64>,
event_type: &str,
commit_sha: Option<String>,
content: Option<String>,
) -> Result<(), PullError> {
self.record_event_with(
self.db_write(),
pull,
actor,
event_type,
commit_sha,
content,
)
.await
}
pub(crate) async fn record_event_with(
&self,
conn: &impl ConnectionTrait,
pull: &pull_requests::Model,
actor: Option<i64>,
event_type: &str,
commit_sha: Option<String>,
content: Option<String>,
) -> Result<(), PullError> {
write_event(conn, pull, actor, event_type, commit_sha, content).await?;
Ok(())
}
pub async fn list_events(
&self,
pull_id: Uuid,
page: PageQuery,
) -> Result<EventPage, PullError> {
let (limit, offset) = page.bounds()?;
let total = pull_request_event_line::Entity::find()
.filter(pull_request_event_line::Column::PullRequestId.eq(pull_id))
.count(self.db_read())
.await?;
let rows = pull_request_event_line::Entity::find()
.filter(pull_request_event_line::Column::PullRequestId.eq(pull_id))
.order_by_asc(pull_request_event_line::Column::CreatedAt)
.limit(limit)
.offset(offset)
.all(self.db_read())
.await?;
let actors = self
.actors
.briefs(&{
let rows = &rows;
let mut ids: Vec<i64> = rows.iter().map(|row| row.user_id).collect();
ids.sort();
ids.dedup();
ids
})
.await?;
let items = rows
.into_iter()
.map(|row| PullEventDto {
id: row.id,
pull_request_id: row.pull_request_id,
event_type: row.event_type,
commit_sha: row.commit_sha,
content: row.content,
created_at: row.created_at,
actor: actors.get(&row.user_id).cloned(),
})
.collect();
Ok(Page {
items,
total,
limit,
offset,
})
}
}