Skip to content
This repository has been archived by the owner on Apr 29, 2024. It is now read-only.

Commit

Permalink
radicle: pinned repositories configuration
Browse files Browse the repository at this point in the history
Introduce pinned repositories to the `profile::Config`, i.e.
`config.json`.

This is used for pinning repositories in the httpd `/projects` handle.

To allow returning all repositories, an `all` query parameter is also
included.

This is non-backwards compatible change, since we introduce the `show`
query parameter which, if not supplied, will default to `pinned`.

Signed-off-by: Fintan Halpenny <[email protected]>
X-Clacks-Overhead: GNU Terry Pratchett
  • Loading branch information
FintanH authored and cloudhead committed Jan 24, 2024
1 parent d722638 commit 49584f4
Show file tree
Hide file tree
Showing 11 changed files with 119 additions and 29 deletions.
5 changes: 5 additions & 0 deletions radicle-cli/examples/rad-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ $ rad config
{
"publicExplorer": "https://app.radicle.xyz/nodes/$host/$rid$path",
"preferredSeeds": [],
"web": {
"pinned": {
"repositories": []
}
},
"cli": {
"hints": false
},
Expand Down
10 changes: 10 additions & 0 deletions radicle-httpd/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,20 @@ async fn root_handler() -> impl IntoResponse {
#[derive(Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct PaginationQuery {
#[serde(default)]
pub show: ProjectQuery,
pub page: Option<usize>,
pub per_page: Option<usize>,
}

#[derive(Serialize, Deserialize, Clone, Default)]
#[serde(rename_all = "camelCase")]
pub enum ProjectQuery {
All,
#[default]
Pinned,
}

#[derive(Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct RawQuery {
Expand Down
47 changes: 33 additions & 14 deletions radicle-httpd/src/api/v1/delegates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use radicle::storage::{ReadRepository, ReadStorage};
use crate::api::error::Error;
use crate::api::project::Info;
use crate::api::Context;
use crate::api::PaginationQuery;
use crate::api::{PaginationQuery, ProjectQuery};
use crate::axum_extra::{Path, Query};

pub fn router(ctx: Context) -> Router {
Expand All @@ -34,19 +34,28 @@ async fn delegates_projects_handler(
Path(delegate): Path<Did>,
Query(qs): Query<PaginationQuery>,
) -> impl IntoResponse {
let PaginationQuery { page, per_page } = qs;
let PaginationQuery {
show,
page,
per_page,
} = qs;
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 mut projects = storage
.repositories()?
.into_iter()
.filter(|id| match &id.doc.visibility {
Visibility::Private { .. } => addr.ip().is_loopback(),
Visibility::Public => true,
})
.collect::<Vec<_>>();
let pinned = &ctx.profile.config.web.pinned;

let mut projects = match show {
ProjectQuery::All => storage
.repositories()?
.into_iter()
.filter(|repo| match &repo.doc.visibility {
Visibility::Private { .. } => addr.ip().is_loopback(),
Visibility::Public => true,
})
.collect::<Vec<_>>(),
ProjectQuery::Pinned => storage.repositories_by_id(pinned.repositories.iter())?,
};
projects.sort_by_key(|p| p.rid);

let infos = projects
Expand Down Expand Up @@ -116,11 +125,16 @@ mod routes {
.layer(MockConnectInfo(SocketAddr::from(([127, 0, 0, 1], 8080))));
let response = get(
&app,
"/delegates/did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/projects",
"/delegates/did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/projects?show=all",
)
.await;

assert_eq!(response.status(), StatusCode::OK);
assert_eq!(
response.status(),
StatusCode::OK,
"failed response: {:?}",
response.json().await
);
assert_eq!(
response.json().await,
json!([
Expand Down Expand Up @@ -179,11 +193,16 @@ mod routes {
))));
let response = get(
&app,
"/delegates/did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/projects",
"/delegates/did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/projects?show=all",
)
.await;

assert_eq!(response.status(), StatusCode::OK);
assert_eq!(
response.status(),
StatusCode::OK,
"failed response: {:?}",
response.json().await
);
assert_eq!(
response.json().await,
json!([
Expand Down
1 change: 1 addition & 0 deletions radicle-httpd/src/api/v1/profile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ mod routes {
"preferredSeeds": [
"z6MkrLMMsiPWUcNPHcRajuMi9mDfYckSoJyPwwnknocNYPm7@seed.radicle.garden:8776"
],
"web": { "pinned": { "repositories": [] } },
"cli": {
"hints": true
},
Expand Down
33 changes: 21 additions & 12 deletions radicle-httpd/src/api/v1/projects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use radicle_surf::{diff, Glob, Oid, Repository};

use crate::api::error::Error;
use crate::api::project::Info;
use crate::api::{self, announce_refs, CobsQuery, Context, PaginationQuery};
use crate::api::{self, announce_refs, CobsQuery, Context, PaginationQuery, ProjectQuery};
use crate::axum_extra::{Path, Query};

const CACHE_1_HOUR: &str = "public, max-age=3600, must-revalidate";
Expand Down Expand Up @@ -79,19 +79,28 @@ async fn project_root_handler(
State(ctx): State<Context>,
Query(qs): Query<PaginationQuery>,
) -> impl IntoResponse {
let PaginationQuery { page, per_page } = qs;
let PaginationQuery {
show,
page,
per_page,
} = qs;
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 mut projects = storage
.repositories()?
.into_iter()
.filter(|id| match &id.doc.visibility {
Visibility::Private { .. } => addr.ip().is_loopback(),
Visibility::Public => true,
})
.collect::<Vec<_>>();
let pinned = &ctx.profile.config.web.pinned;

let mut projects = match show {
ProjectQuery::All => storage
.repositories()?
.into_iter()
.filter(|repo| match &repo.doc.visibility {
Visibility::Private { .. } => addr.ip().is_loopback(),
Visibility::Public => true,
})
.collect::<Vec<_>>(),
ProjectQuery::Pinned => storage.repositories_by_id(pinned.repositories.iter())?,
};
projects.sort_by_key(|p| p.rid);

let infos = projects
Expand Down Expand Up @@ -976,7 +985,7 @@ mod routes {
let seed = seed(tmp.path());
let app = super::router(seed.clone())
.layer(MockConnectInfo(SocketAddr::from(([127, 0, 0, 1], 8080))));
let response = get(&app, "/projects").await;
let response = get(&app, "/projects?show=all").await;

assert_eq!(response.status(), StatusCode::OK);
assert_eq!(
Expand Down Expand Up @@ -1035,7 +1044,7 @@ mod routes {
[192, 168, 13, 37],
8080,
))));
let response = get(&app, "/projects").await;
let response = get(&app, "/projects?show=all").await;

assert_eq!(response.status(), StatusCode::OK);
assert_eq!(
Expand Down
3 changes: 2 additions & 1 deletion radicle-node/src/test/environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ use radicle::cob::issue;
use radicle::crypto::ssh::{keystore::MemorySigner, Keystore};
use radicle::crypto::test::signer::MockSigner;
use radicle::crypto::{KeyPair, Seed, Signer};
use radicle::git;
use radicle::git::refname;
use radicle::identity::{RepoId, Visibility};
use radicle::node::config::ConnectAddress;
Expand All @@ -30,6 +29,7 @@ use radicle::test::fixtures;
use radicle::Storage;
use radicle::{cli, node};
use radicle::{cob, explorer};
use radicle::{git, web};

use crate::node::NodeId;
use crate::service::Event;
Expand Down Expand Up @@ -92,6 +92,7 @@ impl Environment {
cli: cli::Config { hints: false },
public_explorer: explorer::Explorer::default(),
preferred_seeds: PreferredSeeds::from(vec![]),
web: web::Config::default(),
}
}

Expand Down
2 changes: 1 addition & 1 deletion radicle/src/cob/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ pub mod test {
let obj = history.traverse(initial, &children, |mut acc, _, entry| {
match Op::try_from(entry) {
Ok(op) => {
if let Err(err) = acc.op(op, [].into_iter(), repo) {
if let Err(err) = acc.op(op, [], repo) {
log::warn!("Error applying op to `{}` state: {err}", T::type_name());
return ControlFlow::Break(acc);
}
Expand Down
1 change: 1 addition & 0 deletions radicle/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub mod storage;
#[cfg(any(test, feature = "test"))]
pub mod test;
pub mod version;
pub mod web;

pub use cob::{issue, patch};
pub use node::Node;
Expand Down
6 changes: 5 additions & 1 deletion radicle/src/profile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use crate::prelude::Did;
use crate::prelude::NodeId;
use crate::storage::git::transport;
use crate::storage::git::Storage;
use crate::{cli, git, node};
use crate::{cli, git, node, web};

/// Environment variables used by radicle.
pub mod env {
Expand Down Expand Up @@ -123,6 +123,9 @@ pub struct Config {
/// and in other situations when a seed needs to be chosen.
#[serde(default)]
pub preferred_seeds: PreferredSeeds,
/// Web configuration.
#[serde(default)]
pub web: web::Config,
/// CLI configuration.
#[serde(default)]
pub cli: cli::Config,
Expand All @@ -136,6 +139,7 @@ impl Config {
Self {
public_explorer: Explorer::default(),
preferred_seeds: PreferredSeeds::default(),
web: web::Config::default(),
cli: cli::Config::default(),
node: node::Config::new(alias),
}
Expand Down
18 changes: 18 additions & 0 deletions radicle/src/storage/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,24 @@ impl Storage {
Ok(repos)
}

pub fn repositories_by_id<'a>(
&self,
mut rids: impl Iterator<Item = &'a RepoId>,
) -> Result<Vec<RepositoryInfo<Verified>>, RepositoryError> {
rids.try_fold(Vec::new(), |mut infos, rid| {
let repo = self.repository(*rid)?;
let (_, head) = repo.head()?;
let info = RepositoryInfo {
rid: *rid,
head,
doc: repo.identity_doc()?.into(),
refs: refs::SignedRefsAt::load(self.info.key, &repo)?,
};
infos.push(info);
Ok(infos)
})
}

pub fn inspect(&self) -> Result<(), Error> {
for r in self.repositories()? {
let rid = r.rid;
Expand Down
22 changes: 22 additions & 0 deletions radicle/src/web.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use std::collections::HashSet;

use serde::{Deserialize, Serialize};

use crate::prelude::RepoId;

/// Web configuration.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Config {
/// Pinned content.
pub pinned: Pinned,
}

/// Pinned content. This can be used to pin certain content when
/// listing, e.g. pin repositories on a web client.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Pinned {
/// Pinned repositories.
pub repositories: HashSet<RepoId>,
}

0 comments on commit 49584f4

Please sign in to comment.