Radish alpha
r
Radicle web interface
Radicle
Git (anonymous pull)
Log in to clone via SSH
httpd: Add `alias` field to certain routes
xphoniex committed 3 years ago
commit 8880c49c1d8ec2caa5f98e4f5a9a5bfa40ff18f9
parent be9eb3f8e249b220995eb6f00853a60183ecc096
4 files changed +72 -19
modified radicle-httpd/src/api/error.rs
@@ -69,6 +69,10 @@ pub enum Error {
    /// Identity doc error.
    #[error(transparent)]
    IdentityDoc(#[from] radicle::identity::doc::DocError),
+

+
    /// Tracking store error.
+
    #[error(transparent)]
+
    TrackingStore(#[from] radicle::node::tracking::store::Error),
}

impl IntoResponse for Error {
modified radicle-httpd/src/api/json.rs
@@ -12,6 +12,7 @@ use radicle::cob::thread;
use radicle::cob::thread::{CommentId, Thread};
use radicle::cob::{ActorId, Author, Reaction, Timestamp};
use radicle::git::RefString;
+
use radicle::node::tracking::store as TrackingStore;
use radicle::storage::{git, refs, ReadRepository};
use radicle_surf::blob::Blob;
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`.
-
pub(crate) fn issue(id: IssueId, issue: Issue) -> Value {
+
pub(crate) fn issue(id: IssueId, issue: Issue, aliases: &TrackingStore::Config) -> Value {
    json!({
        "id": id.to_string(),
-
        "author": issue.author(),
+
        "author": author(&issue.author(), aliases.alias(issue.author().id())),
        "title": issue.title(),
        "state": issue.state(),
        "assignees": issue.assigned().collect::<Vec<_>>(),
        "discussion": issue
          .comments()
-
          .map(|(id, comment)| Comment::new(id, comment, issue.thread()))
+
          .map(|(id, comment)| Comment::new(id, comment, issue.thread(), aliases))
          .collect::<Vec<_>>(),
        "tags": issue.tags().collect::<Vec<_>>(),
    })
}

/// 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!({
        "id": id.to_string(),
-
        "author": patch.author(),
+
        "author": author(patch.author(), aliases.alias(patch.author().id())),
        "title": patch.title(),
        "description": patch.description(),
        "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![]),
                "merges": rev.merges().collect::<Vec<_>>(),
                "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<_>>(),
                "timestamp": rev.timestamp(),
                "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.
fn name_in_path(path: &str) -> &str {
    match path.rsplit('/').next() {
@@ -168,7 +185,7 @@ fn get_refs(
#[serde(rename_all = "camelCase")]
struct Comment<'a> {
    id: CommentId,
-
    author: Author,
+
    author: Value,
    body: &'a str,
    reactions: Vec<(&'a ActorId, &'a Reaction)>,
    timestamp: Timestamp,
@@ -176,10 +193,16 @@ struct 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 {
            id: *id,
-
            author: Author::new(comment.author()),
+
            author: author(&comment_author, aliases.alias(comment_author.id())),
            body: comment.body(),
            reactions: thread.reactions(id).collect::<Vec<_>>(),
            timestamp: comment.timestamp(),
modified radicle-httpd/src/api/v1/projects.rs
@@ -319,6 +319,7 @@ async fn remotes_handler(State(ctx): State<Context>, Path(project): Path<Id>) ->
    let storage = &ctx.profile.storage;
    let repo = storage.repository(project)?;
    let delegates = repo.delegates()?;
+
    let tracking_store = &ctx.profile.tracking()?;
    let remotes = repo
        .remotes()?
        .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>>();

-
            json!({
-
                "id": remote.id,
-
                "heads": refs,
-
                "delegate": delegates.contains(&remote.id.into()),
-
            })
+
            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,
+
                    "heads": refs,
+
                    "delegate": delegates.contains(&remote.id.into()),
+
                }),
+
            }
        })
        .collect::<Vec<_>>();

@@ -440,9 +449,10 @@ async fn issues_handler(
        .collect::<Vec<_>>();

    issues.sort_by(|(_, a, _), (_, b, _)| b.timestamp().cmp(&a.timestamp()));
+
    let tracking_store = &ctx.profile.tracking()?;
    let issues = issues
        .into_iter()
-
        .map(|(id, issue, _)| api::json::issue(id, issue))
+
        .map(|(id, issue, _)| api::json::issue(id, issue, tracking_store))
        .skip(page * per_page)
        .take(per_page)
        .collect::<Vec<_>>();
@@ -559,8 +569,13 @@ async fn issue_handler(
    let issue = issue::Issues::open(&repo)?
        .get(&issue_id.into())?
        .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)]
@@ -722,9 +737,10 @@ async fn patches_handler(
        })
        .collect::<Vec<_>>();
    patches.sort_by(|(_, a, _), (_, b, _)| b.timestamp().cmp(&a.timestamp()));
+
    let tracking_store = &ctx.profile.tracking()?;
    let patches = patches
        .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)
        .take(per_page)
        .collect::<Vec<_>>();
@@ -743,8 +759,14 @@ async fn patch_handler(
    let patch = patch::Patches::open(&repo)?
        .get(&patch_id.into())?
        .ok_or(Error::NotFound)?;
-

-
    Ok::<_, Error>(Json(api::json::patch(patch_id.into(), patch, &repo)))
+
    let tracking_store = &ctx.profile.tracking()?;
+

+
    Ok::<_, Error>(Json(api::json::patch(
+
        patch_id.into(),
+
        patch,
+
        &repo,
+
        tracking_store,
+
    )))
}

#[cfg(test)]
modified radicle-httpd/src/test.rs
@@ -16,6 +16,7 @@ use radicle::crypto::ssh::keystore::MemorySigner;
use radicle::crypto::ssh::Keystore;
use radicle::crypto::{KeyPair, Seed, Signer};
use radicle::git::{raw as git2, RefString};
+
use radicle::node::tracking::store as TrackingStore;
use radicle::profile::Home;
use radicle::storage::ReadStorage;
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 {
+
    let tracking_db = dir.join("radicle").join("node").join("tracking.db");
+
    TrackingStore::Config::open(tracking_db).unwrap();
+

    let workdir = dir.join("hello-world");

    env::set_var("RAD_COMMIT_TIME", TIMESTAMP.to_string());