mirror of https://github.com/realaravinth/gitpad
feat: REST endpoint to post comment on gist
SUMMARY Comment on gist and utilities to generate post comment REST endpoint route from username and gist public ID component ERRORS - Gist doesn't exist, 404 GistNotFound is returned - Gist is private and commenting user is not ower, 404 GistNotFound is returned - Comment is empty, 400 EmptyComment is returnedmaster
parent
91be25d9f6
commit
b58a2fcee0
|
@ -20,7 +20,7 @@ use actix_web::*;
|
||||||
use db_core::prelude::*;
|
use db_core::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::routes::GetFilePath;
|
use super::routes::{GetFilePath, PostCommentPath};
|
||||||
use crate::data::api::v1::gists::{CreateGist, FileInfo, GistID};
|
use crate::data::api::v1::gists::{CreateGist, FileInfo, GistID};
|
||||||
use crate::errors::*;
|
use crate::errors::*;
|
||||||
use crate::utils::escape_spaces;
|
use crate::utils::escape_spaces;
|
||||||
|
@ -47,6 +47,7 @@ impl CreateGistRequest {
|
||||||
pub fn services(cfg: &mut web::ServiceConfig) {
|
pub fn services(cfg: &mut web::ServiceConfig) {
|
||||||
cfg.service(new);
|
cfg.service(new);
|
||||||
cfg.service(get_file);
|
cfg.service(get_file);
|
||||||
|
cfg.service(post_comment);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[my_codegen::post(
|
#[my_codegen::post(
|
||||||
|
@ -111,6 +112,42 @@ async fn get_file(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct PostCommentRequest {
|
||||||
|
pub comment: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[my_codegen::post(
|
||||||
|
path = "crate::V1_API_ROUTES.gist.post_comment",
|
||||||
|
wrap = "super::get_auth_middleware()"
|
||||||
|
)]
|
||||||
|
async fn post_comment(
|
||||||
|
payload: web::Json<PostCommentRequest>,
|
||||||
|
path: web::Path<PostCommentPath>,
|
||||||
|
id: Identity,
|
||||||
|
db: crate::DB,
|
||||||
|
) -> ServiceResult<impl Responder> {
|
||||||
|
let comment = payload.comment.trim();
|
||||||
|
if comment.is_empty() {
|
||||||
|
return Err(ServiceError::EmptyComment);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 msg = CreateGistComment {
|
||||||
|
owner: &username,
|
||||||
|
gist_public_id: &path.gist,
|
||||||
|
comment: &payload.comment,
|
||||||
|
};
|
||||||
|
|
||||||
|
db.new_comment(&msg).await?; // TODO get comment ID
|
||||||
|
Ok(HttpResponse::Ok().finish())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -300,14 +337,14 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
assert_eq!(create_gist_resp.status(), StatusCode::TEMPORARY_REDIRECT);
|
assert_eq!(create_gist_resp.status(), StatusCode::TEMPORARY_REDIRECT);
|
||||||
|
|
||||||
let unlisted = create_gist_resp
|
let private = create_gist_resp
|
||||||
.headers()
|
.headers()
|
||||||
.get(http::header::LOCATION)
|
.get(http::header::LOCATION)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_str()
|
.to_str()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
get_file_path.gist = unlisted.into();
|
get_file_path.gist = private.into();
|
||||||
for file in one_file.iter() {
|
for file in one_file.iter() {
|
||||||
get_file_path.file = file.filename.clone();
|
get_file_path.file = file.filename.clone();
|
||||||
let path = V1_API_ROUTES.gist.get_file_route(&get_file_path);
|
let path = V1_API_ROUTES.gist.get_file_route(&get_file_path);
|
||||||
|
@ -335,5 +372,89 @@ mod tests {
|
||||||
let txt: ErrorToResponse = test::read_body_json(resp).await;
|
let txt: ErrorToResponse = test::read_body_json(resp).await;
|
||||||
assert_eq!(txt.error, format!("{}", ServiceError::GistNotFound));
|
assert_eq!(txt.error, format!("{}", ServiceError::GistNotFound));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println!("testing comments");
|
||||||
|
let mut create_comment = PostCommentPath {
|
||||||
|
username: NAME2.into(),
|
||||||
|
gist: gist_id.into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut comment = PostCommentRequest { comment: "".into() };
|
||||||
|
println!("empty comment");
|
||||||
|
// empty comment
|
||||||
|
data.bad_post_req_test(
|
||||||
|
&db,
|
||||||
|
NAME,
|
||||||
|
PASSWORD,
|
||||||
|
V1_API_ROUTES.gist.post_comment,
|
||||||
|
&comment,
|
||||||
|
ServiceError::EmptyComment,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
comment.comment = "foo".into();
|
||||||
|
|
||||||
|
create_comment.gist = "gistnotexist".into();
|
||||||
|
let post_comment_path = V1_API_ROUTES.gist.get_post_comment_route(&create_comment);
|
||||||
|
println!("gist not found");
|
||||||
|
data.bad_post_req_test(
|
||||||
|
&db,
|
||||||
|
NAME,
|
||||||
|
PASSWORD,
|
||||||
|
V1_API_ROUTES.gist.post_comment,
|
||||||
|
&comment,
|
||||||
|
ServiceError::GistNotFound,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
println!("comment OK");
|
||||||
|
create_comment.gist = gist_id.into();
|
||||||
|
let post_comment_path = V1_API_ROUTES.gist.get_post_comment_route(&create_comment);
|
||||||
|
let resp = test::call_service(
|
||||||
|
&app,
|
||||||
|
post_request!(&comment, &post_comment_path)
|
||||||
|
.cookie(cookies.clone())
|
||||||
|
.to_request(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
|
||||||
|
println!("comment OK");
|
||||||
|
create_comment.gist = unlisted.into();
|
||||||
|
let post_comment_path = V1_API_ROUTES.gist.get_post_comment_route(&create_comment);
|
||||||
|
let resp = test::call_service(
|
||||||
|
&app,
|
||||||
|
post_request!(&comment, &post_comment_path)
|
||||||
|
.cookie(cookies.clone())
|
||||||
|
.to_request(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
|
||||||
|
println!("comment OK");
|
||||||
|
create_comment.gist = private.into();
|
||||||
|
let post_comment_path = V1_API_ROUTES.gist.get_post_comment_route(&create_comment);
|
||||||
|
let resp = test::call_service(
|
||||||
|
&app,
|
||||||
|
post_request!(&comment, &post_comment_path)
|
||||||
|
.cookie(cookies.clone())
|
||||||
|
.to_request(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
|
||||||
|
// commenting on private gist
|
||||||
|
println!("private gist, not OK");
|
||||||
|
create_comment.gist = private.into();
|
||||||
|
let post_comment_path = V1_API_ROUTES.gist.get_post_comment_route(&create_comment);
|
||||||
|
data.bad_post_req_test(
|
||||||
|
&db,
|
||||||
|
NAME2,
|
||||||
|
PASSWORD,
|
||||||
|
&post_comment_path,
|
||||||
|
&comment,
|
||||||
|
ServiceError::GistNotFound,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,13 +52,22 @@ pub struct GetFilePath {
|
||||||
pub file: String,
|
pub file: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct PostCommentPath {
|
||||||
|
pub username: String,
|
||||||
|
pub gist: String,
|
||||||
|
}
|
||||||
|
|
||||||
/// Authentication routes
|
/// Authentication routes
|
||||||
pub struct Gist {
|
pub struct Gist {
|
||||||
/// logout route
|
/// logout route
|
||||||
pub new: &'static str,
|
pub new: &'static str,
|
||||||
|
|
||||||
/// get fie route
|
/// get flie route
|
||||||
pub get_file: &'static str,
|
pub get_file: &'static str,
|
||||||
|
|
||||||
|
/// post comment on gist
|
||||||
|
pub post_comment: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Gist {
|
impl Gist {
|
||||||
|
@ -66,7 +75,12 @@ impl Gist {
|
||||||
pub const fn new() -> Gist {
|
pub const fn new() -> Gist {
|
||||||
let new = "/api/v1/gist/new";
|
let new = "/api/v1/gist/new";
|
||||||
let get_file = "/api/v1/gist/profile/{username}/{gist}/contents/{file}";
|
let get_file = "/api/v1/gist/profile/{username}/{gist}/contents/{file}";
|
||||||
Gist { new, get_file }
|
let post_comment = "/api/v1/gist/profile/{username}/{gist}/comments";
|
||||||
|
Gist {
|
||||||
|
new,
|
||||||
|
get_file,
|
||||||
|
post_comment,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// get file routes with placeholders replaced with values provided.
|
/// get file routes with placeholders replaced with values provided.
|
||||||
|
@ -77,6 +91,13 @@ impl Gist {
|
||||||
.replace("{gist}", &components.gist)
|
.replace("{gist}", &components.gist)
|
||||||
.replace("{file}", &urlencoding::encode(&components.file))
|
.replace("{file}", &urlencoding::encode(&components.file))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// get post_comment route with placeholders replaced with values provided.
|
||||||
|
pub fn get_post_comment_route(&self, components: &PostCommentPath) -> String {
|
||||||
|
self.post_comment
|
||||||
|
.replace("{username}", &components.username)
|
||||||
|
.replace("{gist}", &components.gist)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Account management routes
|
/// Account management routes
|
||||||
|
|
|
@ -115,6 +115,9 @@ pub enum ServiceError {
|
||||||
|
|
||||||
#[display(fmt = "File System Error {}", _0)]
|
#[display(fmt = "File System Error {}", _0)]
|
||||||
FSError(FSError),
|
FSError(FSError),
|
||||||
|
|
||||||
|
#[display(fmt = "Comment is empty")]
|
||||||
|
EmptyComment,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<CredsError> for ServiceError {
|
impl From<CredsError> for ServiceError {
|
||||||
|
@ -220,6 +223,7 @@ impl ResponseError for ServiceError {
|
||||||
|
|
||||||
ServiceError::GistNotFound => 404,
|
ServiceError::GistNotFound => 404,
|
||||||
ServiceError::CommentNotFound => 404,
|
ServiceError::CommentNotFound => 404,
|
||||||
|
ServiceError::EmptyComment => 400,
|
||||||
};
|
};
|
||||||
|
|
||||||
StatusCode::from_u16(status_code).unwrap()
|
StatusCode::from_u16(status_code).unwrap()
|
||||||
|
|
Loading…
Reference in New Issue