feat: gist index view

master
Aravinth Manivannan 2022-02-28 12:18:49 +05:30
parent edca78906e
commit 2db966c04a
Signed by: realaravinth
GPG Key ID: AD9F0F08E855ED88
3 changed files with 102 additions and 8 deletions

View File

@ -51,6 +51,7 @@ pub fn services(cfg: &mut web::ServiceConfig) {
cfg.service(get_comment);
cfg.service(get_gist_comments);
cfg.service(delete_comment);
cfg.service(index);
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@ -244,10 +245,33 @@ async fn delete_comment(
}
}
#[my_codegen::get(
path = "crate::V1_API_ROUTES.gist.gist_index",
wrap = "super::get_auth_middleware()"
)]
async fn index(
path: web::Path<PostCommentPath>,
id: Identity,
db: crate::DB,
data: AppData,
) -> ServiceResult<impl Responder> {
let username = id.identity().unwrap();
let gist = db.get_gist(&path.gist).await?;
if gist.visibility == GistVisibility::Private && username != gist.owner {
return Err(ServiceError::GistNotFound);
}
let resp = data
.gist_preview(db.as_ref(), &mut GistID::ID(&path.gist))
.await?;
Ok(HttpResponse::Ok().json(resp))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::data::api::v1::gists::{ContentType, FileType};
use crate::data::api::v1::gists::{ContentType, FileType, GistInfo};
use crate::tests::*;
use actix_web::ResponseError;
@ -359,11 +383,10 @@ mod tests {
assert_eq!(&content, &req_escaped_file);
}
// 2. Unlisted gists
let one_file = [files[0].clone()];
let mut msg = CreateGistRequest {
description: None,
visibility: GistVisibility::Unlisted,
files: one_file.to_vec(),
files: files.to_vec(),
};
let create_gist_resp = test::call_service(
@ -378,7 +401,7 @@ mod tests {
let unlisted = unlisted.id;
get_file_path.gist = unlisted.clone();
for file in one_file.iter() {
for file in files.iter() {
// requesting user is owner
get_file_path.file = file.filename.clone();
let path = V1_API_ROUTES.gist.get_file_route(&get_file_path);
@ -428,7 +451,7 @@ mod tests {
let private = private.id;
get_file_path.gist = private.clone();
for file in one_file.iter() {
for file in files.iter() {
get_file_path.file = file.filename.clone();
let path = V1_API_ROUTES.gist.get_file_route(&get_file_path);
println!("Trying to get file {path}");
@ -783,5 +806,59 @@ mod tests {
println!("delete comments; authenticated comment_owner == owner");
let resp = delete_request!(&app, &del_comment_path, cookies.clone());
assert_eq!(resp.status(), StatusCode::OK);
/*
*
* ============================================
* Gist index
* ============================================
*
*/
let mut gist_index = PostCommentPath {
username: NAME.into(),
gist: "non-existant".into(),
};
// unauthenticated request
let path = V1_API_ROUTES.gist.get_gist_index(&gist_index);
let resp = get_request!(&app, &path);
assert_eq!(resp.status(), StatusCode::FOUND);
// non-existant gist
let path = V1_API_ROUTES.gist.get_gist_index(&gist_index);
let resp = get_request!(&app, &path, cookies.clone());
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
let err: ErrorToResponse = test::read_body_json(resp).await;
assert_eq!(err.error, format!("{}", ServiceError::GistNotFound));
// get private gist with user that doesn't have access
gist_index.gist = private.clone();
let path = V1_API_ROUTES.gist.get_gist_index(&gist_index);
let resp = get_request!(&app, &path, cookies2.clone());
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
let err: ErrorToResponse = test::read_body_json(resp).await;
assert_eq!(err.error, format!("{}", ServiceError::GistNotFound));
// get private gist with user=owner
gist_index.gist = private.clone();
let path = V1_API_ROUTES.gist.get_gist_index(&gist_index);
let resp = get_request!(&app, &path, cookies.clone());
assert_eq!(resp.status(), StatusCode::OK);
let preview: GistInfo = test::read_body_json(resp).await;
assert_eq!(preview.owner, NAME);
assert_eq!(preview.files.len(), files.len());
for file in preview.files.iter() {
let processed: Vec<FileInfo> = files
.iter()
.map(|f| FileInfo {
filename: escape_spaces(&f.filename),
content: f.content.clone(),
})
.collect();
assert!(processed
.iter()
.any(|f| f.filename == file.filename && f.content == file.content));
}
}
}

View File

@ -79,12 +79,15 @@ pub struct Gist {
pub get_gist_comments: &'static str,
/// delete comment
pub delete_comment: &'static str,
/// gist index page
pub gist_index: &'static str,
}
impl Gist {
/// create new instance of Authentication route
pub const fn new() -> Gist {
let new = "/api/v1/gist/new";
let gist_index = "/api/v1/gist/profile/{username}/{gist}";
let get_file = "/api/v1/gist/profile/{username}/{gist}/contents/{file}";
let post_comment = "/api/v1/gist/profile/{username}/{gist}/comments";
let get_comment = "/api/v1/gist/profile/{username}/{gist}/comment/{comment_id}";
@ -97,6 +100,7 @@ impl Gist {
get_comment,
get_gist_comments,
delete_comment,
gist_index,
}
}
@ -116,6 +120,13 @@ impl Gist {
.replace("{gist}", &components.gist)
}
/// get gist index route with placeholders replaced with values provided.
pub fn get_gist_index(&self, components: &PostCommentPath) -> String {
self.gist_index
.replace("{username}", &components.username)
.replace("{gist}", &components.gist)
}
/// get post_comment route with placeholders replaced with values provided.
pub fn get_gist_comments(&self, components: &PostCommentPath) -> String {
self.get_post_comment_route(components)
@ -228,6 +239,7 @@ mod tests {
const COMMENT_ID: i64 = 5;
let get_file = format!("/api/v1/gist/profile/{NAME}/{GIST}/contents/{FILE}");
let post_comment = format!("/api/v1/gist/profile/{NAME}/{GIST}/comments");
let gist_index = format!("/api/v1/gist/profile/{NAME}/{GIST}");
let get_gist_comments = format!("/api/v1/gist/profile/{NAME}/{GIST}/comments");
let get_comment = format!("/api/v1/gist/profile/{NAME}/{GIST}/comment/{COMMENT_ID}");
let delete_comment = format!("/api/v1/gist/profile/{NAME}/{GIST}/comment/{COMMENT_ID}");
@ -276,5 +288,7 @@ mod tests {
delete_comment,
ROUTES.gist.get_delete_comment_route(&delete_comment_path)
);
assert_eq!(gist_index, ROUTES.gist.get_gist_index(&post_comment_path));
}
}

View File

@ -294,7 +294,6 @@ impl Data {
let tree = repo.find_tree(id).unwrap();
let mut items = Vec::with_capacity(tree.len());
for item in tree.iter() {
println!("{:?}", item.name());
if let Some(name) = item.name() {
#[allow(clippy::needless_borrow)]
let mode: GitFileMode = (&item).into();
@ -346,6 +345,12 @@ impl Data {
}
}
/// fetches gist metadata from DB and retrieves contents of all the files stored
/// in the repository
// TODO
// Data::gist_preview uses Data::read_file under the hood, which
// currently reads subdirectories up to level 1 depth. Decision has
// to be made regarding what to do with level 2 and below subdirectories.
pub async fn gist_preview<T: GPDatabse>(
&self,
db: &T,
@ -362,7 +367,6 @@ impl Data {
let tree = head.peel_to_tree().unwrap();
let mut files = Vec::with_capacity(5);
for item in tree.iter() {
println!("name from gist_preview: {:?}:", item.name());
if let Some(name) = item.name() {
let file = data.read_file(db, gist_id, name).await?;
files.push(file);
@ -505,7 +509,6 @@ pub mod tests {
.await
.unwrap();
assert_eq!(preview.owner, NAME);
println!("preview {:#?}", preview);
assert_eq!(preview.files.len(), 4);
for file in preview.files.iter() {
if file.filename == escape_spaces(&files2[0].filename) {