diff --git a/src/api/v1/gists.rs b/src/api/v1/gists.rs index fbe01cc..c591b6b 100644 --- a/src/api/v1/gists.rs +++ b/src/api/v1/gists.rs @@ -50,6 +50,7 @@ pub fn services(cfg: &mut web::ServiceConfig) { cfg.service(post_comment); cfg.service(get_comment); cfg.service(get_gist_comments); + cfg.service(delete_comment); } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -212,6 +213,33 @@ async fn get_gist_comments( } } +#[my_codegen::delete( + path = "crate::V1_API_ROUTES.gist.delete_comment", + wrap = "super::get_auth_middleware()" +)] +async fn delete_comment( + path: web::Path, + id: Identity, + db: crate::DB, +) -> ServiceResult { + let gist = db.get_gist(&path.gist).await?; + let comment = db.get_comment_by_id(path.comment_id).await?; + let username = id.identity().unwrap(); + if username != comment.owner { + match gist.visibility { + GistVisibility::Public | GistVisibility::Unlisted => { + Err(ServiceError::UnauthorizedOperation( + "This user is not the owner of the comment to delete it".into(), + )) + } + GistVisibility::Private => Err(ServiceError::GistNotFound), + } + } else { + db.delete_comment(&username, comment.id).await?; + Ok(HttpResponse::Ok()) + } +} + #[cfg(test)] mod tests { use super::*; @@ -684,5 +712,72 @@ mod tests { ); } } + + /* + * +++++++++++++++++++++++++++++++++++ + * DELETE COMMENT + * +++++++++++++++++++++++++++++++++++ + */ + + let mut delete_comment_component = GetCommentPath { + gist: "gistdoesntexist".into(), + username: NAME.into(), + comment_id: 34234234, + }; + + println!("delete comments; unauthenticated, gist does't exist"); + let del_comment_path = V1_API_ROUTES + .gist + .get_delete_comment_route(&delete_comment_component); + let resp = delete_request!(&app, &del_comment_path); + assert_eq!(resp.status(), StatusCode::FOUND); + + println!("delete comments; authenticated, gist does't exist"); + let resp = delete_request!(&app, &del_comment_path, cookies.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)); + + println!("delete comments; authenticated, comment doesn't exist"); + delete_comment_component.gist = gist_id.clone(); + let del_comment_path = V1_API_ROUTES + .gist + .get_delete_comment_route(&delete_comment_component); + let resp = delete_request!(&app, &del_comment_path, cookies.clone()); + assert_eq!(resp.status(), ServiceError::CommentNotFound.status_code()); + let err: ErrorToResponse = test::read_body_json(resp).await; + assert_eq!(err.error, format!("{}", ServiceError::CommentNotFound)); + + println!("delete comments; authenticated but comment_owner != user and gist is public"); + delete_comment_component.gist = gist_id.clone(); + delete_comment_component.comment_id = comment_ids.get(0).as_ref().unwrap().0.id; + let del_comment_path = V1_API_ROUTES + .gist + .get_delete_comment_route(&delete_comment_component); + let resp = delete_request!(&app, &del_comment_path, cookies2.clone()); + assert_eq!( + resp.status(), + ServiceError::UnauthorizedOperation("".into()).status_code() + ); + let err: ErrorToResponse = test::read_body_json(resp).await; + assert!(err.error.contains(&format!( + "{}", + ServiceError::UnauthorizedOperation("".into()) + ))); + + println!("delete comments; authenticated but comment_owner != user and gist is private"); + delete_comment_component.gist = private.clone(); + delete_comment_component.comment_id = comment_ids.last().as_ref().unwrap().0.id; + let del_comment_path = V1_API_ROUTES + .gist + .get_delete_comment_route(&delete_comment_component); + let resp = delete_request!(&app, &del_comment_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)); + + println!("delete comments; authenticated comment_owner == owner"); + let resp = delete_request!(&app, &del_comment_path, cookies.clone()); + assert_eq!(resp.status(), StatusCode::OK); } } diff --git a/src/api/v1/routes.rs b/src/api/v1/routes.rs index 9282ea9..bbcccd4 100644 --- a/src/api/v1/routes.rs +++ b/src/api/v1/routes.rs @@ -77,6 +77,8 @@ pub struct Gist { pub get_comment: &'static str, /// get gist comments pub get_gist_comments: &'static str, + /// delete comment + pub delete_comment: &'static str, } impl Gist { @@ -86,6 +88,7 @@ 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 delete_comment = get_comment; let get_gist_comments = post_comment; Gist { new, @@ -93,6 +96,7 @@ impl Gist { post_comment, get_comment, get_gist_comments, + delete_comment, } } @@ -117,13 +121,18 @@ impl Gist { self.get_post_comment_route(components) } - /// get post_comment route with placeholders replaced with values provided. + /// get get_comment route with placeholders replaced with values provided. pub fn get_get_comment_route(&self, components: &GetCommentPath) -> String { self.get_comment .replace("{username}", &components.username) .replace("{gist}", &components.gist) .replace("{comment_id}", &components.comment_id.to_string()) } + + /// get delete_comment route with placeholders replaced with values provided. + pub fn get_delete_comment_route(&self, components: &GetCommentPath) -> String { + self.get_get_comment_route(components) + } } /// Account management routes @@ -225,6 +234,7 @@ mod tests { 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 delete_comment = format!("/api/v1/gist/profile/{NAME}/{GIST}/comment/{COMMENT_ID}"); let get_file_component = GetFilePath { file: FILE.into(), @@ -260,5 +270,15 @@ mod tests { get_gist_comments, ROUTES.gist.get_post_comment_route(&get_gist_comments_path) ); + + let delete_comment_path = GetCommentPath { + gist: GIST.into(), + username: NAME.into(), + comment_id: COMMENT_ID, + }; + assert_eq!( + delete_comment, + ROUTES.gist.get_delete_comment_route(&delete_comment_path) + ); } }