Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

comments impl #2

Merged
merged 4 commits into from
Jul 9, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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(50).max(0).min(50);

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