From c798e5d4e919ed37e85283a8b8ba2ccd0d1540be Mon Sep 17 00:00:00 2001 From: Sebastian Martinez Date: Thu, 22 Aug 2024 09:44:51 +0200 Subject: [PATCH] http: Modify payloads fields Don't require a repo to have a `xyz.radicle.project` payload. If there are issues with obtaining the repo `head`, issue count or patch count, filter that repo from the list. --- radicle-httpd/src/api.rs | 46 +++-- radicle-httpd/src/api/v1/delegates.rs | 195 +++++++++---------- radicle-httpd/src/api/v1/repos.rs | 258 +++++++++++++------------- 3 files changed, 246 insertions(+), 253 deletions(-) diff --git a/radicle-httpd/src/api.rs b/radicle-httpd/src/api.rs index b061716e7..778c3449b 100644 --- a/radicle-httpd/src/api.rs +++ b/radicle-httpd/src/api.rs @@ -1,3 +1,4 @@ +use std::collections::BTreeMap; use std::sync::Arc; use std::time::Duration; @@ -6,11 +7,12 @@ use axum::http::Method; use axum::response::{IntoResponse, Json}; use axum::routing::get; use axum::Router; +use radicle::identity::doc::PayloadId; use radicle::issue::cache::Issues as _; use radicle::patch::cache::Patches as _; use radicle::storage::git::Repository; use serde::{Deserialize, Serialize}; -use serde_json::json; +use serde_json::{json, Value}; use tower_http::cors::{self, CorsLayer}; use radicle::cob::{issue, patch, Author}; @@ -52,7 +54,6 @@ impl Context { repo: &R, doc: DocAt, ) -> Result { - let (_, head) = repo.head()?; let DocAt { doc, .. } = doc; let rid = repo.id(); @@ -62,19 +63,33 @@ impl Context { .into_iter() .map(|did| json::author(&Author::new(did), aliases.alias(did.as_key()))) .collect::>(); - let issues = self.profile.issues(repo)?.counts()?; - let patches = self.profile.patches(repo)?.counts()?; let db = &self.profile.database()?; let seeding = db.count(&rid).unwrap_or_default(); + let payloads: BTreeMap = doc.payload.into_iter() + .filter_map(|(id, payload)| match id { + id if id == PayloadId::project() => { + let Ok((_, head)) = repo.head() else { + return None + }; + let (Ok(patches), Ok(issues)) = (self.profile.patches(repo), self.profile.issues(repo)) else { + return None + }; + let (Ok(patches), Ok(issues)) = (patches.counts(), issues.counts()) else { + return None + }; + + Some((id, json!({ "data": payload, "meta": { "head": head, "issues": issues, "patches": patches } } ))) + }, + _ => Some((id, json!({ "data": payload }))) + }) + .collect(); + Ok(repo::Info { - payload: doc.payload, + payloads, delegates, threshold: doc.threshold, visibility: doc.visibility, - head, - issues, - patches, rid, seeding, }) @@ -233,8 +248,7 @@ mod search { #[derive(Serialize, Deserialize, Eq, Debug)] pub struct SearchResult { pub rid: RepoId, - #[serde(flatten)] - pub payload: BTreeMap, + pub payloads: BTreeMap, pub delegates: NonEmpty, pub seeds: usize, #[serde(skip)] @@ -267,7 +281,7 @@ mod search { Some(SearchResult { rid: info.rid, - payload: info.doc.payload, + payloads: info.doc.payload, delegates, seeds, index, @@ -306,23 +320,17 @@ mod repo { use serde::Serialize; use serde_json::Value; - use radicle::cob; - use radicle::git::Oid; - use radicle::identity::doc::{Payload, PayloadId}; + use radicle::identity::doc::PayloadId; use radicle::identity::{RepoId, Visibility}; /// Repos info. #[derive(Serialize)] #[serde(rename_all = "camelCase")] pub struct Info { - #[serde(flatten)] - pub payload: BTreeMap, + pub payloads: BTreeMap, pub delegates: Vec, pub threshold: usize, pub visibility: Visibility, - pub head: Oid, - pub patches: cob::patch::PatchCounts, - pub issues: cob::issue::IssueCounts, pub rid: RepoId, pub seeding: usize, } diff --git a/radicle-httpd/src/api/v1/delegates.rs b/radicle-httpd/src/api/v1/delegates.rs index 466c894fe..f0552c2d6 100644 --- a/radicle-httpd/src/api/v1/delegates.rs +++ b/radicle-httpd/src/api/v1/delegates.rs @@ -3,17 +3,11 @@ use axum::response::IntoResponse; use axum::routing::get; use axum::{Json, Router}; -use radicle::cob::Author; use radicle::identity::Did; -use radicle::issue::cache::Issues as _; -use radicle::node::routing::Store; -use radicle::node::AliasStore; -use radicle::patch::cache::Patches as _; -use radicle::storage::{ReadRepository, ReadStorage}; +use radicle::storage::ReadStorage; use crate::api::error::Error; -use crate::api::repo::Info; -use crate::api::{json, Context, PaginationQuery, RepoQuery}; +use crate::api::{Context, PaginationQuery, RepoQuery}; use crate::axum_extra::{Path, Query}; pub fn router(ctx: Context) -> Router { @@ -37,7 +31,6 @@ async fn delegates_repos_handler( let page = page.unwrap_or(0); let per_page = per_page.unwrap_or(10); let storage = &ctx.profile.storage; - let db = &ctx.profile.database()?; let pinned = &ctx.profile.config.web.pinned; let mut repos = match show { RepoQuery::All => storage @@ -59,48 +52,14 @@ async fn delegates_repos_handler( if !id.doc.delegates.iter().any(|d| *d == delegate) { return None; } - let Ok(repo) = storage.repository(id.rid) else { + let Ok((repo, doc)) = ctx.repo(id.rid) else { return None; }; - let Ok((_, head)) = repo.head() else { - return None; - }; - let Ok(_payload) = id.doc.project() else { - return None; - }; - let Ok(issues) = ctx.profile.issues(&repo) else { - return None; - }; - let Ok(issues) = issues.counts() else { - return None; - }; - let Ok(patches) = ctx.profile.patches(&repo) else { - return None; - }; - let Ok(patches) = patches.counts() else { + let Ok(repo_info) = ctx.repo_info(&repo, doc) else { return None; }; - let aliases = ctx.profile.aliases(); - let delegates = id - .doc - .delegates - .into_iter() - .map(|did| json::author(&Author::new(did), aliases.alias(did.as_key()))) - .collect::>(); - let seeding = db.count(&id.rid).unwrap_or_default(); - - Some(Info { - payload: id.doc.payload, - delegates, - threshold: id.doc.threshold, - visibility: id.doc.visibility, - head, - issues, - patches, - rid: id.rid, - seeding, - }) + Some(repo_info) }) .skip(page * per_page) .take(per_page) @@ -141,10 +100,27 @@ mod routes { response.json().await, json!([ { - "xyz.radicle.project": { - "defaultBranch": "master", - "description": "Rad repository for tests", - "name": "hello-world", + "payloads": { + "xyz.radicle.project": { + "data": { + "defaultBranch": "master", + "description": "Rad repository for tests", + "name": "hello-world", + }, + "meta": { + "head": HEAD, + "patches": { + "open": 1, + "draft": 0, + "archived": 0, + "merged": 0, + }, + "issues": { + "open": 1, + "closed": 0, + }, + } + } }, "delegates": [ { @@ -156,25 +132,31 @@ mod routes { "visibility": { "type": "public" }, - "head": HEAD, - "patches": { - "open": 1, - "draft": 0, - "archived": 0, - "merged": 0, - }, - "issues": { - "open": 1, - "closed": 0, - }, "rid": RID, "seeding": 1, }, { - "xyz.radicle.project": { - "defaultBranch": "master", - "description": "Rad repository for sorting", - "name": "again-hello-world", + "payloads": { + "xyz.radicle.project": { + "data": { + "defaultBranch": "master", + "description": "Rad repository for sorting", + "name": "again-hello-world", + }, + "meta": { + "head": "344dcd184df5bf37aab6c107fa9371a1c5b3321a", + "patches": { + "open": 0, + "draft": 0, + "archived": 0, + "merged": 0, + }, + "issues": { + "open": 0, + "closed": 0, + }, + } + } }, "delegates": [ { @@ -186,17 +168,6 @@ mod routes { "visibility": { "type": "public" }, - "head": "344dcd184df5bf37aab6c107fa9371a1c5b3321a", - "patches": { - "open": 0, - "draft": 0, - "archived": 0, - "merged": 0, - }, - "issues": { - "open": 0, - "closed": 0, - }, "rid": "rad:z4GypKmh1gkEfmkXtarcYnkvtFUfE", "seeding": 1, } @@ -223,10 +194,27 @@ mod routes { response.json().await, json!([ { - "xyz.radicle.project": { - "defaultBranch": "master", - "description": "Rad repository for tests", - "name": "hello-world", + "payloads": { + "xyz.radicle.project": { + "data": { + "defaultBranch": "master", + "description": "Rad repository for tests", + "name": "hello-world", + }, + "meta": { + "head": HEAD, + "patches": { + "open": 1, + "draft": 0, + "archived": 0, + "merged": 0, + }, + "issues": { + "open": 1, + "closed": 0, + }, + } + } }, "delegates": [ { @@ -238,25 +226,31 @@ mod routes { "visibility": { "type": "public" }, - "head": HEAD, - "patches": { - "open": 1, - "draft": 0, - "archived": 0, - "merged": 0, - }, - "issues": { - "open": 1, - "closed": 0, - }, "rid": RID, "seeding": 1, }, { - "xyz.radicle.project": { - "defaultBranch": "master", - "description": "Rad repository for sorting", - "name": "again-hello-world", + "payloads": { + "xyz.radicle.project": { + "data": { + "defaultBranch": "master", + "description": "Rad repository for sorting", + "name": "again-hello-world", + }, + "meta": { + "head": "344dcd184df5bf37aab6c107fa9371a1c5b3321a", + "patches": { + "open": 0, + "draft": 0, + "archived": 0, + "merged": 0, + }, + "issues": { + "open": 0, + "closed": 0, + }, + } + } }, "delegates": [ { @@ -268,17 +262,6 @@ mod routes { "visibility": { "type": "public" }, - "head": "344dcd184df5bf37aab6c107fa9371a1c5b3321a", - "patches": { - "open": 0, - "draft": 0, - "archived": 0, - "merged": 0, - }, - "issues": { - "open": 0, - "closed": 0, - }, "rid": "rad:z4GypKmh1gkEfmkXtarcYnkvtFUfE", "seeding": 1, } diff --git a/radicle-httpd/src/api/v1/repos.rs b/radicle-httpd/src/api/v1/repos.rs index e2f987efb..f910631fd 100644 --- a/radicle-httpd/src/api/v1/repos.rs +++ b/radicle-httpd/src/api/v1/repos.rs @@ -11,9 +11,8 @@ use radicle_surf::{diff, Glob, Oid, Repository}; use serde::{Deserialize, Serialize}; use serde_json::json; -use radicle::cob::{issue::cache::Issues as _, patch::cache::Patches as _, Author}; +use radicle::cob::{issue::cache::Issues as _, patch::cache::Patches as _}; use radicle::identity::RepoId; -use radicle::node::routing::Store; use radicle::node::{AliasStore, NodeId}; use radicle::storage::{ReadRepository, ReadStorage, RemoteRepository}; @@ -66,7 +65,6 @@ async fn repo_root_handler( _ => 10, }); let storage = &ctx.profile.storage; - let db = &ctx.profile.database()?; let pinned = &ctx.profile.config.web.pinned; let policies = ctx.profile.policies()?; @@ -90,47 +88,14 @@ async fn repo_root_handler( if !policies.is_seeding(&info.rid).unwrap_or_default() { return None; } - let Ok(repo) = storage.repository(info.rid) else { + let Ok((repo, doc)) = ctx.repo(info.rid) else { return None; }; - let Ok((_, head)) = repo.head() else { + let Ok(repo_info) = ctx.repo_info(&repo, doc) else { return None; }; - let Ok(_payload) = info.doc.project() else { - return None; - }; - let Ok(issues) = ctx.profile.issues(&repo) else { - return None; - }; - let Ok(issues) = issues.counts() else { - return None; - }; - let Ok(patches) = ctx.profile.patches(&repo) else { - return None; - }; - let Ok(patches) = patches.counts() else { - return None; - }; - let aliases = ctx.profile.aliases(); - let delegates = info - .doc - .delegates - .into_iter() - .map(|did| api::json::author(&Author::new(did), aliases.alias(did.as_key()))) - .collect::>(); - let seeding = db.count(&info.rid).unwrap_or_default(); - Some(api::repo::Info { - payload: info.doc.payload, - delegates, - head, - threshold: info.doc.threshold, - visibility: info.doc.visibility, - issues, - patches, - rid: info.rid, - seeding, - }) + Some(repo_info) }) .skip(page * per_page) .take(per_page) @@ -201,7 +166,8 @@ async fn history_handler( Path(rid): Path, Query(qs): Query, ) -> impl IntoResponse { - let (repo, doc) = ctx.repo(rid)?; + let (repo, _) = ctx.repo(rid)?; + let (_, head) = repo.head()?; let CommitsQueryString { since, until, @@ -217,7 +183,7 @@ async fn history_handler( let sha = match parent { Some(commit) => commit, - None => ctx.repo_info(&repo, doc)?.head.to_string(), + None => head.to_string(), }; let repo = Repository::open(repo.path())?; @@ -732,10 +698,27 @@ mod routes { response.json().await, json!([ { - "xyz.radicle.project": { - "defaultBranch": "master", - "description": "Rad repository for tests", - "name": "hello-world", + "payloads": { + "xyz.radicle.project": { + "data": { + "defaultBranch": "master", + "description": "Rad repository for tests", + "name": "hello-world", + }, + "meta": { + "head": HEAD, + "patches": { + "open": 1, + "draft": 0, + "archived": 0, + "merged": 0, + }, + "issues": { + "open": 1, + "closed": 0, + }, + } + } }, "delegates": [ { @@ -747,25 +730,31 @@ mod routes { "visibility": { "type": "public" }, - "head": HEAD, - "patches": { - "open": 1, - "draft": 0, - "archived": 0, - "merged": 0, - }, - "issues": { - "open": 1, - "closed": 0, - }, "rid": RID, "seeding": 1, }, { - "xyz.radicle.project": { - "defaultBranch": "master", - "description": "Rad repository for sorting", - "name": "again-hello-world", + "payloads": { + "xyz.radicle.project": { + "data": { + "defaultBranch": "master", + "description": "Rad repository for sorting", + "name": "again-hello-world", + }, + "meta": { + "head": "344dcd184df5bf37aab6c107fa9371a1c5b3321a", + "patches": { + "open": 0, + "draft": 0, + "archived": 0, + "merged": 0, + }, + "issues": { + "open": 0, + "closed": 0, + }, + } + } }, "delegates": [ { @@ -777,17 +766,6 @@ mod routes { "visibility": { "type": "public" }, - "head": "344dcd184df5bf37aab6c107fa9371a1c5b3321a", - "patches": { - "open": 0, - "draft": 0, - "archived": 0, - "merged": 0, - }, - "issues": { - "open": 0, - "closed": 0, - }, "rid": "rad:z4GypKmh1gkEfmkXtarcYnkvtFUfE", "seeding": 1, }, @@ -805,10 +783,27 @@ mod routes { response.json().await, json!([ { - "xyz.radicle.project": { - "defaultBranch": "master", - "description": "Rad repository for tests", - "name": "hello-world", + "payloads": { + "xyz.radicle.project": { + "data": { + "defaultBranch": "master", + "description": "Rad repository for tests", + "name": "hello-world", + }, + "meta": { + "head": HEAD, + "patches": { + "open": 1, + "draft": 0, + "archived": 0, + "merged": 0, + }, + "issues": { + "open": 1, + "closed": 0, + }, + } + } }, "delegates": [ { @@ -820,25 +815,31 @@ mod routes { "visibility": { "type": "public" }, - "head": HEAD, - "patches": { - "open": 1, - "draft": 0, - "archived": 0, - "merged": 0, - }, - "issues": { - "open": 1, - "closed": 0, - }, "rid": RID, "seeding": 1, }, { - "xyz.radicle.project": { - "name": "again-hello-world", - "description": "Rad repository for sorting", - "defaultBranch": "master", + "payloads": { + "xyz.radicle.project": { + "data": { + "name": "again-hello-world", + "description": "Rad repository for sorting", + "defaultBranch": "master", + }, + "meta": { + "head": "344dcd184df5bf37aab6c107fa9371a1c5b3321a", + "patches": { + "open": 0, + "draft": 0, + "archived": 0, + "merged": 0, + }, + "issues": { + "open": 0, + "closed": 0, + }, + } + } }, "delegates": [ { @@ -850,17 +851,6 @@ mod routes { "visibility": { "type": "public" }, - "head": "344dcd184df5bf37aab6c107fa9371a1c5b3321a", - "patches": { - "open": 0, - "draft": 0, - "archived": 0, - "merged": 0, - }, - "issues": { - "open": 0, - "closed": 0, - }, "rid": "rad:z4GypKmh1gkEfmkXtarcYnkvtFUfE", "seeding": 1, }, @@ -878,10 +868,27 @@ mod routes { assert_eq!( response.json().await, json!({ - "xyz.radicle.project": { - "defaultBranch": "master", - "description": "Rad repository for tests", - "name": "hello-world", + "payloads": { + "xyz.radicle.project": { + "data": { + "defaultBranch": "master", + "description": "Rad repository for tests", + "name": "hello-world", + }, + "meta": { + "head": HEAD, + "patches": { + "open": 1, + "draft": 0, + "archived": 0, + "merged": 0, + }, + "issues": { + "open": 1, + "closed": 0, + }, + } + } }, "delegates": [ { @@ -893,17 +900,6 @@ mod routes { "visibility": { "type": "public" }, - "head": HEAD, - "patches": { - "open": 1, - "draft": 0, - "archived": 0, - "merged": 0, - }, - "issues": { - "open": 1, - "closed": 0, - }, "rid": RID, "seeding": 1, }) @@ -921,10 +917,12 @@ mod routes { response.json().await, json!([ { - "xyz.radicle.project": { - "name": "hello-world", - "description": "Rad repository for tests", - "defaultBranch": "master", + "payloads": { + "xyz.radicle.project": { + "name": "hello-world", + "description": "Rad repository for tests", + "defaultBranch": "master", + } }, "rid": "rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp", "delegates": [ @@ -936,10 +934,12 @@ mod routes { "seeds": 1, }, { - "xyz.radicle.project": { - "name": "again-hello-world", - "description": "Rad repository for sorting", - "defaultBranch": "master", + "payloads": { + "xyz.radicle.project": { + "name": "again-hello-world", + "description": "Rad repository for sorting", + "defaultBranch": "master", + }, }, "rid": "rad:z4GypKmh1gkEfmkXtarcYnkvtFUfE", "delegates": [ @@ -966,10 +966,12 @@ mod routes { json!([ { "rid": "rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp", - "xyz.radicle.project": { - "defaultBranch": "master", - "description": "Rad repository for tests", - "name": "hello-world", + "payloads": { + "xyz.radicle.project": { + "defaultBranch": "master", + "description": "Rad repository for tests", + "name": "hello-world", + }, }, "delegates": [ {