Migrate `/projects`: `root`, `project`, `commit`, `activity` handlers

Signed-off-by: xphoniex <dj.2dixx@gmail.com>
This commit is contained in:
xphoniex 2022-11-22 13:16:34 +00:00
parent 77e0a176bd
commit dea7082a6f
No known key found for this signature in database
GPG Key ID: 9FACFECE1E3B3994
9 changed files with 373 additions and 46 deletions

195
Cargo.lock generated
View File

@ -66,6 +66,12 @@ version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
[[package]]
name = "arrayvec"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]] [[package]]
name = "arrayvec" name = "arrayvec"
version = "0.7.2" version = "0.7.2"
@ -236,16 +242,28 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 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]] [[package]]
name = "bitvec" name = "bitvec"
version = "1.0.1" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
dependencies = [ dependencies = [
"funty", "funty 2.0.0",
"radium", "radium 0.7.0",
"tap", "tap",
"wyz", "wyz 0.5.1",
] ]
[[package]] [[package]]
@ -854,7 +872,7 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ade3e9c97727343984e1ceada4fdab11142d2ee3472d2c67027d56b1251d4f15" checksum = "ade3e9c97727343984e1ceada4fdab11142d2ee3472d2c67027d56b1251d4f15"
dependencies = [ dependencies = [
"arrayvec", "arrayvec 0.7.2",
"bytes", "bytes",
"chrono", "chrono",
"elliptic-curve", "elliptic-curve",
@ -893,6 +911,18 @@ dependencies = [
"subtle", "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]] [[package]]
name = "fixed-hash" name = "fixed-hash"
version = "0.8.0" version = "0.8.0"
@ -936,6 +966,12 @@ dependencies = [
"percent-encoding", "percent-encoding",
] ]
[[package]]
name = "funty"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7"
[[package]] [[package]]
name = "funty" name = "funty"
version = "2.0.0" version = "2.0.0"
@ -1039,8 +1075,17 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d819e0a29c3201eb3373f9371be2bf821437979791ff07fe23a40ed8adadcfc3" checksum = "d819e0a29c3201eb3373f9371be2bf821437979791ff07fe23a40ed8adadcfc3"
dependencies = [ dependencies = [
"git-ref-format-core", "git-ref-format-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"git-ref-format-macro", "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]] [[package]]
@ -1053,13 +1098,33 @@ dependencies = [
"thiserror", "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]] [[package]]
name = "git-ref-format-macro" name = "git-ref-format-macro"
version = "0.1.0" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66434445c9d45a85a9186a76770b1f80a44fdf8ed34fb6d4e3d12c5b9fc942ef" checksum = "66434445c9d45a85a9186a76770b1f80a44fdf8ed34fb6d4e3d12c5b9fc942ef"
dependencies = [ 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", "proc-macro-error",
"quote", "quote",
"syn", "syn",
@ -1071,7 +1136,7 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0934f57135449b88bea0e28efd80aab0c1b53692f8207c7e232086db824c7a8" checksum = "e0934f57135449b88bea0e28efd80aab0c1b53692f8207c7e232086db824c7a8"
dependencies = [ dependencies = [
"nom", "nom 7.1.1",
"thiserror", "thiserror",
] ]
@ -1499,6 +1564,19 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" 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]] [[package]]
name = "lexopt" name = "lexopt"
version = "0.2.1" version = "0.2.1"
@ -1669,6 +1747,19 @@ dependencies = [
"socket2", "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]] [[package]]
name = "nom" name = "nom"
version = "7.1.1" version = "7.1.1"
@ -1679,6 +1770,12 @@ dependencies = [
"minimal-lexical", "minimal-lexical",
] ]
[[package]]
name = "nonempty"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ff7ac1e5ea23db6d61ad103e91864675049644bf47c35912336352fa4e9c109"
[[package]] [[package]]
name = "nonempty" name = "nonempty"
version = "0.8.0" version = "0.8.0"
@ -1797,7 +1894,7 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "786393f80485445794f6043fd3138854dd109cc6c4bd1a6383db304c9ce9b9ce" checksum = "786393f80485445794f6043fd3138854dd109cc6c4bd1a6383db304c9ce9b9ce"
dependencies = [ dependencies = [
"arrayvec", "arrayvec 0.7.2",
"auto_impl", "auto_impl",
"bytes", "bytes",
"ethereum-types", "ethereum-types",
@ -1869,8 +1966,8 @@ version = "3.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "366e44391a8af4cfd6002ef6ba072bae071a96aafca98d7d448a34c5dca38b6a" checksum = "366e44391a8af4cfd6002ef6ba072bae071a96aafca98d7d448a34c5dca38b6a"
dependencies = [ dependencies = [
"arrayvec", "arrayvec 0.7.2",
"bitvec", "bitvec 1.0.1",
"byte-slice-cast", "byte-slice-cast",
"impl-trait-for-tuples", "impl-trait-for-tuples",
"parity-scale-codec-derive", "parity-scale-codec-derive",
@ -2110,11 +2207,11 @@ dependencies = [
"cyphernet", "cyphernet",
"ed25519-compact", "ed25519-compact",
"fastrand", "fastrand",
"git-ref-format", "git-ref-format 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"git2", "git2",
"log", "log",
"multibase", "multibase",
"nonempty", "nonempty 0.8.0",
"olpc-cjson", "olpc-cjson",
"once_cell", "once_cell",
"pretty_assertions", "pretty_assertions",
@ -2168,7 +2265,7 @@ dependencies = [
"ed25519-compact", "ed25519-compact",
"fastrand", "fastrand",
"git-commit", "git-commit",
"git-ref-format", "git-ref-format 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"git-trailers", "git-trailers",
"git2", "git2",
"log", "log",
@ -2206,7 +2303,7 @@ dependencies = [
"cyphernet", "cyphernet",
"ed25519-compact", "ed25519-compact",
"fastrand", "fastrand",
"git-ref-format", "git-ref-format 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"multibase", "multibase",
"quickcheck", "quickcheck",
"quickcheck_macros", "quickcheck_macros",
@ -2223,10 +2320,9 @@ dependencies = [
[[package]] [[package]]
name = "radicle-git-ext" name = "radicle-git-ext"
version = "0.2.0" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/radicle-dev/radicle-git?rev=d3115a22158c8395705babefdc89049f7510d32d#d3115a22158c8395705babefdc89049f7510d32d"
checksum = "25ed92fcf331d19b3110bbed8d3fe2bd99dc75f0059fd135727d24ef829de507"
dependencies = [ dependencies = [
"git-ref-format", "git-ref-format 0.1.0 (git+https://github.com/radicle-dev/radicle-git?rev=d3115a22158c8395705babefdc89049f7510d32d)",
"git2", "git2",
"percent-encoding", "percent-encoding",
"radicle-std-ext", "radicle-std-ext",
@ -2241,12 +2337,14 @@ dependencies = [
"anyhow", "anyhow",
"axum", "axum",
"axum-server", "axum-server",
"chrono",
"ethers-core", "ethers-core",
"fastrand", "fastrand",
"flate2", "flate2",
"hyper", "hyper",
"lexopt", "lexopt",
"radicle", "radicle",
"radicle-surf",
"serde", "serde",
"serde_json", "serde_json",
"siwe", "siwe",
@ -2270,12 +2368,12 @@ dependencies = [
"colored", "colored",
"crossbeam-channel", "crossbeam-channel",
"fastrand", "fastrand",
"git-ref-format", "git-ref-format 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lexopt", "lexopt",
"log", "log",
"nakamoto-net", "nakamoto-net",
"nakamoto-net-poll", "nakamoto-net-poll",
"nonempty", "nonempty 0.8.0",
"quickcheck", "quickcheck",
"quickcheck_macros", "quickcheck_macros",
"radicle", "radicle",
@ -2311,18 +2409,43 @@ dependencies = [
[[package]] [[package]]
name = "radicle-std-ext" name = "radicle-std-ext"
version = "0.1.0" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/radicle-dev/radicle-git?rev=d3115a22158c8395705babefdc89049f7510d32d#d3115a22158c8395705babefdc89049f7510d32d"
checksum = "db20136bbc9ae63f3fec8e5a6c369f4902fac2244501b5dfc6d668e43475aaa4"
[[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]] [[package]]
name = "radicle-tools" name = "radicle-tools"
version = "0.2.0" version = "0.2.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"git-ref-format", "git-ref-format 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"radicle", "radicle",
] ]
[[package]]
name = "radium"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8"
[[package]] [[package]]
name = "radium" name = "radium"
version = "0.7.0" version = "0.7.0"
@ -2985,6 +3108,17 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" 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]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.3.0" version = "3.3.0"
@ -3614,6 +3748,12 @@ version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
[[package]]
name = "wyz"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"
[[package]] [[package]]
name = "wyz" name = "wyz"
version = "0.5.1" version = "0.5.1"
@ -3623,6 +3763,15 @@ dependencies = [
"tap", "tap",
] ]
[[package]]
name = "xattr"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "yaml-rust" name = "yaml-rust"
version = "0.4.5" version = "0.4.5"

View File

@ -35,3 +35,7 @@ version = "0.3.0"
git = "https://github.com/internet2-wg/rust-cyphernet" git = "https://github.com/internet2-wg/rust-cyphernet"
rev = "dee03a95abe4c964e5d9f8532c7dc76998dfeea7" rev = "dee03a95abe4c964e5d9f8532c7dc76998dfeea7"
version = "0.1.0" version = "0.1.0"
[patch.crates-io.radicle-git-ext]
git = "https://github.com/radicle-dev/radicle-git"
rev = "d3115a22158c8395705babefdc89049f7510d32d"

View File

@ -18,6 +18,7 @@ logfmt = [
anyhow = { version = "1" } anyhow = { version = "1" }
axum = { version = "0.5.16", default-features = false, features = ["json", "headers", "query"] } axum = { version = "0.5.16", default-features = false, features = ["json", "headers", "query"] }
axum-server = { version = "0.4.2", default-features = false } axum-server = { version = "0.4.2", default-features = false }
chrono = { version = "0.4.22" }
ethers-core = { version = "1.0" } ethers-core = { version = "1.0" }
fastrand = { version = "1.7.0" } fastrand = { version = "1.7.0" }
flate2 = { version = "1" } flate2 = { version = "1" }
@ -29,7 +30,7 @@ siwe = { version = "0.5" }
thiserror = { version = "1" } thiserror = { version = "1" }
time = { version = "0.3.17" } time = { version = "0.3.17" }
tokio = { version = "1.21", default-features = false, features = ["macros", "rt-multi-thread"] } 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 = { version = "0.1.37", default-features = false, features = ["std", "log"] }
tracing-logfmt = { version = "0.2", optional = true } tracing-logfmt = { version = "0.2", optional = true }
tracing-subscriber = { version = "0.3", default-features = false, features = ["std", "ansi", "fmt"] } 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] [dependencies.radicle]
path = "../radicle" path = "../radicle"
version = "0.2.0" version = "0.2.0"
[dependencies.radicle-surf]
git = "https://github.com/radicle-dev/radicle-git"
features = ["serialize"]
rev = "d3115a22158c8395705babefdc89049f7510d32d"

View File

@ -16,6 +16,9 @@ use tower_http::cors::{self, CorsLayer};
use tower_http::trace::TraceLayer; use tower_http::trace::TraceLayer;
use tracing::Span; use tracing::Span;
use radicle::cob::issue::Issues;
use radicle::identity::{Doc, Id};
use radicle::storage::{ReadRepository, WriteStorage};
use radicle::Profile; use radicle::Profile;
mod auth; mod auth;
@ -41,6 +44,22 @@ impl Context {
sessions: Default::default(), sessions: Default::default(),
} }
} }
pub fn project_info(&self, id: Id) -> Result<project::Info, error::Error> {
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 { pub fn router(ctx: Context) -> Router {
@ -115,3 +134,23 @@ pub struct PaginationQuery {
pub page: Option<usize>, pub page: Option<usize>,
pub per_page: Option<usize>, pub per_page: Option<usize>,
} }
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,
}
}

View File

@ -42,7 +42,19 @@ pub enum Error {
/// Storage error. /// Storage error.
#[error(transparent)] #[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 { impl Error {

View File

@ -1,5 +1,6 @@
mod delegates; mod delegates;
mod node; mod node;
mod projects;
mod sessions; mod sessions;
use axum::Router; use axum::Router;
@ -10,7 +11,8 @@ pub fn router(ctx: Context) -> Router {
let routes = Router::new() let routes = Router::new()
.merge(node::router(ctx.clone())) .merge(node::router(ctx.clone()))
.merge(sessions::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) Router::new().nest("/v1", routes)
} }

View File

@ -1,31 +1,17 @@
use axum::response::IntoResponse; use axum::response::IntoResponse;
use axum::routing::get; use axum::routing::get;
use axum::{Extension, Json, Router}; use axum::{Extension, Json, Router};
use serde::Serialize;
use radicle::cob::store::Store; use radicle::cob::issue::Issues;
use radicle::git::Oid;
use radicle::identity::project::Payload;
use radicle::identity::{Did, Doc}; use radicle::identity::{Did, Doc};
use radicle::storage::{ReadRepository, WriteStorage}; use radicle::storage::{ReadRepository, WriteStorage};
use crate::api::axum_extra::{Path, Query}; use crate::api::axum_extra::{Path, Query};
use crate::api::error::Error; use crate::api::error::Error;
use crate::api::project::Info;
use crate::api::Context; use crate::api::Context;
use crate::api::PaginationQuery; 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 { pub fn router(ctx: Context) -> Router {
Router::new() Router::new()
.route( .route(
@ -58,15 +44,14 @@ async fn delegates_projects_handler(
return None; return None;
} }
let Ok(cobs) = Store::open(ctx.profile.public_key, &repo) else { return None }; let Ok(issues) = Issues::open(ctx.profile.public_key, &repo) else { return None };
let Ok(issues) = cobs.issues().count() else { return None }; let Ok(issues) = (*issues).count() else { return None };
let Ok(patches) = cobs.patches().count() else { return None };
Some(Info { Some(Info {
payload, payload,
head, head,
issues, issues,
patches, patches: 0,
id, id,
}) })
}) })

View File

@ -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<Context>,
Query(qs): Query<PaginationQuery>,
) -> 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::<Vec<_>>();
Ok::<_, Error>(Json(projects))
}
/// Get project metadata.
/// `GET /projects/:project`
async fn project_handler(
Extension(ctx): Extension<Context>,
Path(id): Path<Id>,
) -> 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<Context>,
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<Context>,
Path(project): Path<Id>,
) -> 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::<Vec<i64>>();
Ok::<_, Error>((StatusCode::OK, Json(json!({ "activity": timestamps }))))
}

View File

@ -147,6 +147,13 @@ impl<'a, T: FromHistory> Store<'a, T> {
})) }))
} }
/// Return objects count.
pub fn count(&self) -> Result<usize, Error> {
let raw = cob::list(self.raw, T::type_name())?;
Ok(raw.len())
}
pub fn remove(&self, _id: &ObjectId) -> Result<(), Error> { pub fn remove(&self, _id: &ObjectId) -> Result<(), Error> {
todo!(); todo!();
} }