httpd: Add `alias` field to certain routes
* `/patches`, `/issues`, `/remotes` Signed-off-by: xphoniex <dj.2dixx@gmail.com>
This commit is contained in:
parent
d8711a8d43
commit
ae5982cd6b
|
|
@ -69,6 +69,10 @@ pub enum Error {
|
||||||
/// Identity doc error.
|
/// Identity doc error.
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
IdentityDoc(#[from] radicle::identity::doc::DocError),
|
IdentityDoc(#[from] radicle::identity::doc::DocError),
|
||||||
|
|
||||||
|
/// Tracking store error.
|
||||||
|
#[error(transparent)]
|
||||||
|
TrackingStore(#[from] radicle::node::tracking::store::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoResponse for Error {
|
impl IntoResponse for Error {
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ use radicle::cob::thread;
|
||||||
use radicle::cob::thread::{CommentId, Thread};
|
use radicle::cob::thread::{CommentId, Thread};
|
||||||
use radicle::cob::{ActorId, Author, Reaction, Timestamp};
|
use radicle::cob::{ActorId, Author, Reaction, Timestamp};
|
||||||
use radicle::git::RefString;
|
use radicle::git::RefString;
|
||||||
|
use radicle::node::tracking::store as TrackingStore;
|
||||||
use radicle::storage::{git, refs, ReadRepository};
|
use radicle::storage::{git, refs, ReadRepository};
|
||||||
use radicle_surf::blob::Blob;
|
use radicle_surf::blob::Blob;
|
||||||
use radicle_surf::tree::Tree;
|
use radicle_surf::tree::Tree;
|
||||||
|
|
@ -92,26 +93,31 @@ pub(crate) fn tree(tree: &Tree, path: &str, stats: &Stats) -> Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns JSON for an `issue`.
|
/// Returns JSON for an `issue`.
|
||||||
pub(crate) fn issue(id: IssueId, issue: Issue) -> Value {
|
pub(crate) fn issue(id: IssueId, issue: Issue, aliases: &TrackingStore::Config) -> Value {
|
||||||
json!({
|
json!({
|
||||||
"id": id.to_string(),
|
"id": id.to_string(),
|
||||||
"author": issue.author(),
|
"author": author(&issue.author(), aliases.alias(issue.author().id())),
|
||||||
"title": issue.title(),
|
"title": issue.title(),
|
||||||
"state": issue.state(),
|
"state": issue.state(),
|
||||||
"assignees": issue.assigned().collect::<Vec<_>>(),
|
"assignees": issue.assigned().collect::<Vec<_>>(),
|
||||||
"discussion": issue
|
"discussion": issue
|
||||||
.comments()
|
.comments()
|
||||||
.map(|(id, comment)| Comment::new(id, comment, issue.thread()))
|
.map(|(id, comment)| Comment::new(id, comment, issue.thread(), aliases))
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
"tags": issue.tags().collect::<Vec<_>>(),
|
"tags": issue.tags().collect::<Vec<_>>(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns JSON for a `patch`.
|
/// Returns JSON for a `patch`.
|
||||||
pub(crate) fn patch(id: PatchId, patch: Patch, repo: &git::Repository) -> Value {
|
pub(crate) fn patch(
|
||||||
|
id: PatchId,
|
||||||
|
patch: Patch,
|
||||||
|
repo: &git::Repository,
|
||||||
|
aliases: &TrackingStore::Config,
|
||||||
|
) -> Value {
|
||||||
json!({
|
json!({
|
||||||
"id": id.to_string(),
|
"id": id.to_string(),
|
||||||
"author": patch.author(),
|
"author": author(patch.author(), aliases.alias(patch.author().id())),
|
||||||
"title": patch.title(),
|
"title": patch.title(),
|
||||||
"description": patch.description(),
|
"description": patch.description(),
|
||||||
"state": patch.state(),
|
"state": patch.state(),
|
||||||
|
|
@ -126,7 +132,7 @@ pub(crate) fn patch(id: PatchId, patch: Patch, repo: &git::Repository) -> Value
|
||||||
"refs": get_refs(repo, patch.author().id(), &rev.head()).unwrap_or(vec![]),
|
"refs": get_refs(repo, patch.author().id(), &rev.head()).unwrap_or(vec![]),
|
||||||
"merges": rev.merges().collect::<Vec<_>>(),
|
"merges": rev.merges().collect::<Vec<_>>(),
|
||||||
"discussions": rev.discussion().comments()
|
"discussions": rev.discussion().comments()
|
||||||
.map(|(id, comment)| Comment::new(id, comment, rev.discussion()))
|
.map(|(id, comment)| Comment::new(id, comment, rev.discussion(), aliases))
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
"timestamp": rev.timestamp(),
|
"timestamp": rev.timestamp(),
|
||||||
"reviews": rev.reviews().collect::<Vec<_>>(),
|
"reviews": rev.reviews().collect::<Vec<_>>(),
|
||||||
|
|
@ -135,6 +141,17 @@ pub(crate) fn patch(id: PatchId, patch: Patch, repo: &git::Repository) -> Value
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns JSON for an `author` and fills in `alias` when present.
|
||||||
|
fn author(author: &Author, alias: Option<String>) -> Value {
|
||||||
|
match alias {
|
||||||
|
Some(alias) => json!({
|
||||||
|
"id": author.id,
|
||||||
|
"alias": alias,
|
||||||
|
}),
|
||||||
|
None => json!(author),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the name part of a path string.
|
/// Returns the name part of a path string.
|
||||||
fn name_in_path(path: &str) -> &str {
|
fn name_in_path(path: &str) -> &str {
|
||||||
match path.rsplit('/').next() {
|
match path.rsplit('/').next() {
|
||||||
|
|
@ -168,7 +185,7 @@ fn get_refs(
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct Comment<'a> {
|
struct Comment<'a> {
|
||||||
id: CommentId,
|
id: CommentId,
|
||||||
author: Author,
|
author: Value,
|
||||||
body: &'a str,
|
body: &'a str,
|
||||||
reactions: Vec<(&'a ActorId, &'a Reaction)>,
|
reactions: Vec<(&'a ActorId, &'a Reaction)>,
|
||||||
timestamp: Timestamp,
|
timestamp: Timestamp,
|
||||||
|
|
@ -176,10 +193,16 @@ struct Comment<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Comment<'a> {
|
impl<'a> Comment<'a> {
|
||||||
fn new(id: &'a CommentId, comment: &'a thread::Comment, thread: &'a Thread) -> Self {
|
fn new(
|
||||||
|
id: &'a CommentId,
|
||||||
|
comment: &'a thread::Comment,
|
||||||
|
thread: &'a Thread,
|
||||||
|
aliases: &TrackingStore::Config,
|
||||||
|
) -> Self {
|
||||||
|
let comment_author = Author::new(comment.author());
|
||||||
Self {
|
Self {
|
||||||
id: *id,
|
id: *id,
|
||||||
author: Author::new(comment.author()),
|
author: author(&comment_author, aliases.alias(comment_author.id())),
|
||||||
body: comment.body(),
|
body: comment.body(),
|
||||||
reactions: thread.reactions(id).collect::<Vec<_>>(),
|
reactions: thread.reactions(id).collect::<Vec<_>>(),
|
||||||
timestamp: comment.timestamp(),
|
timestamp: comment.timestamp(),
|
||||||
|
|
|
||||||
|
|
@ -319,6 +319,7 @@ async fn remotes_handler(State(ctx): State<Context>, Path(project): Path<Id>) ->
|
||||||
let storage = &ctx.profile.storage;
|
let storage = &ctx.profile.storage;
|
||||||
let repo = storage.repository(project)?;
|
let repo = storage.repository(project)?;
|
||||||
let delegates = repo.delegates()?;
|
let delegates = repo.delegates()?;
|
||||||
|
let tracking_store = &ctx.profile.tracking()?;
|
||||||
let remotes = repo
|
let remotes = repo
|
||||||
.remotes()?
|
.remotes()?
|
||||||
.filter_map(|r| r.map(|r| r.1).ok())
|
.filter_map(|r| r.map(|r| r.1).ok())
|
||||||
|
|
@ -333,11 +334,19 @@ async fn remotes_handler(State(ctx): State<Context>, Path(project): Path<Id>) ->
|
||||||
})
|
})
|
||||||
.collect::<BTreeMap<String, &Oid>>();
|
.collect::<BTreeMap<String, &Oid>>();
|
||||||
|
|
||||||
json!({
|
match tracking_store.alias(&remote.id) {
|
||||||
|
Some(alias) => json!({
|
||||||
|
"id": remote.id,
|
||||||
|
"alias": alias,
|
||||||
|
"heads": refs,
|
||||||
|
"delegate": delegates.contains(&remote.id.into()),
|
||||||
|
}),
|
||||||
|
None => json!({
|
||||||
"id": remote.id,
|
"id": remote.id,
|
||||||
"heads": refs,
|
"heads": refs,
|
||||||
"delegate": delegates.contains(&remote.id.into()),
|
"delegate": delegates.contains(&remote.id.into()),
|
||||||
})
|
}),
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
|
@ -440,9 +449,10 @@ async fn issues_handler(
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
issues.sort_by(|(_, a, _), (_, b, _)| b.timestamp().cmp(&a.timestamp()));
|
issues.sort_by(|(_, a, _), (_, b, _)| b.timestamp().cmp(&a.timestamp()));
|
||||||
|
let tracking_store = &ctx.profile.tracking()?;
|
||||||
let issues = issues
|
let issues = issues
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(id, issue, _)| api::json::issue(id, issue))
|
.map(|(id, issue, _)| api::json::issue(id, issue, tracking_store))
|
||||||
.skip(page * per_page)
|
.skip(page * per_page)
|
||||||
.take(per_page)
|
.take(per_page)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
@ -559,8 +569,13 @@ async fn issue_handler(
|
||||||
let issue = issue::Issues::open(&repo)?
|
let issue = issue::Issues::open(&repo)?
|
||||||
.get(&issue_id.into())?
|
.get(&issue_id.into())?
|
||||||
.ok_or(Error::NotFound)?;
|
.ok_or(Error::NotFound)?;
|
||||||
|
let tracking_store = &ctx.profile.tracking()?;
|
||||||
|
|
||||||
Ok::<_, Error>(Json(api::json::issue(issue_id.into(), issue)))
|
Ok::<_, Error>(Json(api::json::issue(
|
||||||
|
issue_id.into(),
|
||||||
|
issue,
|
||||||
|
tracking_store,
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
|
|
@ -722,9 +737,10 @@ async fn patches_handler(
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
patches.sort_by(|(_, a, _), (_, b, _)| b.timestamp().cmp(&a.timestamp()));
|
patches.sort_by(|(_, a, _), (_, b, _)| b.timestamp().cmp(&a.timestamp()));
|
||||||
|
let tracking_store = &ctx.profile.tracking()?;
|
||||||
let patches = patches
|
let patches = patches
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(id, patch, _)| api::json::patch(id, patch, &repo))
|
.map(|(id, patch, _)| api::json::patch(id, patch, &repo, tracking_store))
|
||||||
.skip(page * per_page)
|
.skip(page * per_page)
|
||||||
.take(per_page)
|
.take(per_page)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
@ -743,8 +759,14 @@ async fn patch_handler(
|
||||||
let patch = patch::Patches::open(&repo)?
|
let patch = patch::Patches::open(&repo)?
|
||||||
.get(&patch_id.into())?
|
.get(&patch_id.into())?
|
||||||
.ok_or(Error::NotFound)?;
|
.ok_or(Error::NotFound)?;
|
||||||
|
let tracking_store = &ctx.profile.tracking()?;
|
||||||
|
|
||||||
Ok::<_, Error>(Json(api::json::patch(patch_id.into(), patch, &repo)))
|
Ok::<_, Error>(Json(api::json::patch(
|
||||||
|
patch_id.into(),
|
||||||
|
patch,
|
||||||
|
&repo,
|
||||||
|
tracking_store,
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ use radicle::crypto::ssh::keystore::MemorySigner;
|
||||||
use radicle::crypto::ssh::Keystore;
|
use radicle::crypto::ssh::Keystore;
|
||||||
use radicle::crypto::{KeyPair, Seed, Signer};
|
use radicle::crypto::{KeyPair, Seed, Signer};
|
||||||
use radicle::git::{raw as git2, RefString};
|
use radicle::git::{raw as git2, RefString};
|
||||||
|
use radicle::node::tracking::store as TrackingStore;
|
||||||
use radicle::profile::Home;
|
use radicle::profile::Home;
|
||||||
use radicle::storage::ReadStorage;
|
use radicle::storage::ReadStorage;
|
||||||
use radicle::Storage;
|
use radicle::Storage;
|
||||||
|
|
@ -83,6 +84,9 @@ pub fn contributor(dir: &Path) -> Context {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn seed_with_signer<G: Signer>(dir: &Path, profile: radicle::Profile, signer: &G) -> Context {
|
fn seed_with_signer<G: Signer>(dir: &Path, profile: radicle::Profile, signer: &G) -> Context {
|
||||||
|
let tracking_db = dir.join("radicle").join("node").join("tracking.db");
|
||||||
|
TrackingStore::Config::open(tracking_db).unwrap();
|
||||||
|
|
||||||
let workdir = dir.join("hello-world");
|
let workdir = dir.join("hello-world");
|
||||||
|
|
||||||
env::set_var("RAD_COMMIT_TIME", TIMESTAMP.to_string());
|
env::set_var("RAD_COMMIT_TIME", TIMESTAMP.to_string());
|
||||||
|
|
|
||||||
|
|
@ -254,6 +254,14 @@ impl Config {
|
||||||
}
|
}
|
||||||
Ok(Box::new(entries.into_iter()))
|
Ok(Box::new(entries.into_iter()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieve `alias` of given node.
|
||||||
|
/// Calls `Self::node_policy` under the hood.
|
||||||
|
pub fn alias(&self, nid: &NodeId) -> Option<String> {
|
||||||
|
self.node_policy(nid)
|
||||||
|
.map(|node| node.and_then(|n| n.alias))
|
||||||
|
.unwrap_or(None)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue