use api::pagination::Page; use models::pull_request::{pull_request_review, pull_request_reviewer, pull_requests}; use sea_orm::{ ColumnTrait, EntityTrait, PaginatorTrait, QueryFilter, QueryOrder, QuerySelect, Select, }; use serde::{Deserialize, Serialize}; use utoipa::{IntoParams, ToSchema}; use uuid::Uuid; use crate::errors::PullError; use crate::services::pulls::PullDto; use crate::services::service::PullService; #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "kebab-case")] pub enum InboxScope { Created, ReviewRequested, Reviewed, } #[derive(Debug, Clone, Default, Deserialize, IntoParams)] #[serde(deny_unknown_fields)] pub struct InboxQuery { /// open | closed | merged | all #[serde(default)] pub state: Option, #[serde(default)] pub limit: Option, #[serde(default)] pub offset: Option, } pub type InboxPage = Page; impl PullService { pub async fn list_inbox( &self, caller: i64, scope: InboxScope, query: InboxQuery, ) -> Result { let (limit, offset) = api::pagination::page_bounds(query.limit, query.offset, 50, 100)?; let base: Select = match scope { InboxScope::Created => { pull_requests::Entity::find().filter(pull_requests::Column::AuthorId.eq(caller)) } InboxScope::ReviewRequested => { let ids: Vec = pull_request_reviewer::Entity::find() .filter(pull_request_reviewer::Column::UserId.eq(caller)) .all(self.db_read()) .await? .into_iter() .map(|row| row.pull_request_id) .collect(); pull_requests::Entity::find().filter(pull_requests::Column::Id.is_in(ids)) } InboxScope::Reviewed => { let ids: Vec = pull_request_review::Entity::find() .filter(pull_request_review::Column::UserId.eq(caller)) .all(self.db_read()) .await? .into_iter() .map(|row| row.pull_request_id) .collect(); pull_requests::Entity::find().filter(pull_requests::Column::Id.is_in(ids)) } }; let mut select = match query.state.as_deref().unwrap_or("open") { "all" => base, "closed" => base .filter(pull_requests::Column::HasMerged.eq(false)) .filter(pull_requests::Column::ClosedAt.is_not_null()), "merged" => base.filter(pull_requests::Column::HasMerged.eq(true)), "open" => base .filter(pull_requests::Column::HasMerged.eq(false)) .filter(pull_requests::Column::ClosedAt.is_null()), other => { return Err(PullError::BadRequest(format!("unknown state `{other}`"))); } }; let total = select.clone().count(self.db_read()).await?; select = select.order_by_desc(pull_requests::Column::UpdatedAt); let rows = select .limit(limit) .offset(offset) .all(self.db_read()) .await?; let users = self.pull_users(&rows).await?; Ok(Page { items: rows .iter() .map(|row| PullService::dto(row, &users)) .collect(), total, limit, offset, }) } }