Skip to content

Commit

Permalink
comments impl (#2)
Browse files Browse the repository at this point in the history
* comments impl

* optionals removed

* dionis gigachad

* limit up
  • Loading branch information
tikitko authored Jul 9, 2023
1 parent 67c2b75 commit 9fbbbf0
Show file tree
Hide file tree
Showing 18 changed files with 375 additions and 8 deletions.
3 changes: 1 addition & 2 deletions blog-server-api/src/endpoints/authors/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use blog_server_services::traits::author_service::AuthorService;
use screw_api::request::ApiRequest;
use screw_api::response::ApiResponse;
use std::sync::Arc;
use tokio::join;

async fn handler(
offset: Option<i64>,
Expand All @@ -17,7 +16,7 @@ async fn handler(
let offset = offset.unwrap_or(0).max(0);
let limit = limit.unwrap_or(50).max(0).min(50);

let (authors_result, total_result) = join!(
let (authors_result, total_result) = tokio::join!(
author_service.authors(&offset, &limit),
author_service.authors_count(),
);
Expand Down
75 changes: 75 additions & 0 deletions blog-server-api/src/endpoints/comments/handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use super::request_content::CommentsRequestContent;
use super::response_content_failure::CommentsResponseContentFailure;
use super::response_content_failure::CommentsResponseContentFailure::*;
use super::response_content_success::CommentsResponseContentSuccess;
use crate::extensions::Resolve;
use blog_server_services::traits::comment_service::CommentService;
use blog_server_services::traits::post_service::PostService;
use screw_api::request::ApiRequest;
use screw_api::response::ApiResponse;
use std::sync::Arc;

async fn handler(
post_slug: String,
offset: Option<i64>,
limit: Option<i64>,
comment_service: Arc<Box<dyn CommentService>>,
post_service: Arc<Box<dyn PostService>>,
) -> Result<CommentsResponseContentSuccess, CommentsResponseContentFailure> {
if post_slug.is_empty() {
return Err(PostSlugEmpty);
}

let post = post_service
.post_by_slug(&post_slug)
.await
.map_err(|e| DatabaseError {
reason: e.to_string(),
})?
.ok_or(PostNotFound)?;

let offset = offset.unwrap_or(0).max(0);
let limit = limit.unwrap_or(200).max(0).min(200);

let (comments_result, total_result) = tokio::join!(
comment_service.comments_by_post_id(&post.id, &offset, &limit),
comment_service.comments_count_by_post_id(&post.id),
);

let comments = comments_result
.map_err(|e| DatabaseError {
reason: e.to_string(),
})?
.into_iter()
.map(|a| a.into())
.collect();

let total = total_result.map_err(|e| DatabaseError {
reason: e.to_string(),
})?;

Ok(CommentsResponseContentSuccess {
comments,
total,
offset,
limit,
})
}

pub async fn http_handler<Extensions>(
request: ApiRequest<CommentsRequestContent, Extensions>,
) -> ApiResponse<CommentsResponseContentSuccess, CommentsResponseContentFailure>
where
Extensions: Resolve<Arc<Box<dyn CommentService>>> + Resolve<Arc<Box<dyn PostService>>>,
{
ApiResponse::from(
handler(
request.content.post_slug,
request.content.offset,
request.content.limit,
request.content.comment_service,
request.content.post_service,
)
.await,
)
}
6 changes: 6 additions & 0 deletions blog-server-api/src/endpoints/comments/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
mod handler;
mod request_content;
mod response_content_failure;
mod response_content_success;

pub use handler::http_handler;
42 changes: 42 additions & 0 deletions blog-server-api/src/endpoints/comments/request_content.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use crate::extensions::Resolve;
use blog_server_services::traits::comment_service::*;
use blog_server_services::traits::post_service::*;
use screw_api::request::{ApiRequestContent, ApiRequestOriginContent};
use std::sync::Arc;

pub struct CommentsRequestContent {
pub(super) post_slug: String,
pub(super) offset: Option<i64>,
pub(super) limit: Option<i64>,
pub(super) comment_service: Arc<Box<dyn CommentService>>,
pub(super) post_service: Arc<Box<dyn PostService>>,
}

impl<Extensions> ApiRequestContent<Extensions> for CommentsRequestContent
where
Extensions: Resolve<Arc<Box<dyn CommentService>>> + Resolve<Arc<Box<dyn PostService>>>,
{
type Data = ();

fn create(origin_content: ApiRequestOriginContent<Self::Data, Extensions>) -> Self {
Self {
post_slug: origin_content
.path
.get("post_slug")
.map(|n| n.to_owned())
.unwrap_or_default(),
offset: origin_content
.query
.get("offset")
.map(|v| v.parse().ok())
.flatten(),
limit: origin_content
.query
.get("limit")
.map(|v| v.parse().ok())
.flatten(),
comment_service: origin_content.extensions.resolve(),
post_service: origin_content.extensions.resolve(),
}
}
}
50 changes: 50 additions & 0 deletions blog-server-api/src/endpoints/comments/response_content_failure.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use hyper::StatusCode;
use screw_api::response::{ApiResponseContentBase, ApiResponseContentFailure};

pub enum CommentsResponseContentFailure {
DatabaseError { reason: String },
PostSlugEmpty,
PostNotFound,
}

impl ApiResponseContentBase for CommentsResponseContentFailure {
fn status_code(&self) -> &'static StatusCode {
match self {
CommentsResponseContentFailure::DatabaseError { reason: _ } => {
&StatusCode::INTERNAL_SERVER_ERROR
}
CommentsResponseContentFailure::PostSlugEmpty => &StatusCode::BAD_REQUEST,
CommentsResponseContentFailure::PostNotFound => &StatusCode::NOT_FOUND,
}
}
}

impl ApiResponseContentFailure for CommentsResponseContentFailure {
fn identifier(&self) -> &'static str {
match self {
CommentsResponseContentFailure::DatabaseError { reason: _ } => {
"COMMENTS_DATABASE_ERROR"
}
CommentsResponseContentFailure::PostSlugEmpty => "COMMENTS_POST_SLUG_EMPTY",
CommentsResponseContentFailure::PostNotFound => "COMMENTS_POST_NOT_FOUND",
}
}

fn reason(&self) -> Option<String> {
Some(match self {
CommentsResponseContentFailure::DatabaseError { reason } => {
if cfg!(debug_assertions) {
format!("database error: {}", reason)
} else {
"internal database error".to_string()
}
}
CommentsResponseContentFailure::PostSlugEmpty => {
"comments root post slug is empty in request URL".to_string()
}
CommentsResponseContentFailure::PostNotFound => {
"comments root post record not found in database".to_string()
}
})
}
}
35 changes: 35 additions & 0 deletions blog-server-api/src/endpoints/comments/response_content_success.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use crate::entities::Comment;
use hyper::StatusCode;
use screw_api::response::{ApiResponseContentBase, ApiResponseContentSuccess};
use serde::Serialize;

#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct CommentsResponseContentSuccess {
pub(super) comments: Vec<Comment>,
pub(super) total: i64,
pub(super) offset: i64,
pub(super) limit: i64,
}

impl ApiResponseContentBase for CommentsResponseContentSuccess {
fn status_code(&self) -> &'static StatusCode {
&StatusCode::OK
}
}

impl ApiResponseContentSuccess for CommentsResponseContentSuccess {
type Data = Self;

fn identifier(&self) -> &'static str {
"COMMENTS_OK"
}

fn description(&self) -> Option<String> {
Some("comments list returned".to_string())
}

fn data(&self) -> &Self::Data {
self
}
}
1 change: 1 addition & 0 deletions blog-server-api/src/endpoints/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod author;
pub mod author_me;
pub mod authors;
pub mod comments;
pub mod login;
pub mod post;
pub mod posts;
27 changes: 27 additions & 0 deletions blog-server-api/src/entities/comment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use super::*;
use blog_server_services::traits::comment_service::Comment as ServiceComment;
use serde::Serialize;

#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Comment {
pub post_id: i64,
pub created_at: i64,
pub content: String,
pub short_author: ShortAuthor,
}

impl Into<Comment> for ServiceComment {
fn into(self) -> Comment {
Comment {
post_id: self.base.post_id,
created_at: self.base.created_at,
content: self.base.content,
short_author: ShortAuthor {
slug: self.author_slug,
first_name: self.author_first_name,
last_name: self.author_last_name,
},
}
}
}
2 changes: 2 additions & 0 deletions blog-server-api/src/entities/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
mod author;
mod comment;
mod post;
mod tag;

pub use author::*;
pub use comment::*;
pub use post::*;
pub use tag::*;
2 changes: 1 addition & 1 deletion blog-server-api/src/entities/post.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use serde::Serialize;
pub struct Post {
pub title: String,
pub slug: String,
pub summary: Option<String>,
pub summary: String,
pub created_at: i64,
pub content: Option<String>,
pub short_author: ShortAuthor,
Expand Down
17 changes: 15 additions & 2 deletions blog-server-api/src/extensions.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use blog_server_services::impls::{create_rbatis_author_service, create_rbatis_post_service};
use blog_server_services::impls::{
create_rbatis_author_service, create_rbatis_comment_service, create_rbatis_post_service,
};
use blog_server_services::traits::author_service::AuthorService;
use blog_server_services::traits::comment_service::CommentService;
use blog_server_services::traits::post_service::PostService;
use rbatis::rbatis::RBatis;
use std::sync::Arc;
Expand All @@ -9,13 +12,16 @@ pub trait Resolve<T>: Send + Sync {
}

pub trait ExtensionsProviderType:
Resolve<Arc<Box<dyn AuthorService>>> + Resolve<Arc<Box<dyn PostService>>>
Resolve<Arc<Box<dyn AuthorService>>>
+ Resolve<Arc<Box<dyn PostService>>>
+ Resolve<Arc<Box<dyn CommentService>>>
{
}

struct ExtensionsProvider {
author_service: Arc<Box<dyn AuthorService>>,
post_service: Arc<Box<dyn PostService>>,
comment_service: Arc<Box<dyn CommentService>>,
}

impl ExtensionsProviderType for ExtensionsProvider {}
Expand All @@ -32,9 +38,16 @@ impl Resolve<Arc<Box<dyn PostService>>> for ExtensionsProvider {
}
}

impl Resolve<Arc<Box<dyn CommentService>>> for ExtensionsProvider {
fn resolve(&self) -> Arc<Box<dyn CommentService>> {
self.comment_service.clone()
}
}

pub fn make_extensions(rbatis: RBatis) -> impl ExtensionsProviderType {
ExtensionsProvider {
author_service: Arc::new(create_rbatis_author_service(rbatis.clone())),
post_service: Arc::new(create_rbatis_post_service(rbatis.clone())),
comment_service: Arc::new(create_rbatis_comment_service(rbatis.clone())),
}
}
5 changes: 5 additions & 0 deletions blog-server-api/src/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ pub fn make_router<Extensions: ExtensionsProviderType>(
.and_path("/posts")
.and_handler(posts::http_handler),
)
.route(
route::first::Route::with_method(&hyper::Method::GET)
.and_path("/comments/{post_slug:[^/]*}")
.and_handler(comments::http_handler),
)
.route(
route::first::Route::with_method(&hyper::Method::POST)
.and_path("/login")
Expand Down
2 changes: 2 additions & 0 deletions blog-server-services/src/impls/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
mod rbatis_author_service;
mod rbatis_comment_service;
mod rbatis_post_service;

pub use rbatis_author_service::create_rbatis_author_service;
pub use rbatis_comment_service::create_rbatis_comment_service;
pub use rbatis_post_service::create_rbatis_post_service;
Loading

0 comments on commit 9fbbbf0

Please sign in to comment.