Skip to content

Commit

Permalink
Merge pull request #12 from tikitko/feat/createPostApi
Browse files Browse the repository at this point in the history
feat: create new post route init
  • Loading branch information
YoshkiMatryoshki authored Jul 20, 2023
2 parents 7f80ffb + fe6d03c commit 1b00e7b
Show file tree
Hide file tree
Showing 21 changed files with 290 additions and 40 deletions.
39 changes: 39 additions & 0 deletions blog-server-api/src/endpoints/create_post/handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use super::request_content::CreatePostRequestContent;
use super::response_content_failure::CreatePostContentFailure;
use super::response_content_failure::CreatePostContentFailure::*;
use super::response_content_success::CreatePostContentSuccess;

pub async fn http_handler(
(CreatePostRequestContent {
new_post_data,
post_service,
auth_author_future,
},): (CreatePostRequestContent,),
) -> Result<CreatePostContentSuccess, CreatePostContentFailure> {
let author = auth_author_future.await.map_err(|e| Unauthorized {
reason: e.to_string(),
})?;

let base_post = new_post_data
.map_err(|e| ValidationError {
reason: e.to_string(),
})?
.into(author.id);

let inserted_id = post_service
.create_post(&base_post)
.await
.map_err(|e| DatabaseError {
reason: e.to_string(),
})?;

let created_post = post_service
.post_by_id(&inserted_id)
.await
.map_err(|e| DatabaseError {
reason: e.to_string(),
})?
.ok_or(InsertFailed)?;

Ok(created_post.into())
}
6 changes: 6 additions & 0 deletions blog-server-api/src/endpoints/create_post/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;
32 changes: 32 additions & 0 deletions blog-server-api/src/endpoints/create_post/request_content.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use crate::{entities::CreatePost, extensions::Resolve, utils::auth};
use blog_server_services::traits::{
author_service::{Author, AuthorService},
post_service::PostService,
};
use screw_api::request::{ApiRequestContent, ApiRequestOriginContent};
use screw_components::{dyn_fn::DFuture, dyn_result::DResult};
use std::sync::Arc;

pub struct CreatePostRequestContent {
pub(super) new_post_data: DResult<CreatePost>,
pub(super) post_service: Arc<Box<dyn PostService>>,
pub(super) auth_author_future: DFuture<Result<Author, auth::Error>>,
}

impl<Extensions> ApiRequestContent<Extensions> for CreatePostRequestContent
where
Extensions: Resolve<Arc<Box<dyn PostService>>> + Resolve<Arc<Box<dyn AuthorService>>>,
{
type Data = CreatePost;

fn create(origin_content: ApiRequestOriginContent<Self::Data, Extensions>) -> Self {
Self {
new_post_data: origin_content.data_result,
post_service: origin_content.extensions.resolve(),
auth_author_future: Box::pin(auth::author(
origin_content.http_parts,
origin_content.extensions.resolve(),
)),
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use hyper::StatusCode;
use screw_api::response::{ApiResponseContentBase, ApiResponseContentFailure};

pub enum CreatePostContentFailure {
DatabaseError { reason: String },
ValidationError { reason: String },
AlreadyExists,
InsertFailed,
Unauthorized { reason: String },
}

impl ApiResponseContentBase for CreatePostContentFailure {
fn status_code(&self) -> &'static StatusCode {
match self {
CreatePostContentFailure::DatabaseError { reason: _ } => {
&StatusCode::INTERNAL_SERVER_ERROR
}
CreatePostContentFailure::AlreadyExists => &StatusCode::BAD_REQUEST,
CreatePostContentFailure::ValidationError { reason: _ } => &StatusCode::BAD_REQUEST,
CreatePostContentFailure::Unauthorized { reason: _ } => &StatusCode::UNAUTHORIZED,
CreatePostContentFailure::InsertFailed => &StatusCode::INTERNAL_SERVER_ERROR,
}
}
}

impl ApiResponseContentFailure for CreatePostContentFailure {
fn identifier(&self) -> &'static str {
match self {
CreatePostContentFailure::DatabaseError { reason: _ } => "CREATE_POST_DATABASE_ERROR",
CreatePostContentFailure::ValidationError { reason: _ } => {
"CREATE_POST_VALIDATION_ERROR"
}
CreatePostContentFailure::AlreadyExists => "CREATE_POST_ALREASY_EXISTS",
CreatePostContentFailure::InsertFailed => "CREATE_POST_COULD_NOT_FIND_CREATED_POST",
CreatePostContentFailure::Unauthorized { reason: _ } => "CREATE_POST_UNAUTHORIZED",
}
}

fn reason(&self) -> Option<String> {
Some(match self {
CreatePostContentFailure::DatabaseError { reason } => {
if cfg!(debug_assertions) {
format!("database error: {}", reason)
} else {
"internal database error".to_string()
}
}
CreatePostContentFailure::Unauthorized { reason } => {
if cfg!(debug_assertions) {
format!("unauthorized error: {}", reason)
} else {
"unauthorized error".to_string()
}
}
CreatePostContentFailure::ValidationError { reason } => {
format!("validation error: {}", reason)
}
CreatePostContentFailure::AlreadyExists => {
String::from("post with specified ID already exists")
}
CreatePostContentFailure::InsertFailed => String::from("error while creating new post"),
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use crate::entities::Post;
use blog_server_services::traits::post_service::Post as ServicePost;
use hyper::StatusCode;
use screw_api::response::{ApiResponseContentBase, ApiResponseContentSuccess};
use serde::Serialize;

#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct CreatePostContentSuccess {
created_post: Post,
}

impl Into<CreatePostContentSuccess> for ServicePost {
fn into(self) -> CreatePostContentSuccess {
CreatePostContentSuccess {
created_post: (self.into()),
}
}
}

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

impl ApiResponseContentSuccess for CreatePostContentSuccess {
type Data = Self;

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

fn description(&self) -> Option<String> {
Some(String::from("post record created"))
}

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
Expand Up @@ -2,6 +2,7 @@ pub mod author;
pub mod author_me;
pub mod authors;
pub mod comments;
pub mod create_post;
pub mod login;
pub mod post;
pub mod posts;
4 changes: 2 additions & 2 deletions blog-server-api/src/entities/author.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use blog_server_services::traits::author_service::Author as ServiceAuthor;
use serde::Serialize;
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Serialize)]
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ShortAuthor {
pub slug: String,
Expand Down
35 changes: 35 additions & 0 deletions blog-server-api/src/entities/create_post.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use blog_server_services::traits::post_service::BasePost;
use serde::{Deserialize, Serialize};

use crate::utils::time_utils;

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CreatePost {
pub title: String,
pub slug: String,
pub summary: String,
pub published: u8,
pub content: Option<String>,
pub tags: Vec<ShortTag>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ShortTag {
pub title: String,
}

impl CreatePost {
pub fn into(self, author_id: u64) -> BasePost {
BasePost {
author_id,
created_at: time_utils::now_as_secs(),
title: self.title,
slug: self.slug,
summary: self.summary,
published: self.published,
content: self.content,
}
}
}
2 changes: 2 additions & 0 deletions blog-server-api/src/entities/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
mod author;
mod comment;
mod create_post;
mod post;
mod tag;

pub use author::*;
pub use comment::*;
pub use create_post::*;
pub use post::*;
pub use tag::*;
12 changes: 7 additions & 5 deletions blog-server-api/src/entities/post.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use super::*;
use blog_server_services::traits::post_service::Post as ServicePost;
use serde::Serialize;
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Serialize)]
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Post {
pub id: u64,
pub title: String,
pub slug: String,
pub summary: String,
pub created_at: i64,
pub created_at: u64,
pub content: Option<String>,
pub short_author: ShortAuthor,
pub tags: Vec<Tag>,
Expand All @@ -17,6 +18,7 @@ pub struct Post {
impl Into<Post> for ServicePost {
fn into(self) -> Post {
Post {
id: self.id,
title: self.base.title,
slug: self.base.slug,
summary: self.base.summary,
Expand All @@ -31,9 +33,9 @@ impl Into<Post> for ServicePost {
.tags
.into_iter()
.map(|v| Tag {
id: v.id,
title: v.title,
//TODO: Change to ID
slug: v.id.to_string(),
slug: v.slug,
})
.collect(),
}
Expand Down
5 changes: 3 additions & 2 deletions blog-server-api/src/entities/tag.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use serde::Serialize;
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Serialize)]
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Tag {
pub id: u64,
pub title: String,
pub slug: String,
}
5 changes: 5 additions & 0 deletions blog-server-api/src/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ pub fn make_router<Extensions: ExtensionsProviderType>(
.and_path("/posts")
.and_handler(posts::http_handler),
)
.route(
route::first::Route::with_method(&hyper::Method::POST)
.and_path("/post")
.and_handler(create_post::http_handler),
)
.scoped("/search", |r| {
r.route(
route::first::Route::with_method(&hyper::Method::GET)
Expand Down
2 changes: 1 addition & 1 deletion blog-server-api/src/utils/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::sync::Arc;
#[derive(Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
struct Data {
author_id: i64,
author_id: u64,
exp: u64,
}

Expand Down
1 change: 1 addition & 0 deletions blog-server-api/src/utils/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod auth;
pub mod jwt;
pub mod password;
pub mod time_utils;
8 changes: 8 additions & 0 deletions blog-server-api/src/utils/time_utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use std::time::{SystemTime, UNIX_EPOCH};

pub fn now_as_secs() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs()
}
4 changes: 2 additions & 2 deletions blog-server-services/src/impls/rbatis_author_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ pub fn create_rbatis_author_service(rb: RBatis) -> Box<dyn AuthorService> {
}

impl_insert!(BaseAuthor {}, "author");
impl_select!(Author {select_by_id(id: &i64) -> Option =>
impl_select!(Author {select_by_id(id: &u64) -> Option =>
"`WHERE id = #{id} LIMIT 1`"});
impl_select!(Author {select_by_slug(slug: &String) -> Option =>
"`WHERE slug = #{slug} LIMIT 1`"});
Expand Down Expand Up @@ -67,7 +67,7 @@ impl AuthorService for RbatisAuthorService {
async fn authors(&self, offset: &i64, limit: &i64) -> DResult<Vec<Author>> {
Ok(Author::select_all_with_offset_and_limit(&mut self.rb.clone(), offset, limit).await?)
}
async fn author_by_id(&self, id: &i64) -> DResult<Option<Author>> {
async fn author_by_id(&self, id: &u64) -> DResult<Option<Author>> {
Ok(Author::select_by_id(&mut self.rb.clone(), id).await?)
}
async fn author_by_slug(&self, slug: &String) -> DResult<Option<Author>> {
Expand Down
8 changes: 4 additions & 4 deletions blog-server-services/src/impls/rbatis_comment_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ impl Comment {
WHERE post_comment.post_id = #{post_id}
"
)]
async fn count_by_post_id(rb: &RBatis, post_id: &i64) -> rbatis::Result<i64> {
async fn count_by_post_id(rb: &RBatis, post_id: &u64) -> rbatis::Result<i64> {
impled!()
}
#[py_sql(
Expand All @@ -35,7 +35,7 @@ impl Comment {
)]
async fn select_all_by_post_id_with_limit_and_offset(
rb: &RBatis,
post_id: &i64,
post_id: &u64,
limit: &i64,
offset: &i64,
) -> rbatis::Result<Vec<Comment>> {
Expand All @@ -49,12 +49,12 @@ struct RbatisCommentService {

#[async_trait]
impl CommentService for RbatisCommentService {
async fn comments_count_by_post_id(&self, post_id: &i64) -> DResult<i64> {
async fn comments_count_by_post_id(&self, post_id: &u64) -> DResult<i64> {
Ok(Comment::count_by_post_id(&self.rb, post_id).await?)
}
async fn comments_by_post_id(
&self,
post_id: &i64,
post_id: &u64,
offset: &i64,
limit: &i64,
) -> DResult<Vec<Comment>> {
Expand Down
Loading

0 comments on commit 1b00e7b

Please sign in to comment.