mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-10-29 00:10:47 +08:00
http: Rename LoC with projects ocurrences to repos
This commit is contained in:
+25
-23
@@ -47,16 +47,15 @@ impl Context {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn project_info<R: ReadRepository + radicle::cob::Store>(
|
||||
pub fn repo_info<R: ReadRepository + radicle::cob::Store>(
|
||||
&self,
|
||||
repo: &R,
|
||||
doc: DocAt,
|
||||
) -> Result<project::Info, error::Error> {
|
||||
) -> Result<repo::Info, error::Error> {
|
||||
let (_, head) = repo.head()?;
|
||||
let DocAt { doc, .. } = doc;
|
||||
let id = repo.id();
|
||||
let rid = repo.id();
|
||||
|
||||
let payload = doc.project()?;
|
||||
let aliases = self.profile.aliases();
|
||||
let delegates = doc
|
||||
.delegates
|
||||
@@ -66,17 +65,17 @@ impl Context {
|
||||
let issues = self.profile.issues(repo)?.counts()?;
|
||||
let patches = self.profile.patches(repo)?.counts()?;
|
||||
let db = &self.profile.database()?;
|
||||
let seeding = db.count(&id).unwrap_or_default();
|
||||
let seeding = db.count(&rid).unwrap_or_default();
|
||||
|
||||
Ok(project::Info {
|
||||
payload,
|
||||
Ok(repo::Info {
|
||||
payload: doc.payload,
|
||||
delegates,
|
||||
threshold: doc.threshold,
|
||||
visibility: doc.visibility,
|
||||
head,
|
||||
issues,
|
||||
patches,
|
||||
id,
|
||||
rid,
|
||||
seeding,
|
||||
})
|
||||
}
|
||||
@@ -130,14 +129,14 @@ async fn root_handler() -> impl IntoResponse {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PaginationQuery {
|
||||
#[serde(default)]
|
||||
pub show: ProjectQuery,
|
||||
pub show: RepoQuery,
|
||||
pub page: Option<usize>,
|
||||
pub per_page: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum ProjectQuery {
|
||||
pub enum RepoQuery {
|
||||
All,
|
||||
#[default]
|
||||
Pinned,
|
||||
@@ -209,16 +208,17 @@ impl PatchStatus {
|
||||
|
||||
mod search {
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use nonempty::NonEmpty;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
|
||||
use radicle::crypto::Verified;
|
||||
use radicle::identity::{Project, RepoId};
|
||||
use radicle::identity::doc::{Payload, PayloadId};
|
||||
use radicle::identity::RepoId;
|
||||
use radicle::node::routing::Store;
|
||||
use radicle::node::AliasStore;
|
||||
use radicle::node::Database;
|
||||
use radicle::node::{AliasStore, Database};
|
||||
use radicle::profile::Aliases;
|
||||
use radicle::storage::RepositoryInfo;
|
||||
|
||||
@@ -234,7 +234,7 @@ mod search {
|
||||
pub struct SearchResult {
|
||||
pub rid: RepoId,
|
||||
#[serde(flatten)]
|
||||
pub payload: Project,
|
||||
pub payload: BTreeMap<PayloadId, Payload>,
|
||||
pub delegates: NonEmpty<serde_json::Value>,
|
||||
pub seeds: usize,
|
||||
#[serde(skip)]
|
||||
@@ -251,8 +251,9 @@ mod search {
|
||||
if info.doc.visibility.is_private() {
|
||||
return None;
|
||||
}
|
||||
let payload = info.doc.project().ok()?;
|
||||
let index = payload.name().find(q)?;
|
||||
let Ok(Some(index)) = info.doc.project().map(|p| p.name().find(q)) else {
|
||||
return None;
|
||||
};
|
||||
let seeds = db.count(&info.rid).unwrap_or_default();
|
||||
let delegates = info.doc.delegates.map(|did| match aliases.alias(&did) {
|
||||
Some(alias) => json!({
|
||||
@@ -266,7 +267,7 @@ mod search {
|
||||
|
||||
Some(SearchResult {
|
||||
rid: info.rid,
|
||||
payload,
|
||||
payload: info.doc.payload,
|
||||
delegates,
|
||||
seeds,
|
||||
index,
|
||||
@@ -299,29 +300,30 @@ mod search {
|
||||
}
|
||||
}
|
||||
|
||||
mod project {
|
||||
mod repo {
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use serde::Serialize;
|
||||
use serde_json::Value;
|
||||
|
||||
use radicle::cob;
|
||||
use radicle::git::Oid;
|
||||
use radicle::identity::project::Project;
|
||||
use radicle::identity::doc::{Payload, PayloadId};
|
||||
use radicle::identity::{RepoId, Visibility};
|
||||
|
||||
/// Project info.
|
||||
/// Repos info.
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Info {
|
||||
/// Project metadata.
|
||||
#[serde(flatten)]
|
||||
pub payload: Project,
|
||||
pub payload: BTreeMap<PayloadId, Payload>,
|
||||
pub delegates: Vec<Value>,
|
||||
pub threshold: usize,
|
||||
pub visibility: Visibility,
|
||||
pub head: Oid,
|
||||
pub patches: cob::patch::PatchCounts,
|
||||
pub issues: cob::issue::IssueCounts,
|
||||
pub id: RepoId,
|
||||
pub rid: RepoId,
|
||||
pub seeding: usize,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,8 +36,13 @@ async fn root_handler(State(ctx): State<Context>) -> impl IntoResponse {
|
||||
"path": "/api/v1",
|
||||
"links": [
|
||||
{
|
||||
"href": "/projects",
|
||||
"rel": "projects",
|
||||
"href": "/repos",
|
||||
"rel": "repos",
|
||||
"type": "GET"
|
||||
},
|
||||
{
|
||||
"href": "/repos/:rid",
|
||||
"rel": "repo",
|
||||
"type": "GET"
|
||||
},
|
||||
{
|
||||
@@ -51,8 +56,8 @@ async fn root_handler(State(ctx): State<Context>) -> impl IntoResponse {
|
||||
"type": "GET"
|
||||
},
|
||||
{
|
||||
"href": "/delegates/:did/projects",
|
||||
"rel": "projects",
|
||||
"href": "/delegates/:did/repos",
|
||||
"rel": "repos",
|
||||
"type": "GET"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -12,24 +12,19 @@ use radicle::patch::cache::Patches as _;
|
||||
use radicle::storage::{ReadRepository, ReadStorage};
|
||||
|
||||
use crate::api::error::Error;
|
||||
use crate::api::json;
|
||||
use crate::api::project::Info;
|
||||
use crate::api::Context;
|
||||
use crate::api::{PaginationQuery, ProjectQuery};
|
||||
use crate::api::repo::Info;
|
||||
use crate::api::{json, Context, PaginationQuery, RepoQuery};
|
||||
use crate::axum_extra::{Path, Query};
|
||||
|
||||
pub fn router(ctx: Context) -> Router {
|
||||
Router::new()
|
||||
.route(
|
||||
"/delegates/:delegate/projects",
|
||||
get(delegates_projects_handler),
|
||||
)
|
||||
.route("/delegates/:delegate/repos", get(delegates_repos_handler))
|
||||
.with_state(ctx)
|
||||
}
|
||||
|
||||
/// List all projects which delegate is a part of.
|
||||
/// `GET /delegates/:delegate/projects`
|
||||
async fn delegates_projects_handler(
|
||||
/// List all repos which delegate is a part of.
|
||||
/// `GET /delegates/:delegate/repos`
|
||||
async fn delegates_repos_handler(
|
||||
State(ctx): State<Context>,
|
||||
Path(delegate): Path<Did>,
|
||||
Query(qs): Query<PaginationQuery>,
|
||||
@@ -44,17 +39,21 @@ async fn delegates_projects_handler(
|
||||
let storage = &ctx.profile.storage;
|
||||
let db = &ctx.profile.database()?;
|
||||
let pinned = &ctx.profile.config.web.pinned;
|
||||
let mut projects = match show {
|
||||
ProjectQuery::All => storage
|
||||
let mut repos = match show {
|
||||
RepoQuery::All => storage
|
||||
.repositories()?
|
||||
.into_iter()
|
||||
.filter(|repo| repo.doc.visibility.is_public())
|
||||
.collect::<Vec<_>>(),
|
||||
ProjectQuery::Pinned => storage.repositories_by_id(pinned.repositories.iter())?,
|
||||
RepoQuery::Pinned => storage
|
||||
.repositories_by_id(pinned.repositories.iter())?
|
||||
.into_iter()
|
||||
.filter(|repo| repo.doc.visibility.is_public())
|
||||
.collect::<Vec<_>>(),
|
||||
};
|
||||
projects.sort_by_key(|p| p.rid);
|
||||
repos.sort_by_key(|p| p.rid);
|
||||
|
||||
let infos = projects
|
||||
let infos = repos
|
||||
.into_iter()
|
||||
.filter_map(|id| {
|
||||
if !id.doc.delegates.iter().any(|d| *d == delegate) {
|
||||
@@ -66,7 +65,7 @@ async fn delegates_projects_handler(
|
||||
let Ok((_, head)) = repo.head() else {
|
||||
return None;
|
||||
};
|
||||
let Ok(payload) = id.doc.project() else {
|
||||
let Ok(_payload) = id.doc.project() else {
|
||||
return None;
|
||||
};
|
||||
let Ok(issues) = ctx.profile.issues(&repo) else {
|
||||
@@ -92,14 +91,14 @@ async fn delegates_projects_handler(
|
||||
let seeding = db.count(&id.rid).unwrap_or_default();
|
||||
|
||||
Some(Info {
|
||||
payload,
|
||||
payload: id.doc.payload,
|
||||
delegates,
|
||||
threshold: id.doc.threshold,
|
||||
visibility: id.doc.visibility,
|
||||
head,
|
||||
issues,
|
||||
patches,
|
||||
id: id.rid,
|
||||
rid: id.rid,
|
||||
seeding,
|
||||
})
|
||||
})
|
||||
@@ -121,14 +120,14 @@ mod routes {
|
||||
use crate::test::{self, get, CONTRIBUTOR_ALIAS, DID, HEAD, RID};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_delegates_projects() {
|
||||
async fn test_delegates_repos() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let seed = test::seed(tmp.path());
|
||||
let app = super::router(seed.clone())
|
||||
.layer(MockConnectInfo(SocketAddr::from(([127, 0, 0, 1], 8080))));
|
||||
let response = get(
|
||||
&app,
|
||||
"/delegates/did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/projects?show=all",
|
||||
"/delegates/did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/repos?show=all",
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -142,9 +141,11 @@ mod routes {
|
||||
response.json().await,
|
||||
json!([
|
||||
{
|
||||
"name": "hello-world",
|
||||
"description": "Rad repository for tests",
|
||||
"defaultBranch": "master",
|
||||
"xyz.radicle.project": {
|
||||
"defaultBranch": "master",
|
||||
"description": "Rad repository for tests",
|
||||
"name": "hello-world",
|
||||
},
|
||||
"delegates": [
|
||||
{
|
||||
"id": DID,
|
||||
@@ -166,13 +167,15 @@ mod routes {
|
||||
"open": 1,
|
||||
"closed": 0,
|
||||
},
|
||||
"id": RID,
|
||||
"rid": RID,
|
||||
"seeding": 1,
|
||||
},
|
||||
{
|
||||
"name": "again-hello-world",
|
||||
"description": "Rad repository for sorting",
|
||||
"defaultBranch": "master",
|
||||
"xyz.radicle.project": {
|
||||
"defaultBranch": "master",
|
||||
"description": "Rad repository for sorting",
|
||||
"name": "again-hello-world",
|
||||
},
|
||||
"delegates": [
|
||||
{
|
||||
"id": DID,
|
||||
@@ -194,7 +197,7 @@ mod routes {
|
||||
"open": 0,
|
||||
"closed": 0,
|
||||
},
|
||||
"id": "rad:z4GypKmh1gkEfmkXtarcYnkvtFUfE",
|
||||
"rid": "rad:z4GypKmh1gkEfmkXtarcYnkvtFUfE",
|
||||
"seeding": 1,
|
||||
}
|
||||
])
|
||||
@@ -206,7 +209,7 @@ mod routes {
|
||||
))));
|
||||
let response = get(
|
||||
&app,
|
||||
"/delegates/did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/projects?show=all",
|
||||
"/delegates/did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/repos?show=all",
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -220,9 +223,11 @@ mod routes {
|
||||
response.json().await,
|
||||
json!([
|
||||
{
|
||||
"name": "hello-world",
|
||||
"description": "Rad repository for tests",
|
||||
"defaultBranch": "master",
|
||||
"xyz.radicle.project": {
|
||||
"defaultBranch": "master",
|
||||
"description": "Rad repository for tests",
|
||||
"name": "hello-world",
|
||||
},
|
||||
"delegates": [
|
||||
{
|
||||
"id": DID,
|
||||
@@ -244,13 +249,15 @@ mod routes {
|
||||
"open": 1,
|
||||
"closed": 0,
|
||||
},
|
||||
"id": RID,
|
||||
"rid": RID,
|
||||
"seeding": 1,
|
||||
},
|
||||
{
|
||||
"name": "again-hello-world",
|
||||
"description": "Rad repository for sorting",
|
||||
"defaultBranch": "master",
|
||||
"xyz.radicle.project": {
|
||||
"defaultBranch": "master",
|
||||
"description": "Rad repository for sorting",
|
||||
"name": "again-hello-world",
|
||||
},
|
||||
"delegates": [
|
||||
{
|
||||
"id": DID,
|
||||
@@ -272,7 +279,7 @@ mod routes {
|
||||
"open": 0,
|
||||
"closed": 0,
|
||||
},
|
||||
"id": "rad:z4GypKmh1gkEfmkXtarcYnkvtFUfE",
|
||||
"rid": "rad:z4GypKmh1gkEfmkXtarcYnkvtFUfE",
|
||||
"seeding": 1,
|
||||
}
|
||||
])
|
||||
|
||||
@@ -17,44 +17,41 @@ use radicle::node::routing::Store;
|
||||
use radicle::node::{AliasStore, NodeId};
|
||||
use radicle::storage::{ReadRepository, ReadStorage, RemoteRepository};
|
||||
|
||||
use crate::api;
|
||||
use crate::api::error::Error;
|
||||
use crate::api::project::Info;
|
||||
use crate::api::search::{SearchQueryString, SearchResult};
|
||||
use crate::api::{self, CobsQuery, Context, PaginationQuery, ProjectQuery};
|
||||
use crate::api::{CobsQuery, Context, PaginationQuery, RepoQuery};
|
||||
use crate::axum_extra::{cached_response, immutable_response, Path, Query};
|
||||
|
||||
const MAX_BODY_LIMIT: usize = 4_194_304;
|
||||
|
||||
pub fn router(ctx: Context) -> Router {
|
||||
Router::new()
|
||||
.route("/projects", get(project_root_handler))
|
||||
.route("/projects/search", get(project_search_handler))
|
||||
.route("/projects/:project", get(project_handler))
|
||||
.route("/projects/:project/commits", get(history_handler))
|
||||
.route("/projects/:project/commits/:sha", get(commit_handler))
|
||||
.route("/projects/:project/diff/:base/:oid", get(diff_handler))
|
||||
.route("/projects/:project/activity", get(activity_handler))
|
||||
.route("/projects/:project/tree/:sha/", get(tree_handler_root))
|
||||
.route("/projects/:project/tree/:sha/*path", get(tree_handler))
|
||||
.route(
|
||||
"/projects/:project/stats/tree/:sha",
|
||||
get(stats_tree_handler),
|
||||
)
|
||||
.route("/projects/:project/remotes", get(remotes_handler))
|
||||
.route("/projects/:project/remotes/:peer", get(remote_handler))
|
||||
.route("/projects/:project/blob/:sha/*path", get(blob_handler))
|
||||
.route("/projects/:project/readme/:sha", get(readme_handler))
|
||||
.route("/projects/:project/issues", get(issues_handler))
|
||||
.route("/projects/:project/issues/:id", get(issue_handler))
|
||||
.route("/projects/:project/patches", get(patches_handler))
|
||||
.route("/projects/:project/patches/:id", get(patch_handler))
|
||||
.route("/repos", get(repo_root_handler))
|
||||
.route("/repos/search", get(repo_search_handler))
|
||||
.route("/repos/:rid", get(repo_handler))
|
||||
.route("/repos/:rid/commits", get(history_handler))
|
||||
.route("/repos/:rid/commits/:sha", get(commit_handler))
|
||||
.route("/repos/:rid/diff/:base/:oid", get(diff_handler))
|
||||
.route("/repos/:rid/activity", get(activity_handler))
|
||||
.route("/repos/:rid/tree/:sha/", get(tree_handler_root))
|
||||
.route("/repos/:rid/tree/:sha/*path", get(tree_handler))
|
||||
.route("/repos/:rid/stats/tree/:sha", get(stats_tree_handler))
|
||||
.route("/repos/:rid/remotes", get(remotes_handler))
|
||||
.route("/repos/:rid/remotes/:peer", get(remote_handler))
|
||||
.route("/repos/:rid/blob/:sha/*path", get(blob_handler))
|
||||
.route("/repos/:rid/readme/:sha", get(readme_handler))
|
||||
.route("/repos/:rid/issues", get(issues_handler))
|
||||
.route("/repos/:rid/issues/:id", get(issue_handler))
|
||||
.route("/repos/:rid/patches", get(patches_handler))
|
||||
.route("/repos/:rid/patches/:id", get(patch_handler))
|
||||
.with_state(ctx)
|
||||
.layer(DefaultBodyLimit::max(MAX_BODY_LIMIT))
|
||||
}
|
||||
|
||||
/// List all projects.
|
||||
/// `GET /projects`
|
||||
async fn project_root_handler(
|
||||
/// List all repos.
|
||||
/// `GET /repos`
|
||||
async fn repo_root_handler(
|
||||
State(ctx): State<Context>,
|
||||
Query(qs): Query<PaginationQuery>,
|
||||
) -> impl IntoResponse {
|
||||
@@ -65,7 +62,7 @@ async fn project_root_handler(
|
||||
} = qs;
|
||||
let page = page.unwrap_or(0);
|
||||
let per_page = per_page.unwrap_or_else(|| match show {
|
||||
ProjectQuery::Pinned => ctx.profile.config.web.pinned.repositories.len(),
|
||||
RepoQuery::Pinned => ctx.profile.config.web.pinned.repositories.len(),
|
||||
_ => 10,
|
||||
});
|
||||
let storage = &ctx.profile.storage;
|
||||
@@ -73,21 +70,21 @@ async fn project_root_handler(
|
||||
let pinned = &ctx.profile.config.web.pinned;
|
||||
let policies = ctx.profile.policies()?;
|
||||
|
||||
let mut projects = match show {
|
||||
ProjectQuery::All => storage
|
||||
let mut repos = match show {
|
||||
RepoQuery::All => storage
|
||||
.repositories()?
|
||||
.into_iter()
|
||||
.filter(|repo| repo.doc.visibility.is_public())
|
||||
.collect::<Vec<_>>(),
|
||||
ProjectQuery::Pinned => storage
|
||||
RepoQuery::Pinned => storage
|
||||
.repositories_by_id(pinned.repositories.iter())?
|
||||
.into_iter()
|
||||
.filter(|repo| repo.doc.visibility.is_public())
|
||||
.collect::<Vec<_>>(),
|
||||
};
|
||||
projects.sort_by_key(|p| p.rid);
|
||||
repos.sort_by_key(|p| p.rid);
|
||||
|
||||
let infos = projects
|
||||
let infos = repos
|
||||
.into_iter()
|
||||
.filter_map(|info| {
|
||||
if !policies.is_seeding(&info.rid).unwrap_or_default() {
|
||||
@@ -99,7 +96,7 @@ async fn project_root_handler(
|
||||
let Ok((_, head)) = repo.head() else {
|
||||
return None;
|
||||
};
|
||||
let Ok(payload) = info.doc.project() else {
|
||||
let Ok(_payload) = info.doc.project() else {
|
||||
return None;
|
||||
};
|
||||
let Ok(issues) = ctx.profile.issues(&repo) else {
|
||||
@@ -123,15 +120,15 @@ async fn project_root_handler(
|
||||
.collect::<Vec<_>>();
|
||||
let seeding = db.count(&info.rid).unwrap_or_default();
|
||||
|
||||
Some(Info {
|
||||
payload,
|
||||
Some(api::repo::Info {
|
||||
payload: info.doc.payload,
|
||||
delegates,
|
||||
head,
|
||||
threshold: info.doc.threshold,
|
||||
visibility: info.doc.visibility,
|
||||
issues,
|
||||
patches,
|
||||
id: info.rid,
|
||||
rid: info.rid,
|
||||
seeding,
|
||||
})
|
||||
})
|
||||
@@ -143,7 +140,7 @@ async fn project_root_handler(
|
||||
}
|
||||
|
||||
/// Search repositories by name.
|
||||
/// `GET /projects/search?q=<query>`
|
||||
/// `GET /repos/search?q=<query>`
|
||||
///
|
||||
/// We obtain the byte index of the first character of the query that matches the repo name.
|
||||
/// And skip if the query doesn't match the repo name.
|
||||
@@ -153,7 +150,7 @@ async fn project_root_handler(
|
||||
/// A repo name with a byte index of 0 should come before non-zero indices.
|
||||
/// If both indices are non-zero and equal, then compare by seeding count.
|
||||
/// If none of the above, all non-zero indices are compared by their seeding count primarily.
|
||||
async fn project_search_handler(
|
||||
async fn repo_search_handler(
|
||||
State(ctx): State<Context>,
|
||||
Query(SearchQueryString { q, per_page, page }): Query<SearchQueryString>,
|
||||
) -> impl IntoResponse {
|
||||
@@ -178,11 +175,11 @@ async fn project_search_handler(
|
||||
Ok::<_, Error>(cached_response(found_repos, 600).into_response())
|
||||
}
|
||||
|
||||
/// Get project metadata.
|
||||
/// `GET /projects/:project`
|
||||
async fn project_handler(State(ctx): State<Context>, Path(rid): Path<RepoId>) -> impl IntoResponse {
|
||||
/// Get repo metadata.
|
||||
/// `GET /repos/:rid`
|
||||
async fn repo_handler(State(ctx): State<Context>, Path(rid): Path<RepoId>) -> impl IntoResponse {
|
||||
let (repo, doc) = ctx.repo(rid)?;
|
||||
let info = ctx.project_info(&repo, doc)?;
|
||||
let info = ctx.repo_info(&repo, doc)?;
|
||||
|
||||
Ok::<_, Error>(Json(info))
|
||||
}
|
||||
@@ -197,8 +194,8 @@ pub struct CommitsQueryString {
|
||||
pub per_page: Option<usize>,
|
||||
}
|
||||
|
||||
/// Get project commit range.
|
||||
/// `GET /projects/:project/commits?parent=<sha>`
|
||||
/// Get repo commit range.
|
||||
/// `GET /repos/:rid/commits?parent=<sha>`
|
||||
async fn history_handler(
|
||||
State(ctx): State<Context>,
|
||||
Path(rid): Path<RepoId>,
|
||||
@@ -220,7 +217,7 @@ async fn history_handler(
|
||||
|
||||
let sha = match parent {
|
||||
Some(commit) => commit,
|
||||
None => ctx.project_info(&repo, doc)?.head.to_string(),
|
||||
None => ctx.repo_info(&repo, doc)?.head.to_string(),
|
||||
};
|
||||
let repo = Repository::open(repo.path())?;
|
||||
|
||||
@@ -257,13 +254,13 @@ async fn history_handler(
|
||||
}
|
||||
}
|
||||
|
||||
/// Get project commit.
|
||||
/// `GET /projects/:project/commits/:sha`
|
||||
/// Get repo commit.
|
||||
/// `GET /repos/:rid/commits/:sha`
|
||||
async fn commit_handler(
|
||||
State(ctx): State<Context>,
|
||||
Path((project, sha)): Path<(RepoId, Oid)>,
|
||||
Path((rid, sha)): Path<(RepoId, Oid)>,
|
||||
) -> impl IntoResponse {
|
||||
let (repo, _) = ctx.repo(project)?;
|
||||
let (repo, _) = ctx.repo(rid)?;
|
||||
let repo = Repository::open(repo.path())?;
|
||||
let commit = repo.commit(sha)?;
|
||||
|
||||
@@ -324,12 +321,12 @@ async fn commit_handler(
|
||||
}
|
||||
|
||||
/// Get diff between two commits
|
||||
/// `GET /projects/:project/diff/:base/:oid`
|
||||
/// `GET /repos/:rid/diff/:base/:oid`
|
||||
async fn diff_handler(
|
||||
State(ctx): State<Context>,
|
||||
Path((project, base, oid)): Path<(RepoId, Oid, Oid)>,
|
||||
Path((rid, base, oid)): Path<(RepoId, Oid, Oid)>,
|
||||
) -> impl IntoResponse {
|
||||
let (repo, _) = ctx.repo(project)?;
|
||||
let (repo, _) = ctx.repo(rid)?;
|
||||
let repo = Repository::open(repo.path())?;
|
||||
let base = repo.commit(base)?;
|
||||
let commit = repo.commit(oid)?;
|
||||
@@ -390,13 +387,13 @@ async fn diff_handler(
|
||||
Ok::<_, Error>(immutable_response(response))
|
||||
}
|
||||
|
||||
/// Get project activity for the past year.
|
||||
/// `GET /projects/:project/activity`
|
||||
/// Get repo activity for the past year.
|
||||
/// `GET /repos/:rid/activity`
|
||||
async fn activity_handler(
|
||||
State(ctx): State<Context>,
|
||||
Path(project): Path<RepoId>,
|
||||
Path(rid): Path<RepoId>,
|
||||
) -> impl IntoResponse {
|
||||
let (repo, _) = ctx.repo(project)?;
|
||||
let (repo, _) = ctx.repo(rid)?;
|
||||
let current_date = chrono::Utc::now().timestamp();
|
||||
// SAFETY: The number of weeks is static and not out of bounds.
|
||||
#[allow(clippy::unwrap_used)]
|
||||
@@ -419,8 +416,8 @@ async fn activity_handler(
|
||||
Ok::<_, Error>(cached_response(json!({ "activity": timestamps }), 3600))
|
||||
}
|
||||
|
||||
/// Get project source tree for '/' path.
|
||||
/// `GET /projects/:project/tree/:sha/`
|
||||
/// Get repo source tree for '/' path.
|
||||
/// `GET /repos/:rid/tree/:sha/`
|
||||
async fn tree_handler_root(
|
||||
State(ctx): State<Context>,
|
||||
Path((rid, sha)): Path<(RepoId, Oid)>,
|
||||
@@ -428,17 +425,17 @@ async fn tree_handler_root(
|
||||
tree_handler(State(ctx), Path((rid, sha, String::new()))).await
|
||||
}
|
||||
|
||||
/// Get project source tree.
|
||||
/// `GET /projects/:project/tree/:sha/*path`
|
||||
/// Get repo source tree.
|
||||
/// `GET /repos/:rid/tree/:sha/*path`
|
||||
async fn tree_handler(
|
||||
State(ctx): State<Context>,
|
||||
Path((project, sha, path)): Path<(RepoId, Oid, String)>,
|
||||
Path((rid, sha, path)): Path<(RepoId, Oid, String)>,
|
||||
) -> impl IntoResponse {
|
||||
let (repo, _) = ctx.repo(project)?;
|
||||
let (repo, _) = ctx.repo(rid)?;
|
||||
|
||||
if let Some(ref cache) = ctx.cache {
|
||||
let cache = &mut cache.tree.lock().await;
|
||||
if let Some(response) = cache.get(&(project, sha, path.clone())) {
|
||||
if let Some(response) = cache.get(&(rid, sha, path.clone())) {
|
||||
return Ok::<_, Error>(immutable_response(response.clone()));
|
||||
}
|
||||
}
|
||||
@@ -449,32 +446,29 @@ async fn tree_handler(
|
||||
|
||||
if let Some(cache) = &ctx.cache {
|
||||
let cache = &mut cache.tree.lock().await;
|
||||
cache.put((project, sha, path.clone()), response.clone());
|
||||
cache.put((rid, sha, path.clone()), response.clone());
|
||||
}
|
||||
|
||||
Ok::<_, Error>(immutable_response(response))
|
||||
}
|
||||
|
||||
/// Get project source tree stats.
|
||||
/// `GET /projects/:project/stats/tree/:sha`
|
||||
/// Get repo source tree stats.
|
||||
/// `GET /repos/:rid/stats/tree/:sha`
|
||||
async fn stats_tree_handler(
|
||||
State(ctx): State<Context>,
|
||||
Path((project, sha)): Path<(RepoId, Oid)>,
|
||||
Path((rid, sha)): Path<(RepoId, Oid)>,
|
||||
) -> impl IntoResponse {
|
||||
let (repo, _) = ctx.repo(project)?;
|
||||
let (repo, _) = ctx.repo(rid)?;
|
||||
let repo = Repository::open(repo.path())?;
|
||||
let stats = repo.stats_from(&sha)?;
|
||||
|
||||
Ok::<_, Error>(immutable_response(stats))
|
||||
}
|
||||
|
||||
/// Get all project remotes.
|
||||
/// `GET /projects/:project/remotes`
|
||||
async fn remotes_handler(
|
||||
State(ctx): State<Context>,
|
||||
Path(project): Path<RepoId>,
|
||||
) -> impl IntoResponse {
|
||||
let (repo, doc) = ctx.repo(project)?;
|
||||
/// Get all repo remotes.
|
||||
/// `GET /repos/:rid/remotes`
|
||||
async fn remotes_handler(State(ctx): State<Context>, Path(rid): Path<RepoId>) -> impl IntoResponse {
|
||||
let (repo, doc) = ctx.repo(rid)?;
|
||||
let delegates = &doc.delegates;
|
||||
let aliases = &ctx.profile.aliases();
|
||||
let remotes = repo
|
||||
@@ -510,13 +504,13 @@ async fn remotes_handler(
|
||||
Ok::<_, Error>(Json(remotes))
|
||||
}
|
||||
|
||||
/// Get project remote.
|
||||
/// `GET /projects/:project/remotes/:peer`
|
||||
/// Get repo remote.
|
||||
/// `GET /repos/:rid/remotes/:peer`
|
||||
async fn remote_handler(
|
||||
State(ctx): State<Context>,
|
||||
Path((project, node_id)): Path<(RepoId, NodeId)>,
|
||||
Path((rid, node_id)): Path<(RepoId, NodeId)>,
|
||||
) -> impl IntoResponse {
|
||||
let (repo, doc) = ctx.repo(project)?;
|
||||
let (repo, doc) = ctx.repo(rid)?;
|
||||
let delegates = &doc.delegates;
|
||||
let remote = repo.remote(&node_id)?;
|
||||
let refs = remote
|
||||
@@ -537,13 +531,13 @@ async fn remote_handler(
|
||||
Ok::<_, Error>(Json(remote))
|
||||
}
|
||||
|
||||
/// Get project source file.
|
||||
/// `GET /projects/:project/blob/:sha/*path`
|
||||
/// Get repo source file.
|
||||
/// `GET /repos/:rid/blob/:sha/*path`
|
||||
async fn blob_handler(
|
||||
State(ctx): State<Context>,
|
||||
Path((project, sha, path)): Path<(RepoId, Oid, String)>,
|
||||
Path((rid, sha, path)): Path<(RepoId, Oid, String)>,
|
||||
) -> impl IntoResponse {
|
||||
let (repo, _) = ctx.repo(project)?;
|
||||
let (repo, _) = ctx.repo(rid)?;
|
||||
let repo = Repository::open(repo.path())?;
|
||||
let blob = repo.blob(sha, &path)?;
|
||||
|
||||
@@ -560,13 +554,13 @@ async fn blob_handler(
|
||||
Ok::<_, Error>(immutable_response(api::json::blob(&blob, &path)).into_response())
|
||||
}
|
||||
|
||||
/// Get project readme.
|
||||
/// `GET /projects/:project/readme/:sha`
|
||||
/// Get repo readme.
|
||||
/// `GET /repos/:rid/readme/:sha`
|
||||
async fn readme_handler(
|
||||
State(ctx): State<Context>,
|
||||
Path((project, sha)): Path<(RepoId, Oid)>,
|
||||
Path((rid, sha)): Path<(RepoId, Oid)>,
|
||||
) -> impl IntoResponse {
|
||||
let (repo, _) = ctx.repo(project)?;
|
||||
let (repo, _) = ctx.repo(rid)?;
|
||||
let repo = Repository::open(repo.path())?;
|
||||
let paths = [
|
||||
"README",
|
||||
@@ -604,14 +598,14 @@ async fn readme_handler(
|
||||
Err(Error::NotFound)
|
||||
}
|
||||
|
||||
/// Get project issues list.
|
||||
/// `GET /projects/:project/issues`
|
||||
/// Get repo issues list.
|
||||
/// `GET /repos/:rid/issues`
|
||||
async fn issues_handler(
|
||||
State(ctx): State<Context>,
|
||||
Path(project): Path<RepoId>,
|
||||
Path(rid): Path<RepoId>,
|
||||
Query(qs): Query<CobsQuery<api::IssueStatus>>,
|
||||
) -> impl IntoResponse {
|
||||
let (repo, _) = ctx.repo(project)?;
|
||||
let (repo, _) = ctx.repo(rid)?;
|
||||
let CobsQuery {
|
||||
page,
|
||||
per_page,
|
||||
@@ -641,13 +635,13 @@ async fn issues_handler(
|
||||
Ok::<_, Error>(Json(issues))
|
||||
}
|
||||
|
||||
/// Get project issue.
|
||||
/// `GET /projects/:project/issues/:id`
|
||||
/// Get repo issue.
|
||||
/// `GET /repos/:rid/issues/:id`
|
||||
async fn issue_handler(
|
||||
State(ctx): State<Context>,
|
||||
Path((project, issue_id)): Path<(RepoId, Oid)>,
|
||||
Path((rid, issue_id)): Path<(RepoId, Oid)>,
|
||||
) -> impl IntoResponse {
|
||||
let (repo, _) = ctx.repo(project)?;
|
||||
let (repo, _) = ctx.repo(rid)?;
|
||||
let issue = ctx
|
||||
.profile
|
||||
.issues(&repo)?
|
||||
@@ -658,8 +652,8 @@ async fn issue_handler(
|
||||
Ok::<_, Error>(Json(api::json::issue(issue_id.into(), issue, &aliases)))
|
||||
}
|
||||
|
||||
/// Get project patches list.
|
||||
/// `GET /projects/:project/patches`
|
||||
/// Get repo patches list.
|
||||
/// `GET /repos/:rid/patches`
|
||||
async fn patches_handler(
|
||||
State(ctx): State<Context>,
|
||||
Path(rid): Path<RepoId>,
|
||||
@@ -694,8 +688,8 @@ async fn patches_handler(
|
||||
Ok::<_, Error>(Json(patches))
|
||||
}
|
||||
|
||||
/// Get project patch.
|
||||
/// `GET /projects/:project/patches/:id`
|
||||
/// Get repo patch.
|
||||
/// `GET /repos/:rid/patches/:id`
|
||||
async fn patch_handler(
|
||||
State(ctx): State<Context>,
|
||||
Path((rid, patch_id)): Path<(RepoId, Oid)>,
|
||||
@@ -726,21 +720,23 @@ mod routes {
|
||||
use crate::test::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_projects_root() {
|
||||
async fn test_repos_root() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
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?show=all").await;
|
||||
let response = get(&app, "/repos?show=all").await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
response.json().await,
|
||||
json!([
|
||||
{
|
||||
"name": "hello-world",
|
||||
"description": "Rad repository for tests",
|
||||
"defaultBranch": "master",
|
||||
"xyz.radicle.project": {
|
||||
"defaultBranch": "master",
|
||||
"description": "Rad repository for tests",
|
||||
"name": "hello-world",
|
||||
},
|
||||
"delegates": [
|
||||
{
|
||||
"id": DID,
|
||||
@@ -762,13 +758,15 @@ mod routes {
|
||||
"open": 1,
|
||||
"closed": 0,
|
||||
},
|
||||
"id": RID,
|
||||
"rid": RID,
|
||||
"seeding": 1,
|
||||
},
|
||||
{
|
||||
"name": "again-hello-world",
|
||||
"description": "Rad repository for sorting",
|
||||
"defaultBranch": "master",
|
||||
"xyz.radicle.project": {
|
||||
"defaultBranch": "master",
|
||||
"description": "Rad repository for sorting",
|
||||
"name": "again-hello-world",
|
||||
},
|
||||
"delegates": [
|
||||
{
|
||||
"id": DID,
|
||||
@@ -790,7 +788,7 @@ mod routes {
|
||||
"open": 0,
|
||||
"closed": 0,
|
||||
},
|
||||
"id": "rad:z4GypKmh1gkEfmkXtarcYnkvtFUfE",
|
||||
"rid": "rad:z4GypKmh1gkEfmkXtarcYnkvtFUfE",
|
||||
"seeding": 1,
|
||||
},
|
||||
])
|
||||
@@ -800,16 +798,18 @@ mod routes {
|
||||
[192, 168, 13, 37],
|
||||
8080,
|
||||
))));
|
||||
let response = get(&app, "/projects?show=all").await;
|
||||
let response = get(&app, "/repos?show=all").await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
response.json().await,
|
||||
json!([
|
||||
{
|
||||
"name": "hello-world",
|
||||
"description": "Rad repository for tests",
|
||||
"defaultBranch": "master",
|
||||
"xyz.radicle.project": {
|
||||
"defaultBranch": "master",
|
||||
"description": "Rad repository for tests",
|
||||
"name": "hello-world",
|
||||
},
|
||||
"delegates": [
|
||||
{
|
||||
"id": DID,
|
||||
@@ -831,13 +831,15 @@ mod routes {
|
||||
"open": 1,
|
||||
"closed": 0,
|
||||
},
|
||||
"id": RID,
|
||||
"rid": RID,
|
||||
"seeding": 1,
|
||||
},
|
||||
{
|
||||
"name": "again-hello-world",
|
||||
"description": "Rad repository for sorting",
|
||||
"defaultBranch": "master",
|
||||
"xyz.radicle.project": {
|
||||
"name": "again-hello-world",
|
||||
"description": "Rad repository for sorting",
|
||||
"defaultBranch": "master",
|
||||
},
|
||||
"delegates": [
|
||||
{
|
||||
"id": DID,
|
||||
@@ -859,7 +861,7 @@ mod routes {
|
||||
"open": 0,
|
||||
"closed": 0,
|
||||
},
|
||||
"id": "rad:z4GypKmh1gkEfmkXtarcYnkvtFUfE",
|
||||
"rid": "rad:z4GypKmh1gkEfmkXtarcYnkvtFUfE",
|
||||
"seeding": 1,
|
||||
},
|
||||
])
|
||||
@@ -867,18 +869,20 @@ mod routes {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_projects() {
|
||||
async fn test_repos() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(seed(tmp.path()));
|
||||
let response = get(&app, format!("/projects/{RID}")).await;
|
||||
let response = get(&app, format!("/repos/{RID}")).await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
response.json().await,
|
||||
json!({
|
||||
"name": "hello-world",
|
||||
"description": "Rad repository for tests",
|
||||
"defaultBranch": "master",
|
||||
"xyz.radicle.project": {
|
||||
"defaultBranch": "master",
|
||||
"description": "Rad repository for tests",
|
||||
"name": "hello-world",
|
||||
},
|
||||
"delegates": [
|
||||
{
|
||||
"id": DID,
|
||||
@@ -900,27 +904,29 @@ mod routes {
|
||||
"open": 1,
|
||||
"closed": 0,
|
||||
},
|
||||
"id": RID,
|
||||
"rid": RID,
|
||||
"seeding": 1,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_search_projects() {
|
||||
async fn test_search_repos() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(seed(tmp.path()));
|
||||
let response = get(&app, "/projects/search?q=hello").await;
|
||||
let response = get(&app, "/repos/search?q=hello").await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
response.json().await,
|
||||
json!([
|
||||
{
|
||||
"xyz.radicle.project": {
|
||||
"name": "hello-world",
|
||||
"description": "Rad repository for tests",
|
||||
"defaultBranch": "master",
|
||||
},
|
||||
"rid": "rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp",
|
||||
"name": "hello-world",
|
||||
"description": "Rad repository for tests",
|
||||
"defaultBranch": "master",
|
||||
"delegates": [
|
||||
{
|
||||
"id": DID,
|
||||
@@ -930,10 +936,12 @@ mod routes {
|
||||
"seeds": 1,
|
||||
},
|
||||
{
|
||||
"xyz.radicle.project": {
|
||||
"name": "again-hello-world",
|
||||
"description": "Rad repository for sorting",
|
||||
"defaultBranch": "master",
|
||||
},
|
||||
"rid": "rad:z4GypKmh1gkEfmkXtarcYnkvtFUfE",
|
||||
"name": "again-hello-world",
|
||||
"description": "Rad repository for sorting",
|
||||
"defaultBranch": "master",
|
||||
"delegates": [
|
||||
{
|
||||
"id": DID,
|
||||
@@ -947,10 +955,10 @@ mod routes {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_search_projects_pagination() {
|
||||
async fn test_search_repos_pagination() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(seed(tmp.path()));
|
||||
let response = get(&app, "/projects/search?q=hello&perPage=1").await;
|
||||
let response = get(&app, "/repos/search?q=hello&perPage=1").await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
@@ -958,9 +966,11 @@ mod routes {
|
||||
json!([
|
||||
{
|
||||
"rid": "rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp",
|
||||
"name": "hello-world",
|
||||
"description": "Rad repository for tests",
|
||||
"defaultBranch": "master",
|
||||
"xyz.radicle.project": {
|
||||
"defaultBranch": "master",
|
||||
"description": "Rad repository for tests",
|
||||
"name": "hello-world",
|
||||
},
|
||||
"delegates": [
|
||||
{
|
||||
"id": DID,
|
||||
@@ -974,19 +984,19 @@ mod routes {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_projects_not_found() {
|
||||
async fn test_repos_not_found() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(seed(tmp.path()));
|
||||
let response = get(&app, "/projects/rad:z2u2CP3ZJzB7ZqE8jHrau19yjcfCQ").await;
|
||||
let response = get(&app, "/repos/rad:z2u2CP3ZJzB7ZqE8jHrau19yjcfCQ").await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_projects_commits_root() {
|
||||
async fn test_repos_commits_root() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(seed(tmp.path()));
|
||||
let response = get(&app, format!("/projects/{RID}/commits")).await;
|
||||
let response = get(&app, format!("/repos/{RID}/commits")).await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
@@ -1046,10 +1056,10 @@ mod routes {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_projects_commits() {
|
||||
async fn test_repos_commits() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(seed(tmp.path()));
|
||||
let response = get(&app, format!("/projects/{RID}/commits/{HEAD}")).await;
|
||||
let response = get(&app, format!("/repos/{RID}/commits/{HEAD}")).await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
@@ -1214,12 +1224,12 @@ mod routes {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_projects_commits_not_found() {
|
||||
async fn test_repos_commits_not_found() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(seed(tmp.path()));
|
||||
let response = get(
|
||||
&app,
|
||||
format!("/projects/{RID}/commits/ffffffffffffffffffffffffffffffffffffffff"),
|
||||
format!("/repos/{RID}/commits/ffffffffffffffffffffffffffffffffffffffff"),
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -1227,10 +1237,10 @@ mod routes {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_projects_stats() {
|
||||
async fn test_repos_stats() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(seed(tmp.path()));
|
||||
let response = get(&app, format!("/projects/{RID}/stats/tree/{HEAD}")).await;
|
||||
let response = get(&app, format!("/repos/{RID}/stats/tree/{HEAD}")).await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
@@ -1246,10 +1256,10 @@ mod routes {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_projects_tree() {
|
||||
async fn test_repos_tree() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(seed(tmp.path()));
|
||||
let response = get(&app, format!("/projects/{RID}/tree/{HEAD}/")).await;
|
||||
let response = get(&app, format!("/repos/{RID}/tree/{HEAD}/")).await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
@@ -1292,7 +1302,7 @@ mod routes {
|
||||
)
|
||||
);
|
||||
|
||||
let response = get(&app, format!("/projects/{RID}/tree/{HEAD}/dir1")).await;
|
||||
let response = get(&app, format!("/repos/{RID}/tree/{HEAD}/dir1")).await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
@@ -1330,25 +1340,25 @@ mod routes {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_projects_tree_not_found() {
|
||||
async fn test_repos_tree_not_found() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(seed(tmp.path()));
|
||||
let response = get(
|
||||
&app,
|
||||
format!("/projects/{RID}/tree/ffffffffffffffffffffffffffffffffffffffff"),
|
||||
format!("/repos/{RID}/tree/ffffffffffffffffffffffffffffffffffffffff"),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
||||
|
||||
let response = get(&app, format!("/projects/{RID}/tree/{HEAD}/unknown")).await;
|
||||
let response = get(&app, format!("/repos/{RID}/tree/{HEAD}/unknown")).await;
|
||||
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_projects_remotes_root() {
|
||||
async fn test_repos_remotes_root() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(seed(tmp.path()));
|
||||
let response = get(&app, format!("/projects/{RID}/remotes")).await;
|
||||
let response = get(&app, format!("/repos/{RID}/remotes")).await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
@@ -1367,12 +1377,12 @@ mod routes {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_projects_remotes() {
|
||||
async fn test_repos_remotes() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(seed(tmp.path()));
|
||||
let response = get(
|
||||
&app,
|
||||
format!("/projects/{RID}/remotes/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi"),
|
||||
format!("/repos/{RID}/remotes/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi"),
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -1390,12 +1400,12 @@ mod routes {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_projects_remotes_not_found() {
|
||||
async fn test_repos_remotes_not_found() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(seed(tmp.path()));
|
||||
let response = get(
|
||||
&app,
|
||||
format!("/projects/{RID}/remotes/z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT"),
|
||||
format!("/repos/{RID}/remotes/z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT"),
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -1403,10 +1413,10 @@ mod routes {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_projects_blob() {
|
||||
async fn test_repos_blob() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(seed(tmp.path()));
|
||||
let response = get(&app, format!("/projects/{RID}/blob/{HEAD}/README")).await;
|
||||
let response = get(&app, format!("/repos/{RID}/blob/{HEAD}/README")).await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
@@ -1438,19 +1448,19 @@ mod routes {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_projects_blob_not_found() {
|
||||
async fn test_repos_blob_not_found() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(seed(tmp.path()));
|
||||
let response = get(&app, format!("/projects/{RID}/blob/{HEAD}/unknown")).await;
|
||||
let response = get(&app, format!("/repos/{RID}/blob/{HEAD}/unknown")).await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_projects_readme() {
|
||||
async fn test_repos_readme() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(seed(tmp.path()));
|
||||
let response = get(&app, format!("/projects/{RID}/readme/{INITIAL_COMMIT}")).await;
|
||||
let response = get(&app, format!("/repos/{RID}/readme/{INITIAL_COMMIT}")).await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
@@ -1480,14 +1490,10 @@ mod routes {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_projects_diff() {
|
||||
async fn test_repos_diff() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(seed(tmp.path()));
|
||||
let response = get(
|
||||
&app,
|
||||
format!("/projects/{RID}/diff/{INITIAL_COMMIT}/{HEAD}"),
|
||||
)
|
||||
.await;
|
||||
let response = get(&app, format!("/repos/{RID}/diff/{INITIAL_COMMIT}/{HEAD}")).await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
@@ -1586,10 +1592,10 @@ mod routes {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_projects_issues_root() {
|
||||
async fn test_repos_issues_root() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(seed(tmp.path()));
|
||||
let response = get(&app, format!("/projects/{RID}/issues")).await;
|
||||
let response = get(&app, format!("/repos/{RID}/issues")).await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
@@ -1639,10 +1645,10 @@ mod routes {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_projects_issue() {
|
||||
async fn test_repos_issue() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(seed(tmp.path()));
|
||||
let response = get(&app, format!("/projects/{RID}/issues/{ISSUE_ID}")).await;
|
||||
let response = get(&app, format!("/repos/{RID}/issues/{ISSUE_ID}")).await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
@@ -1690,10 +1696,10 @@ mod routes {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_projects_patches_root() {
|
||||
async fn test_repos_patches_root() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(seed(tmp.path()));
|
||||
let response = get(&app, format!("/projects/{RID}/patches")).await;
|
||||
let response = get(&app, format!("/repos/{RID}/patches")).await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
@@ -1750,10 +1756,10 @@ mod routes {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_projects_patch() {
|
||||
async fn test_repos_patch() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(seed(tmp.path()));
|
||||
let response = get(&app, format!("/projects/{RID}/patches/{PATCH_ID}")).await;
|
||||
let response = get(&app, format!("/repos/{RID}/patches/{PATCH_ID}")).await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
@@ -1807,7 +1813,7 @@ mod routes {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_projects_private() {
|
||||
async fn test_repos_private() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let ctx = seed(tmp.path());
|
||||
let app = super::router(ctx.to_owned());
|
||||
@@ -1818,19 +1824,19 @@ mod routes {
|
||||
.repository(RID_PRIVATE.parse().unwrap())
|
||||
.unwrap();
|
||||
|
||||
let response = get(&app, format!("/projects/{RID_PRIVATE}")).await;
|
||||
let response = get(&app, format!("/repos/{RID_PRIVATE}")).await;
|
||||
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
||||
|
||||
let response = get(&app, format!("/projects/{RID_PRIVATE}/patches")).await;
|
||||
let response = get(&app, format!("/repos/{RID_PRIVATE}/patches")).await;
|
||||
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
||||
|
||||
let response = get(&app, format!("/projects/{RID_PRIVATE}/issues")).await;
|
||||
let response = get(&app, format!("/repos/{RID_PRIVATE}/issues")).await;
|
||||
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
||||
|
||||
let response = get(&app, format!("/projects/{RID_PRIVATE}/commits")).await;
|
||||
let response = get(&app, format!("/repos/{RID_PRIVATE}/commits")).await;
|
||||
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
||||
|
||||
let response = get(&app, format!("/projects/{RID_PRIVATE}/remotes")).await;
|
||||
let response = get(&app, format!("/repos/{RID_PRIVATE}/remotes")).await;
|
||||
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,13 +24,13 @@ use crate::error::GitError as Error;
|
||||
|
||||
pub fn router(profile: Arc<Profile>, aliases: HashMap<String, RepoId>) -> Router {
|
||||
Router::new()
|
||||
.route("/:project/*request", any(git_handler))
|
||||
.route("/:rid/*request", any(git_handler))
|
||||
.with_state((profile, aliases))
|
||||
}
|
||||
|
||||
async fn git_handler(
|
||||
State((profile, aliases)): State<(Arc<Profile>, HashMap<String, RepoId>)>,
|
||||
AxumPath((project, request)): AxumPath<(String, String)>,
|
||||
AxumPath((repository, request)): AxumPath<(String, String)>,
|
||||
method: Method,
|
||||
headers: HeaderMap,
|
||||
ConnectInfo(remote): ConnectInfo<SocketAddr>,
|
||||
@@ -38,7 +38,7 @@ async fn git_handler(
|
||||
body: Bytes,
|
||||
) -> impl IntoResponse {
|
||||
let query = query.0.unwrap_or_default();
|
||||
let name = project.strip_suffix(".git").unwrap_or(&project);
|
||||
let name = repository.strip_suffix(".git").unwrap_or(&repository);
|
||||
let rid: RepoId = match name.parse() {
|
||||
Ok(rid) => rid,
|
||||
Err(_) => {
|
||||
|
||||
@@ -150,7 +150,7 @@ async fn root_index_handler() -> impl IntoResponse {
|
||||
"type": "GET"
|
||||
},
|
||||
{
|
||||
"href": "/:project/*request",
|
||||
"href": "/:rid/*request",
|
||||
"rel": "git",
|
||||
"type": "GET"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user