httpd: Add cob state to listing endpoints
This commit adds the cob state of either patches or issues to the corresponding endpoint. So we can either query a cob listing with the required state or expect the default state. And once filtered by cob state we paginate. Signed-off-by: Sebastian Martinez <me@sebastinez.dev>
This commit is contained in:
parent
6f17a4db67
commit
8667553740
|
|
@ -14,8 +14,8 @@ use serde_json::json;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
use tower_http::cors::{self, CorsLayer};
|
use tower_http::cors::{self, CorsLayer};
|
||||||
|
|
||||||
use radicle::cob::issue::Issues;
|
use radicle::cob::issue;
|
||||||
use radicle::cob::patch::Patches;
|
use radicle::cob::patch;
|
||||||
use radicle::identity::Id;
|
use radicle::identity::Id;
|
||||||
use radicle::storage::{ReadRepository, ReadStorage};
|
use radicle::storage::{ReadRepository, ReadStorage};
|
||||||
use radicle::Profile;
|
use radicle::Profile;
|
||||||
|
|
@ -50,8 +50,8 @@ impl Context {
|
||||||
let doc = repo.identity_doc()?.1.verified()?;
|
let doc = repo.identity_doc()?.1.verified()?;
|
||||||
let payload = doc.project()?;
|
let payload = doc.project()?;
|
||||||
let delegates = doc.delegates;
|
let delegates = doc.delegates;
|
||||||
let issues = Issues::open(&repo)?.counts()?;
|
let issues = issue::Issues::open(&repo)?.counts()?;
|
||||||
let patches = Patches::open(&repo)?.counts()?;
|
let patches = patch::Patches::open(&repo)?.counts()?;
|
||||||
|
|
||||||
Ok(project::Info {
|
Ok(project::Info {
|
||||||
payload,
|
payload,
|
||||||
|
|
@ -115,6 +115,52 @@ pub struct PaginationQuery {
|
||||||
pub per_page: Option<usize>,
|
pub per_page: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct CobsQuery<T> {
|
||||||
|
pub page: Option<usize>,
|
||||||
|
pub per_page: Option<usize>,
|
||||||
|
pub state: Option<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Serialize, Deserialize, Clone)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub enum IssueState {
|
||||||
|
Closed,
|
||||||
|
#[default]
|
||||||
|
Open,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IssueState {
|
||||||
|
pub fn matches(&self, issue: &issue::State) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Open => matches!(issue, issue::State::Open),
|
||||||
|
Self::Closed => matches!(issue, issue::State::Closed { .. }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Serialize, Deserialize, Clone)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub enum PatchState {
|
||||||
|
#[default]
|
||||||
|
Open,
|
||||||
|
Draft,
|
||||||
|
Archived,
|
||||||
|
Merged,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PatchState {
|
||||||
|
pub fn matches(&self, patch: &patch::State) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Open => matches!(patch, patch::State::Open),
|
||||||
|
Self::Draft => matches!(patch, patch::State::Draft),
|
||||||
|
Self::Archived => matches!(patch, patch::State::Archived),
|
||||||
|
Self::Merged => matches!(patch, patch::State::Merged),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mod project {
|
mod project {
|
||||||
use nonempty::NonEmpty;
|
use nonempty::NonEmpty;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ use radicle_surf::{Glob, Oid, Repository};
|
||||||
|
|
||||||
use crate::api::error::Error;
|
use crate::api::error::Error;
|
||||||
use crate::api::project::Info;
|
use crate::api::project::Info;
|
||||||
use crate::api::{self, Context, PaginationQuery};
|
use crate::api::{self, CobsQuery, Context, PaginationQuery};
|
||||||
use crate::axum_extra::{Path, Query};
|
use crate::axum_extra::{Path, Query};
|
||||||
|
|
||||||
const CACHE_1_HOUR: &str = "public, max-age=3600, must-revalidate";
|
const CACHE_1_HOUR: &str = "public, max-age=3600, must-revalidate";
|
||||||
|
|
@ -416,15 +416,27 @@ async fn readme_handler(
|
||||||
async fn issues_handler(
|
async fn issues_handler(
|
||||||
State(ctx): State<Context>,
|
State(ctx): State<Context>,
|
||||||
Path(project): Path<Id>,
|
Path(project): Path<Id>,
|
||||||
Query(qs): Query<PaginationQuery>,
|
Query(qs): Query<CobsQuery<api::IssueState>>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
let PaginationQuery { page, per_page } = qs;
|
let CobsQuery {
|
||||||
|
page,
|
||||||
|
per_page,
|
||||||
|
state,
|
||||||
|
} = qs;
|
||||||
let page = page.unwrap_or(0);
|
let page = page.unwrap_or(0);
|
||||||
let per_page = per_page.unwrap_or(10);
|
let per_page = per_page.unwrap_or(10);
|
||||||
|
let state = state.unwrap_or_default();
|
||||||
let storage = &ctx.profile.storage;
|
let storage = &ctx.profile.storage;
|
||||||
let repo = storage.repository(project)?;
|
let repo = storage.repository(project)?;
|
||||||
let issues = issue::Issues::open(&repo)?;
|
let issues = issue::Issues::open(&repo)?;
|
||||||
let mut issues: Vec<_> = issues.all()?.filter_map(|r| r.ok()).collect::<Vec<_>>();
|
let mut issues: Vec<_> = issues
|
||||||
|
.all()?
|
||||||
|
.filter_map(|r| {
|
||||||
|
let (id, issue, clock) = r.ok()?;
|
||||||
|
(state.matches(issue.state())).then_some((id, issue, clock))
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
issues.sort_by(|(_, a, _), (_, b, _)| b.timestamp().cmp(&a.timestamp()));
|
issues.sort_by(|(_, a, _), (_, b, _)| b.timestamp().cmp(&a.timestamp()));
|
||||||
let issues = issues
|
let issues = issues
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
@ -687,15 +699,26 @@ async fn patch_update_handler(
|
||||||
async fn patches_handler(
|
async fn patches_handler(
|
||||||
State(ctx): State<Context>,
|
State(ctx): State<Context>,
|
||||||
Path(project): Path<Id>,
|
Path(project): Path<Id>,
|
||||||
Query(qs): Query<PaginationQuery>,
|
Query(qs): Query<CobsQuery<api::PatchState>>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
let PaginationQuery { page, per_page } = qs;
|
let CobsQuery {
|
||||||
|
page,
|
||||||
|
per_page,
|
||||||
|
state,
|
||||||
|
} = qs;
|
||||||
let page = page.unwrap_or(0);
|
let page = page.unwrap_or(0);
|
||||||
let per_page = per_page.unwrap_or(10);
|
let per_page = per_page.unwrap_or(10);
|
||||||
|
let state = state.unwrap_or_default();
|
||||||
let storage = &ctx.profile.storage;
|
let storage = &ctx.profile.storage;
|
||||||
let repo = storage.repository(project)?;
|
let repo = storage.repository(project)?;
|
||||||
let patches = patch::Patches::open(&repo)?;
|
let patches = patch::Patches::open(&repo)?;
|
||||||
let mut patches = patches.all()?.filter_map(|r| r.ok()).collect::<Vec<_>>();
|
let mut patches = patches
|
||||||
|
.all()?
|
||||||
|
.filter_map(|r| {
|
||||||
|
let (id, patch, clock) = r.ok()?;
|
||||||
|
(state.matches(&patch.state())).then_some((id, patch, clock))
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
patches.sort_by(|(_, a, _), (_, b, _)| b.timestamp().cmp(&a.timestamp()));
|
patches.sort_by(|(_, a, _), (_, b, _)| b.timestamp().cmp(&a.timestamp()));
|
||||||
let patches = patches
|
let patches = patches
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ impl std::fmt::Display for State {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Closed { .. } => write!(f, "closed"),
|
Self::Closed { .. } => write!(f, "closed"),
|
||||||
Self::Open { .. } => write!(f, "open"),
|
Self::Open => write!(f, "open"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -71,8 +71,8 @@ impl std::fmt::Display for State {
|
||||||
impl State {
|
impl State {
|
||||||
pub fn lifecycle_message(self) -> String {
|
pub fn lifecycle_message(self) -> String {
|
||||||
match self {
|
match self {
|
||||||
State::Open => "Open issue".to_owned(),
|
Self::Open => "Open issue".to_owned(),
|
||||||
State::Closed { .. } => "Close issue".to_owned(),
|
Self::Closed { .. } => "Close issue".to_owned(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue