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.
This commit is contained in:
Sebastian Martinez
2024-08-22 09:44:51 +02:00
parent 868f7b1e2d
commit c798e5d4e9
3 changed files with 246 additions and 253 deletions
+27 -19
View File
@@ -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<repo::Info, error::Error> {
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::<Vec<_>>();
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<PayloadId, Value> = 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<PayloadId, Payload>,
pub payloads: BTreeMap<PayloadId, Payload>,
pub delegates: NonEmpty<serde_json::Value>,
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<PayloadId, Payload>,
pub payloads: BTreeMap<PayloadId, Value>,
pub delegates: Vec<Value>,
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,
}
+89 -106
View File
@@ -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::<Vec<_>>();
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,
}
+130 -128
View File
@@ -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::<Vec<_>>();
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<RepoId>,
Query(qs): Query<CommitsQueryString>,
) -> 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": [
{