Skip to content
This repository has been archived by the owner on Apr 29, 2024. It is now read-only.

Commit

Permalink
httpd: Add routes for handling embeds in issues
Browse files Browse the repository at this point in the history
Signed-off-by: Sebastian Martinez <[email protected]>
  • Loading branch information
sebastinez authored and cloudhead committed Sep 19, 2023
1 parent a3f460e commit 9df1922
Show file tree
Hide file tree
Showing 8 changed files with 148 additions and 31 deletions.
7 changes: 4 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion radicle-cob/src/change/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ impl Embed<Vec<u8>> {
.into()
}

/// Return am embed where the content is replaced by a content hash.
/// Return an embed where the content is replaced by a content hash.
pub fn hashed<T: From<Oid>>(&self) -> Embed<T> {
Embed {
name: self.name.clone(),
Expand Down
1 change: 1 addition & 0 deletions radicle-httpd/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ anyhow = { version = "1" }
axum = { version = "0.6.7", default-features = false, features = ["headers", "json", "query", "tokio"] }
axum-auth = { version= "0.4.0", default-features = false, features = ["auth-bearer"] }
axum-server = { version = "0.5.1", default-features = false }
base64 = "0.21.3"
chrono = { version = "0.4.22", default-features = false, features = ["clock"] }
fastrand = { version = "2.0.0" }
flate2 = { version = "1" }
Expand Down
6 changes: 6 additions & 0 deletions radicle-httpd/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,12 @@ pub struct PaginationQuery {
pub per_page: Option<usize>,
}

#[derive(Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct RawQuery {
pub mime: Option<String>,
}

#[derive(Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct CobsQuery<T> {
Expand Down
4 changes: 3 additions & 1 deletion radicle-httpd/src/api/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use radicle::cob::patch::Review;
use radicle::cob::patch::{Patch, PatchId};
use radicle::cob::thread;
use radicle::cob::thread::CommentId;
use radicle::cob::{ActorId, Author, Reaction, Timestamp};
use radicle::cob::{ActorId, Author, Embed, Reaction, Timestamp, Uri};
use radicle::git::RefString;
use radicle::node::{Alias, AliasStore};
use radicle::prelude::NodeId;
Expand Down Expand Up @@ -241,6 +241,7 @@ struct Comment<'a> {
id: CommentId,
author: Value,
body: &'a str,
embeds: Vec<Embed<Uri>>,
reactions: Vec<(&'a ActorId, &'a Reaction)>,
#[serde(with = "radicle::serde_ext::localtime::time")]
timestamp: Timestamp,
Expand All @@ -254,6 +255,7 @@ impl<'a> Comment<'a> {
id: *id,
author: author(&comment_author, aliases.alias(comment_author.id())),
body: comment.body(),
embeds: comment.embeds().to_vec(),
reactions: comment.reactions().collect::<Vec<_>>(),
timestamp: comment.timestamp(),
reply_to: comment.reply_to(),
Expand Down
88 changes: 80 additions & 8 deletions radicle-httpd/src/api/v1/projects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ use axum::response::IntoResponse;
use axum::routing::{get, patch, post};
use axum::{Json, Router};
use axum_auth::AuthBearer;
use base64::prelude::{Engine, BASE64_STANDARD};
use hyper::StatusCode;
use radicle_surf::blob::{Blob, BlobRef};
use serde::{Deserialize, Serialize};
use serde_json::json;
use tower_http::set_header::SetResponseHeaderLayer;

use radicle::cob::{issue, patch, Label};
use radicle::cob::{issue, patch, Embed, Label, Uri};
use radicle::identity::{Did, Id};
use radicle::node::routing::Store;
use radicle::node::AliasStore;
Expand Down Expand Up @@ -511,6 +512,7 @@ pub struct IssueCreate {
pub description: String,
pub labels: Vec<Label>,
pub assignees: Vec<Did>,
pub embeds: Vec<Embed<Uri>>,
}

/// Create a new issue.
Expand All @@ -528,14 +530,33 @@ async fn issue_create_handler(
.signer()
.map_err(|_| Error::Auth("Unauthorized"))?;
let repo = storage.repository(project)?;
let embeds: Vec<Embed> = issue
.embeds
.into_iter()
.filter_map(|embed| {
if let Some(content) = embed
.content
.as_str()
.strip_prefix("data:content/type;base64,")
{
return BASE64_STANDARD.decode(content).ok().map(|content| Embed {
name: embed.name,
content,
});
}

None
})
.collect();

let mut issues = issue::Issues::open(&repo)?;
let issue = issues
.create(
issue.title,
issue.description,
&issue.labels,
&issue.assignees,
[],
embeds,
&signer,
)
.map_err(Error::from)?;
Expand Down Expand Up @@ -575,9 +596,30 @@ async fn issue_update_handler(
issue::Action::Edit { title } => {
issue.edit(title, &signer)?;
}
issue::Action::Comment { body, reply_to, .. } => {
issue::Action::Comment {
body,
reply_to,
embeds,
} => {
let embeds: Vec<Embed> = embeds
.into_iter()
.filter_map(|embed| {
if let Some(content) = embed
.content
.as_str()
.strip_prefix("data:content/type;base64,")
{
return BASE64_STANDARD.decode(content).ok().map(|content| Embed {
name: embed.name,
content,
});
}

None
})
.collect();
if let Some(to) = reply_to {
issue.comment(body, to, [], &signer)?;
issue.comment(body, to, embeds, &signer)?;
} else {
return Err(Error::BadRequest("`replyTo` missing".to_owned()));
}
Expand Down Expand Up @@ -1777,6 +1819,7 @@ mod routes {
"id": DID
},
"body": "Change 'hello world' to 'hello everyone'",
"embeds": [],
"reactions": [],
"timestamp": TIMESTAMP,
"replyTo": null
Expand All @@ -1790,7 +1833,7 @@ mod routes {

#[tokio::test]
async fn test_projects_issues_create() {
const CREATED_ISSUE_ID: &str = "c7cff5ab610408470406e023baa1ab087ce78adc";
const CREATED_ISSUE_ID: &str = "e712eb0b5874d5256022fb620f26caf847d96723";

let tmp = tempfile::tempdir().unwrap();
let ctx = contributor(tmp.path());
Expand All @@ -1802,6 +1845,12 @@ mod routes {
"title": "Issue #2",
"description": "Change 'hello world' to 'hello everyone'",
"labels": ["bug"],
"embeds": [
{
"name": "example.html",
"content": "data:content/type;base64,PGh0bWw+SGVsbG8gV29ybGQhPC9odG1sPg=="
}
],
"assignees": [],
}))
.unwrap();
Expand Down Expand Up @@ -1833,17 +1882,23 @@ mod routes {
"author": {
"id": CONTRIBUTOR_DID,
},
"assignees": [],
"title": "Issue #2",
"state": {
"status": "open",
},
"assignees": [],
"discussion": [{
"id": CREATED_ISSUE_ID,
"author": {
"id": CONTRIBUTOR_DID,
},
"body": "Change 'hello world' to 'hello everyone'",
"embeds": [
{
"name": "example.html",
"content": "git:b62df2ec90365e3749cd4fa431cb844492908b84"
}
],
"reactions": [],
"timestamp": TIMESTAMP,
"replyTo": null,
Expand All @@ -1866,6 +1921,12 @@ mod routes {
let body = serde_json::to_vec(&json!({
"type": "comment",
"body": "This is first-level comment",
"embeds": [
{
"name": "image.jpg",
"content": "data:content/type;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4//8/AAX+Av4N70a4AAAAAElFTkSuQmCC"
}
],
"replyTo": CONTRIBUTOR_ISSUE_ID,
}))
.unwrap();
Expand All @@ -1883,7 +1944,7 @@ mod routes {

let body = serde_json::to_vec(&json!({
"type": "comment.react",
"id": "26cadcc7cb51ee9c56b6232023e9bf63b7b0df60",
"id": "6fe9fdec2ec9f6436f2875dbcbedb95dd215b863",
"reaction": "🚀",
"active": true,
}))
Expand Down Expand Up @@ -1921,16 +1982,23 @@ mod routes {
"id": CONTRIBUTOR_DID,
},
"body": "Change 'hello world' to 'hello everyone'",
"embeds": [],
"reactions": [],
"timestamp": TIMESTAMP,
"replyTo": null,
},
{
"id": "26cadcc7cb51ee9c56b6232023e9bf63b7b0df60",
"id": "6fe9fdec2ec9f6436f2875dbcbedb95dd215b863",
"author": {
"id": CONTRIBUTOR_DID,
},
"body": "This is first-level comment",
"embeds": [
{
"name": "image.jpg",
"content": "git:94381b429d7f7fe87e1bade52d893ab348ae29cc",
},
],
"reactions": [
[
"z6Mkk7oqY4pPxhMmGEotDYsFo97vhCj85BLY1H256HrJmjN8",
Expand Down Expand Up @@ -1998,6 +2066,7 @@ mod routes {
"id": CONTRIBUTOR_DID,
},
"body": "Change 'hello world' to 'hello everyone'",
"embeds": [],
"reactions": [],
"timestamp": TIMESTAMP,
"replyTo": null,
Expand All @@ -2008,6 +2077,7 @@ mod routes {
"id": CONTRIBUTOR_DID,
},
"body": "This is a reply to the first comment",
"embeds": [],
"reactions": [],
"timestamp": TIMESTAMP,
"replyTo": ISSUE_DISCUSSION_ID,
Expand Down Expand Up @@ -2498,6 +2568,7 @@ mod routes {
"id": CONTRIBUTOR_DID,
},
"body": "EDIT: This is a root level comment",
"embeds": [],
"reactions": [["z6Mkk7oqY4pPxhMmGEotDYsFo97vhCj85BLY1H256HrJmjN8","🚀"]],
"timestamp": TIMESTAMP,
"replyTo": null,
Expand All @@ -2508,6 +2579,7 @@ mod routes {
"id": CONTRIBUTOR_DID,
},
"body": "This is a root level comment",
"embeds": [],
"reactions": [],
"timestamp": TIMESTAMP,
"replyTo": CONTRIBUTOR_COMMENT_1,
Expand Down
12 changes: 12 additions & 0 deletions radicle-httpd/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,18 @@ pub enum RawError {
#[error(transparent)]
Surf(#[from] radicle_surf::Error),

/// Git error.
#[error(transparent)]
Git(#[from] radicle::git::ext::Error),

/// Radicle Storage error.
#[error(transparent)]
Storage(#[from] radicle::storage::Error),

/// Http Headers error.
#[error(transparent)]
Headers(#[from] http::header::InvalidHeaderValue),

/// Surf file error.
#[error(transparent)]
SurfFile(#[from] radicle_surf::fs::error::File),
Expand Down
Loading

0 comments on commit 9df1922

Please sign in to comment.