From dea7082a6f4b14a232a924ba577e85ca2ba8f6e4 Mon Sep 17 00:00:00 2001 From: xphoniex Date: Tue, 22 Nov 2022 13:16:34 +0000 Subject: [PATCH] Migrate `/projects`: `root`, `project`, `commit`, `activity` handlers Signed-off-by: xphoniex --- Cargo.lock | 195 +++++++++++++++++++++++--- Cargo.toml | 4 + radicle-httpd/Cargo.toml | 8 +- radicle-httpd/src/api.rs | 39 ++++++ radicle-httpd/src/api/error.rs | 14 +- radicle-httpd/src/api/v1.rs | 4 +- radicle-httpd/src/api/v1/delegates.rs | 25 +--- radicle-httpd/src/api/v1/projects.rs | 123 ++++++++++++++++ radicle/src/cob/store.rs | 7 + 9 files changed, 373 insertions(+), 46 deletions(-) create mode 100644 radicle-httpd/src/api/v1/projects.rs diff --git a/Cargo.lock b/Cargo.lock index 6e09c229..bd2cfa2a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -66,6 +66,12 @@ version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + [[package]] name = "arrayvec" version = "0.7.2" @@ -236,16 +242,28 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitvec" +version = "0.19.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55f93d0ef3363c364d5976646a38f04cf67cfe1d4c8d160cdea02cab2c116b33" +dependencies = [ + "funty 1.1.0", + "radium 0.5.3", + "tap", + "wyz 0.2.0", +] + [[package]] name = "bitvec" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ - "funty", - "radium", + "funty 2.0.0", + "radium 0.7.0", "tap", - "wyz", + "wyz 0.5.1", ] [[package]] @@ -854,7 +872,7 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ade3e9c97727343984e1ceada4fdab11142d2ee3472d2c67027d56b1251d4f15" dependencies = [ - "arrayvec", + "arrayvec 0.7.2", "bytes", "chrono", "elliptic-curve", @@ -893,6 +911,18 @@ dependencies = [ "subtle", ] +[[package]] +name = "filetime" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9663d381d07ae25dc88dbdf27df458faa83a9b25336bcac83d5e452b5fc9d3" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "windows-sys", +] + [[package]] name = "fixed-hash" version = "0.8.0" @@ -936,6 +966,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "funty" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" + [[package]] name = "funty" version = "2.0.0" @@ -1039,8 +1075,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d819e0a29c3201eb3373f9371be2bf821437979791ff07fe23a40ed8adadcfc3" dependencies = [ - "git-ref-format-core", - "git-ref-format-macro", + "git-ref-format-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "git-ref-format-macro 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "git-ref-format" +version = "0.1.0" +source = "git+https://github.com/radicle-dev/radicle-git?rev=d3115a22158c8395705babefdc89049f7510d32d#d3115a22158c8395705babefdc89049f7510d32d" +dependencies = [ + "git-ref-format-core 0.1.0 (git+https://github.com/radicle-dev/radicle-git?rev=d3115a22158c8395705babefdc89049f7510d32d)", + "git-ref-format-macro 0.1.0 (git+https://github.com/radicle-dev/radicle-git?rev=d3115a22158c8395705babefdc89049f7510d32d)", ] [[package]] @@ -1053,13 +1098,33 @@ dependencies = [ "thiserror", ] +[[package]] +name = "git-ref-format-core" +version = "0.1.0" +source = "git+https://github.com/radicle-dev/radicle-git?rev=d3115a22158c8395705babefdc89049f7510d32d#d3115a22158c8395705babefdc89049f7510d32d" +dependencies = [ + "serde", + "thiserror", +] + [[package]] name = "git-ref-format-macro" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66434445c9d45a85a9186a76770b1f80a44fdf8ed34fb6d4e3d12c5b9fc942ef" dependencies = [ - "git-ref-format-core", + "git-ref-format-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-error", + "quote", + "syn", +] + +[[package]] +name = "git-ref-format-macro" +version = "0.1.0" +source = "git+https://github.com/radicle-dev/radicle-git?rev=d3115a22158c8395705babefdc89049f7510d32d#d3115a22158c8395705babefdc89049f7510d32d" +dependencies = [ + "git-ref-format-core 0.1.0 (git+https://github.com/radicle-dev/radicle-git?rev=d3115a22158c8395705babefdc89049f7510d32d)", "proc-macro-error", "quote", "syn", @@ -1071,7 +1136,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0934f57135449b88bea0e28efd80aab0c1b53692f8207c7e232086db824c7a8" dependencies = [ - "nom", + "nom 7.1.1", "thiserror", ] @@ -1499,6 +1564,19 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" +[[package]] +name = "lexical-core" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" +dependencies = [ + "arrayvec 0.5.2", + "bitflags", + "cfg-if", + "ryu", + "static_assertions", +] + [[package]] name = "lexopt" version = "0.2.1" @@ -1669,6 +1747,19 @@ dependencies = [ "socket2", ] +[[package]] +name = "nom" +version = "6.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2" +dependencies = [ + "bitvec 0.19.6", + "funty 1.1.0", + "lexical-core", + "memchr", + "version_check", +] + [[package]] name = "nom" version = "7.1.1" @@ -1679,6 +1770,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nonempty" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ff7ac1e5ea23db6d61ad103e91864675049644bf47c35912336352fa4e9c109" + [[package]] name = "nonempty" version = "0.8.0" @@ -1797,7 +1894,7 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "786393f80485445794f6043fd3138854dd109cc6c4bd1a6383db304c9ce9b9ce" dependencies = [ - "arrayvec", + "arrayvec 0.7.2", "auto_impl", "bytes", "ethereum-types", @@ -1869,8 +1966,8 @@ version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "366e44391a8af4cfd6002ef6ba072bae071a96aafca98d7d448a34c5dca38b6a" dependencies = [ - "arrayvec", - "bitvec", + "arrayvec 0.7.2", + "bitvec 1.0.1", "byte-slice-cast", "impl-trait-for-tuples", "parity-scale-codec-derive", @@ -2110,11 +2207,11 @@ dependencies = [ "cyphernet", "ed25519-compact", "fastrand", - "git-ref-format", + "git-ref-format 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "git2", "log", "multibase", - "nonempty", + "nonempty 0.8.0", "olpc-cjson", "once_cell", "pretty_assertions", @@ -2168,7 +2265,7 @@ dependencies = [ "ed25519-compact", "fastrand", "git-commit", - "git-ref-format", + "git-ref-format 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "git-trailers", "git2", "log", @@ -2206,7 +2303,7 @@ dependencies = [ "cyphernet", "ed25519-compact", "fastrand", - "git-ref-format", + "git-ref-format 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "multibase", "quickcheck", "quickcheck_macros", @@ -2223,10 +2320,9 @@ dependencies = [ [[package]] name = "radicle-git-ext" version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25ed92fcf331d19b3110bbed8d3fe2bd99dc75f0059fd135727d24ef829de507" +source = "git+https://github.com/radicle-dev/radicle-git?rev=d3115a22158c8395705babefdc89049f7510d32d#d3115a22158c8395705babefdc89049f7510d32d" dependencies = [ - "git-ref-format", + "git-ref-format 0.1.0 (git+https://github.com/radicle-dev/radicle-git?rev=d3115a22158c8395705babefdc89049f7510d32d)", "git2", "percent-encoding", "radicle-std-ext", @@ -2241,12 +2337,14 @@ dependencies = [ "anyhow", "axum", "axum-server", + "chrono", "ethers-core", "fastrand", "flate2", "hyper", "lexopt", "radicle", + "radicle-surf", "serde", "serde_json", "siwe", @@ -2270,12 +2368,12 @@ dependencies = [ "colored", "crossbeam-channel", "fastrand", - "git-ref-format", + "git-ref-format 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "lexopt", "log", "nakamoto-net", "nakamoto-net-poll", - "nonempty", + "nonempty 0.8.0", "quickcheck", "quickcheck_macros", "radicle", @@ -2311,18 +2409,43 @@ dependencies = [ [[package]] name = "radicle-std-ext" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db20136bbc9ae63f3fec8e5a6c369f4902fac2244501b5dfc6d668e43475aaa4" +source = "git+https://github.com/radicle-dev/radicle-git?rev=d3115a22158c8395705babefdc89049f7510d32d#d3115a22158c8395705babefdc89049f7510d32d" + +[[package]] +name = "radicle-surf" +version = "0.8.0" +source = "git+https://github.com/radicle-dev/radicle-git?rev=d3115a22158c8395705babefdc89049f7510d32d#d3115a22158c8395705babefdc89049f7510d32d" +dependencies = [ + "anyhow", + "base64", + "either", + "flate2", + "git-ref-format 0.1.0 (git+https://github.com/radicle-dev/radicle-git?rev=d3115a22158c8395705babefdc89049f7510d32d)", + "git2", + "nom 6.1.2", + "nonempty 0.5.0", + "radicle-git-ext", + "regex", + "serde", + "tar", + "thiserror", +] [[package]] name = "radicle-tools" version = "0.2.0" dependencies = [ "anyhow", - "git-ref-format", + "git-ref-format 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "radicle", ] +[[package]] +name = "radium" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8" + [[package]] name = "radium" version = "0.7.0" @@ -2985,6 +3108,17 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "tar" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" +dependencies = [ + "filetime", + "libc", + "xattr", +] + [[package]] name = "tempfile" version = "3.3.0" @@ -3614,6 +3748,12 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +[[package]] +name = "wyz" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" + [[package]] name = "wyz" version = "0.5.1" @@ -3623,6 +3763,15 @@ dependencies = [ "tap", ] +[[package]] +name = "xattr" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" +dependencies = [ + "libc", +] + [[package]] name = "yaml-rust" version = "0.4.5" diff --git a/Cargo.toml b/Cargo.toml index 844202eb..9c5e88e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,3 +35,7 @@ version = "0.3.0" git = "https://github.com/internet2-wg/rust-cyphernet" rev = "dee03a95abe4c964e5d9f8532c7dc76998dfeea7" version = "0.1.0" + +[patch.crates-io.radicle-git-ext] +git = "https://github.com/radicle-dev/radicle-git" +rev = "d3115a22158c8395705babefdc89049f7510d32d" diff --git a/radicle-httpd/Cargo.toml b/radicle-httpd/Cargo.toml index 69b6b7f6..bfd91259 100644 --- a/radicle-httpd/Cargo.toml +++ b/radicle-httpd/Cargo.toml @@ -18,6 +18,7 @@ logfmt = [ anyhow = { version = "1" } axum = { version = "0.5.16", default-features = false, features = ["json", "headers", "query"] } axum-server = { version = "0.4.2", default-features = false } +chrono = { version = "0.4.22" } ethers-core = { version = "1.0" } fastrand = { version = "1.7.0" } flate2 = { version = "1" } @@ -29,7 +30,7 @@ siwe = { version = "0.5" } thiserror = { version = "1" } time = { version = "0.3.17" } tokio = { version = "1.21", default-features = false, features = ["macros", "rt-multi-thread"] } -tower-http = { version = "0.3.4", default-features = false, features = ["trace", "cors"] } +tower-http = { version = "0.3.4", default-features = false, features = ["trace", "cors", "set-header"] } tracing = { version = "0.1.37", default-features = false, features = ["std", "log"] } tracing-logfmt = { version = "0.2", optional = true } tracing-subscriber = { version = "0.3", default-features = false, features = ["std", "ansi", "fmt"] } @@ -37,3 +38,8 @@ tracing-subscriber = { version = "0.3", default-features = false, features = ["s [dependencies.radicle] path = "../radicle" version = "0.2.0" + +[dependencies.radicle-surf] +git = "https://github.com/radicle-dev/radicle-git" +features = ["serialize"] +rev = "d3115a22158c8395705babefdc89049f7510d32d" diff --git a/radicle-httpd/src/api.rs b/radicle-httpd/src/api.rs index 215650f3..8650460e 100644 --- a/radicle-httpd/src/api.rs +++ b/radicle-httpd/src/api.rs @@ -16,6 +16,9 @@ use tower_http::cors::{self, CorsLayer}; use tower_http::trace::TraceLayer; use tracing::Span; +use radicle::cob::issue::Issues; +use radicle::identity::{Doc, Id}; +use radicle::storage::{ReadRepository, WriteStorage}; use radicle::Profile; mod auth; @@ -41,6 +44,22 @@ impl Context { sessions: Default::default(), } } + + pub fn project_info(&self, id: Id) -> Result { + let storage = &self.profile.storage; + let repo = storage.repository(id)?; + let (_, head) = repo.head()?; + let Doc { payload, .. } = repo.project_of(self.profile.id())?; + let issues = (Issues::open(self.profile.public_key, &repo)?).count()?; + + Ok(project::Info { + payload, + head, + issues, + patches: 0, + id, + }) + } } pub fn router(ctx: Context) -> Router { @@ -115,3 +134,23 @@ pub struct PaginationQuery { pub page: Option, pub per_page: Option, } + +mod project { + use radicle::git::Oid; + use radicle::identity::project::Payload; + use radicle::identity::Id; + use serde::Serialize; + + /// Project info. + #[derive(Serialize)] + #[serde(rename_all = "camelCase")] + pub struct Info { + /// Project metadata. + #[serde(flatten)] + pub payload: Payload, + pub head: Oid, + pub patches: usize, + pub issues: usize, + pub id: Id, + } +} diff --git a/radicle-httpd/src/api/error.rs b/radicle-httpd/src/api/error.rs index b8c5e1d1..7100b1a5 100644 --- a/radicle-httpd/src/api/error.rs +++ b/radicle-httpd/src/api/error.rs @@ -42,7 +42,19 @@ pub enum Error { /// Storage error. #[error(transparent)] - StorageError(#[from] radicle::storage::Error), + Storage(#[from] radicle::storage::Error), + + /// Cob store error. + #[error(transparent)] + CobStore(#[from] radicle::cob::store::Error), + + /// Git project error. + #[error(transparent)] + GitProject(#[from] radicle::storage::git::ProjectError), + + /// Surf error. + #[error(transparent)] + SurfCommit(#[from] radicle_surf::commit::Error), } impl Error { diff --git a/radicle-httpd/src/api/v1.rs b/radicle-httpd/src/api/v1.rs index b39bc609..543d4dc4 100644 --- a/radicle-httpd/src/api/v1.rs +++ b/radicle-httpd/src/api/v1.rs @@ -1,5 +1,6 @@ mod delegates; mod node; +mod projects; mod sessions; use axum::Router; @@ -10,7 +11,8 @@ pub fn router(ctx: Context) -> Router { let routes = Router::new() .merge(node::router(ctx.clone())) .merge(sessions::router(ctx.clone())) - .merge(delegates::router(ctx)); + .merge(delegates::router(ctx.clone())) + .merge(projects::router(ctx)); Router::new().nest("/v1", routes) } diff --git a/radicle-httpd/src/api/v1/delegates.rs b/radicle-httpd/src/api/v1/delegates.rs index 05abb9d5..c28385b0 100644 --- a/radicle-httpd/src/api/v1/delegates.rs +++ b/radicle-httpd/src/api/v1/delegates.rs @@ -1,31 +1,17 @@ use axum::response::IntoResponse; use axum::routing::get; use axum::{Extension, Json, Router}; -use serde::Serialize; -use radicle::cob::store::Store; -use radicle::git::Oid; -use radicle::identity::project::Payload; +use radicle::cob::issue::Issues; use radicle::identity::{Did, Doc}; use radicle::storage::{ReadRepository, WriteStorage}; use crate::api::axum_extra::{Path, Query}; use crate::api::error::Error; +use crate::api::project::Info; use crate::api::Context; use crate::api::PaginationQuery; -/// Project info. -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Info { - /// Project metadata. - #[serde(flatten)] - pub payload: Payload, - pub head: Oid, - pub patches: usize, - pub issues: usize, -} - pub fn router(ctx: Context) -> Router { Router::new() .route( @@ -58,15 +44,14 @@ async fn delegates_projects_handler( return None; } - let Ok(cobs) = Store::open(ctx.profile.public_key, &repo) else { return None }; - let Ok(issues) = cobs.issues().count() else { return None }; - let Ok(patches) = cobs.patches().count() else { return None }; + let Ok(issues) = Issues::open(ctx.profile.public_key, &repo) else { return None }; + let Ok(issues) = (*issues).count() else { return None }; Some(Info { payload, head, issues, - patches, + patches: 0, id, }) }) diff --git a/radicle-httpd/src/api/v1/projects.rs b/radicle-httpd/src/api/v1/projects.rs new file mode 100644 index 00000000..1ab12125 --- /dev/null +++ b/radicle-httpd/src/api/v1/projects.rs @@ -0,0 +1,123 @@ +use axum::handler::Handler; +use axum::http::{header, HeaderValue}; +use axum::response::IntoResponse; +use axum::routing::get; +use axum::{Extension, Json, Router}; +use hyper::StatusCode; +use serde_json::json; +use tower_http::set_header::SetResponseHeaderLayer; + +use radicle::cob::issue::Issues; +use radicle::identity::{Doc, Id}; +use radicle::storage::{Oid, ReadRepository, WriteRepository, WriteStorage}; +use radicle_surf::git::History; + +use crate::api::axum_extra::{Path, Query}; +use crate::api::error::Error; +use crate::api::project::Info; +use crate::api::{Context, PaginationQuery}; + +const CACHE_1_HOUR: &str = "public, max-age=3600, must-revalidate"; + +pub fn router(ctx: Context) -> Router { + Router::new() + .route("/projects", get(project_root_handler)) + .route("/projects/:project", get(project_handler)) + .route("/projects/:project/commits/:sha", get(commit_handler)) + .route( + "/projects/:project/activity", + get( + activity_handler.layer(SetResponseHeaderLayer::if_not_present( + header::CACHE_CONTROL, + HeaderValue::from_static(CACHE_1_HOUR), + )), + ), + ) + .layer(Extension(ctx)) +} + +/// List all projects. +/// `GET /projects` +async fn project_root_handler( + Extension(ctx): Extension, + Query(qs): Query, +) -> impl IntoResponse { + let PaginationQuery { page, per_page } = qs; + let page = page.unwrap_or(0); + let per_page = per_page.unwrap_or(10); + let storage = &ctx.profile.storage; + let projects = storage + .projects()? + .into_iter() + .filter_map(|id| { + let Ok(repo) = storage.repository(id) else { return None }; + let Ok((_, head)) = repo.head() else { return None }; + let Ok(Doc { payload, .. }) = repo.project_of(ctx.profile.id()) else { return None }; + let Ok(issues) = Issues::open(ctx.profile.public_key, &repo) else { return None }; + let Ok(issues) = (*issues).count() else { return None }; + + Some(Info { + payload, + head, + issues, + patches: 0, + id, + }) + }) + .skip(page * per_page) + .take(per_page) + .collect::>(); + + Ok::<_, Error>(Json(projects)) +} + +/// Get project metadata. +/// `GET /projects/:project` +async fn project_handler( + Extension(ctx): Extension, + Path(id): Path, +) -> impl IntoResponse { + let info = ctx.project_info(id)?; + + Ok::<_, Error>(Json(info)) +} + +/// Get project commit. +/// `GET /projects/:project/commits/:sha` +async fn commit_handler( + Extension(ctx): Extension, + Path((project, sha)): Path<(Id, Oid)>, +) -> impl IntoResponse { + let storage = &ctx.profile.storage; + let repo = storage.repository(project)?; + let commit = radicle_surf::commit(&repo.raw().into(), sha)?; + + Ok::<_, Error>(Json(json!(commit))) +} + +/// Get project activity for the past year. +/// `GET /projects/:project/activity` +async fn activity_handler( + Extension(ctx): Extension, + Path(project): Path, +) -> impl IntoResponse { + let current_date = chrono::Utc::now().timestamp(); + let one_year_ago = chrono::Duration::weeks(52); + let storage = &ctx.profile.storage; + let repo = storage.repository(project)?; + let (_, head) = repo.head()?; + let timestamps = History::new(repo.raw().into(), head) + .unwrap() + .filter_map(|a| { + if let Ok(a) = a { + let seconds = a.committer.time.seconds(); + if seconds > current_date - one_year_ago.num_seconds() { + return Some(seconds); + } + } + None + }) + .collect::>(); + + Ok::<_, Error>((StatusCode::OK, Json(json!({ "activity": timestamps })))) +} diff --git a/radicle/src/cob/store.rs b/radicle/src/cob/store.rs index 7ebbd87a..e4b1febf 100644 --- a/radicle/src/cob/store.rs +++ b/radicle/src/cob/store.rs @@ -147,6 +147,13 @@ impl<'a, T: FromHistory> Store<'a, T> { })) } + /// Return objects count. + pub fn count(&self) -> Result { + let raw = cob::list(self.raw, T::type_name())?; + + Ok(raw.len()) + } + pub fn remove(&self, _id: &ObjectId) -> Result<(), Error> { todo!(); }