feat: REST endpoint to get all comments on a gist

DESCRIPTION
    Get all comments on a gist. Access controlled but optionally
    authenticated HTTP REST endpoint is added to get comment by ID.

ERRORS RETURNED
    - Gist doesn't exist: 404 GistNotFound
    - Gist is private and requesting user is not owner or is not
      authenticated: 404 GistNotFound
master
Aravinth Manivannan 2 years ago
parent f8b9172b88
commit 043211a357
Signed by: realaravinth
GPG Key ID: AD9F0F08E855ED88

@ -49,6 +49,7 @@ pub fn services(cfg: &mut web::ServiceConfig) {
cfg.service(get_file);
cfg.service(post_comment);
cfg.service(get_comment);
cfg.service(get_gist_comments);
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@ -186,6 +187,31 @@ async fn get_comment(
}
}
#[my_codegen::get(path = "crate::V1_API_ROUTES.gist.get_gist_comments")]
async fn get_gist_comments(
path: web::Path<PostCommentPath>,
id: Identity,
db: crate::DB,
) -> ServiceResult<impl Responder> {
let gist = db.get_gist(&path.gist).await?;
match gist.visibility {
GistVisibility::Public | GistVisibility::Unlisted => {
let comments = db.get_comments_on_gist(&path.gist).await?;
Ok(HttpResponse::Ok().json(comments))
}
GistVisibility::Private => {
if let Some(username) = id.identity() {
if gist.owner == username {
let comments = db.get_comments_on_gist(&path.gist).await?;
return Ok(HttpResponse::Ok().json(comments));
}
};
Err(ServiceError::GistNotFound)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
@ -587,5 +613,76 @@ mod tests {
assert_eq!(comment.comment, comment_payload.comment);
}
}
/*
* +++++++++++++++++++++++++++++++++++
* GET GIST COMMENT
* +++++++++++++++++++++++++++++++++++
*/
// gist not found
let mut get_gist_comments_component = PostCommentPath {
gist: "gistdoesntexist".into(),
username: NAME.into(),
};
let get_comment_path = V1_API_ROUTES
.gist
.get_gist_comments(&get_gist_comments_component);
println!("getting comments; gist doesn't exist");
let resp = get_request!(&app, &get_comment_path);
assert_eq!(resp.status(), ServiceError::GistNotFound.status_code());
let resp_err: ErrorToResponse = test::read_body_json(resp).await;
assert_eq!(resp_err.error, format!("{}", ServiceError::GistNotFound));
// private gist
get_gist_comments_component.gist = private.clone();
let get_comment_path = V1_API_ROUTES
.gist
.get_gist_comments(&get_gist_comments_component);
println!("getting comments; private gist");
let resp = get_request!(&app, &get_comment_path);
assert_eq!(resp.status(), ServiceError::GistNotFound.status_code());
let resp_err: ErrorToResponse = test::read_body_json(resp).await;
assert_eq!(resp_err.error, format!("{}", ServiceError::GistNotFound));
for (_comment_id, comment_payload, gist, visibility) in comment_ids.iter() {
let component = PostCommentPath {
gist: gist.into(),
username: NAME.into(),
};
if visibility == &GistVisibility::Private {
println!("getting comments; private gist but user==owner");
let path = V1_API_ROUTES.gist.get_gist_comments(&component);
let resp = get_request!(&app, &path, cookies.clone());
assert_eq!(resp.status(), StatusCode::OK);
let mut comment: Vec<GistComment> = test::read_body_json(resp).await;
assert_eq!(
comment.pop().as_ref().unwrap().comment,
comment_payload.comment
);
println!("getting comments; private gist but user is unauthenticated");
let resp = get_request!(&app, &path);
assert_eq!(resp.status(), ServiceError::GistNotFound.status_code());
let resp_err: ErrorToResponse = test::read_body_json(resp).await;
assert_eq!(resp_err.error, format!("{}", ServiceError::GistNotFound));
println!("getting comments; private gist but user != owner");
let resp = get_request!(&app, &path, cookies2.clone());
assert_eq!(resp.status(), ServiceError::GistNotFound.status_code());
let err: ErrorToResponse = test::read_body_json(resp).await;
assert_eq!(err.error, format!("{}", ServiceError::GistNotFound));
} else {
let path = V1_API_ROUTES.gist.get_gist_comments(&component);
let resp = get_request!(&app, &path, cookies.clone());
assert_eq!(resp.status(), StatusCode::OK);
let mut comment: Vec<GistComment> = test::read_body_json(resp).await;
assert_eq!(
comment.pop().as_ref().unwrap().comment,
comment_payload.comment
);
}
}
}
}

@ -75,6 +75,8 @@ pub struct Gist {
pub post_comment: &'static str,
/// get comment
pub get_comment: &'static str,
/// get gist comments
pub get_gist_comments: &'static str,
}
impl Gist {
@ -84,11 +86,13 @@ impl 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}";
let get_gist_comments = post_comment;
Gist {
new,
get_file,
post_comment,
get_comment,
get_gist_comments,
}
}
@ -108,6 +112,11 @@ impl Gist {
.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)
}
/// get post_comment route with placeholders replaced with values provided.
pub fn get_get_comment_route(&self, components: &GetCommentPath) -> String {
self.get_comment
@ -214,6 +223,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 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 get_file_component = GetFilePath {
@ -241,5 +251,14 @@ mod tests {
get_comment,
ROUTES.gist.get_get_comment_route(&get_comment_path)
);
let get_gist_comments_path = PostCommentPath {
gist: GIST.into(),
username: NAME.into(),
};
assert_eq!(
get_gist_comments,
ROUTES.gist.get_post_comment_route(&get_gist_comments_path)
);
}
}

Loading…
Cancel
Save