From 262ceed660f335904978ad541cfc024ee878c519 Mon Sep 17 00:00:00 2001 From: Brantov Denis Date: Thu, 13 Jul 2023 12:28:52 +0200 Subject: [PATCH 1/8] feat: create new post route init --- .../src/endpoints/create_post/handler.rs | 41 ++++++++++++++++ .../src/endpoints/create_post/mod.rs | 6 +++ .../endpoints/create_post/request_content.rs | 32 +++++++++++++ .../create_post/response_content_failure.rs | 48 +++++++++++++++++++ .../create_post/response_content_success.rs | 41 ++++++++++++++++ blog-server-api/src/endpoints/mod.rs | 1 + blog-server-api/src/router.rs | 5 ++ 7 files changed, 174 insertions(+) create mode 100644 blog-server-api/src/endpoints/create_post/handler.rs create mode 100644 blog-server-api/src/endpoints/create_post/mod.rs create mode 100644 blog-server-api/src/endpoints/create_post/request_content.rs create mode 100644 blog-server-api/src/endpoints/create_post/response_content_failure.rs create mode 100644 blog-server-api/src/endpoints/create_post/response_content_success.rs diff --git a/blog-server-api/src/endpoints/create_post/handler.rs b/blog-server-api/src/endpoints/create_post/handler.rs new file mode 100644 index 0000000..ff900cb --- /dev/null +++ b/blog-server-api/src/endpoints/create_post/handler.rs @@ -0,0 +1,41 @@ +use super::request_content::{CreatePostRequestContent, CreatePostRequestData}; +use super::response_content_failure::CreatePostContentFailure; +use super::response_content_failure::CreatePostContentFailure::*; +use super::response_content_success::CreatePostContentSuccess; +use crate::extensions::Resolve; +use blog_server_services::traits::post_service::PostService; +use screw_api::request::ApiRequest; +use screw_api::response::ApiResponse; +use screw_components::dyn_result::DResult; +use std::sync::Arc; + +async fn handler( + new_post_data: DResult, + post_service: Arc>, +) -> Result { + let CreatePostRequestData { + post_id, + post_content, + } = new_post_data.map_err(|e| ValidationError { + reason: e.to_string(), + })?; + + let test = post_service + .post_by_id(&post_id) + .await + .map_err(|e| DatabaseError { + reason: e.to_string(), + })? + .ok_or(AlreadyExists)?; + + Ok(test.into()) +} + +pub async fn http_handler( + request: ApiRequest, +) -> ApiResponse +where + Extensions: Resolve>>, +{ + ApiResponse::from(handler(request.content.new_post_data, request.content.post_service).await) +} diff --git a/blog-server-api/src/endpoints/create_post/mod.rs b/blog-server-api/src/endpoints/create_post/mod.rs new file mode 100644 index 0000000..3426965 --- /dev/null +++ b/blog-server-api/src/endpoints/create_post/mod.rs @@ -0,0 +1,6 @@ +mod handler; +mod request_content; +mod response_content_failure; +mod response_content_success; + +pub use handler::http_handler; diff --git a/blog-server-api/src/endpoints/create_post/request_content.rs b/blog-server-api/src/endpoints/create_post/request_content.rs new file mode 100644 index 0000000..c935d2b --- /dev/null +++ b/blog-server-api/src/endpoints/create_post/request_content.rs @@ -0,0 +1,32 @@ +use crate::extensions::Resolve; +use blog_server_services::traits::post_service::*; +use screw_api::request::{ApiRequestContent, ApiRequestOriginContent}; +use screw_components::dyn_result::DResult; +use serde::Deserialize; +use std::sync::Arc; + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CreatePostRequestData { + pub post_id: i64, + pub post_content: String, +} + +pub struct CreatePostRequestContent { + pub(super) new_post_data: DResult, + pub(super) post_service: Arc>, +} + +impl ApiRequestContent for CreatePostRequestContent +where + Extensions: Resolve>>, +{ + type Data = CreatePostRequestData; + + fn create(origin_content: ApiRequestOriginContent) -> Self { + Self { + new_post_data: origin_content.data_result, + post_service: origin_content.extensions.resolve(), + } + } +} diff --git a/blog-server-api/src/endpoints/create_post/response_content_failure.rs b/blog-server-api/src/endpoints/create_post/response_content_failure.rs new file mode 100644 index 0000000..1b7780e --- /dev/null +++ b/blog-server-api/src/endpoints/create_post/response_content_failure.rs @@ -0,0 +1,48 @@ +use hyper::StatusCode; +use screw_api::response::{ApiResponseContentBase, ApiResponseContentFailure}; + +pub enum CreatePostContentFailure { + DatabaseError { reason: String }, + ValidationError { reason: String }, + AlreadyExists, +} + +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, + } + } +} + +impl ApiResponseContentFailure for CreatePostContentFailure { + fn identifier(&self) -> &'static str { + match self { + CreatePostContentFailure::DatabaseError { reason: _ } => "POST_DATABASE_ERROR", + CreatePostContentFailure::ValidationError { reason: _ } => "POST_VALIDATION_ERROR", + CreatePostContentFailure::AlreadyExists => "POST_ALREASY_EXISTS", + } + } + + fn reason(&self) -> Option { + Some(match self { + CreatePostContentFailure::DatabaseError { reason } => { + if cfg!(debug_assertions) { + format!("database error: {}", reason) + } else { + "internal database error".to_string() + } + } + CreatePostContentFailure::ValidationError { reason } => { + format!("validation error: {}", reason) + } + CreatePostContentFailure::AlreadyExists => { + String::from("post with specified ID already exists") + } + }) + } +} diff --git a/blog-server-api/src/endpoints/create_post/response_content_success.rs b/blog-server-api/src/endpoints/create_post/response_content_success.rs new file mode 100644 index 0000000..b9098f1 --- /dev/null +++ b/blog-server-api/src/endpoints/create_post/response_content_success.rs @@ -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 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 { + Some(String::from("post record created")) + } + + fn data(&self) -> &Self::Data { + self + } +} diff --git a/blog-server-api/src/endpoints/mod.rs b/blog-server-api/src/endpoints/mod.rs index cc98090..b9131a4 100644 --- a/blog-server-api/src/endpoints/mod.rs +++ b/blog-server-api/src/endpoints/mod.rs @@ -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; diff --git a/blog-server-api/src/router.rs b/blog-server-api/src/router.rs index 5a13b77..ea986df 100644 --- a/blog-server-api/src/router.rs +++ b/blog-server-api/src/router.rs @@ -77,6 +77,11 @@ pub fn make_router( .and_path("/posts") .and_handler(posts::http_handler), ) + .route( + route::first::Route::with_method(&hyper::Method::POST) + .and_path("/post/new") + .and_handler(create_post::http_handler), + ) .scoped("/search", |r| { r.route( route::first::Route::with_method(&hyper::Method::GET) From 64fe094e84e2d1e4fa0c33d244c95902a6f1a677 Mon Sep 17 00:00:00 2001 From: Brantov Denis Date: Sat, 15 Jul 2023 13:32:33 +0200 Subject: [PATCH 2/8] feat: simple blog savind and contracts extending --- .../src/endpoints/create_post/handler.rs | 31 +++++++++------- .../endpoints/create_post/request_content.rs | 16 +++------ .../create_post/response_content_failure.rs | 4 +++ blog-server-api/src/entities/author.rs | 4 +-- blog-server-api/src/entities/create_post.rs | 35 +++++++++++++++++++ blog-server-api/src/entities/mod.rs | 2 ++ blog-server-api/src/entities/post.rs | 12 ++++--- blog-server-api/src/entities/tag.rs | 5 +-- blog-server-api/src/router.rs | 2 +- blog-server-api/src/utils/mod.rs | 1 + blog-server-api/src/utils/time_utils.rs | 8 +++++ .../src/impls/rbatis_post_service.rs | 24 +++++++++---- .../src/traits/post_service.rs | 3 +- 13 files changed, 105 insertions(+), 42 deletions(-) create mode 100644 blog-server-api/src/entities/create_post.rs create mode 100644 blog-server-api/src/utils/time_utils.rs diff --git a/blog-server-api/src/endpoints/create_post/handler.rs b/blog-server-api/src/endpoints/create_post/handler.rs index ff900cb..f3fdf37 100644 --- a/blog-server-api/src/endpoints/create_post/handler.rs +++ b/blog-server-api/src/endpoints/create_post/handler.rs @@ -1,7 +1,8 @@ -use super::request_content::{CreatePostRequestContent, CreatePostRequestData}; +use super::request_content::CreatePostRequestContent; use super::response_content_failure::CreatePostContentFailure; use super::response_content_failure::CreatePostContentFailure::*; use super::response_content_success::CreatePostContentSuccess; +use crate::entities::CreatePost; use crate::extensions::Resolve; use blog_server_services::traits::post_service::PostService; use screw_api::request::ApiRequest; @@ -10,25 +11,31 @@ use screw_components::dyn_result::DResult; use std::sync::Arc; async fn handler( - new_post_data: DResult, + new_post_data: DResult, post_service: Arc>, ) -> Result { - let CreatePostRequestData { - post_id, - post_content, - } = new_post_data.map_err(|e| ValidationError { - reason: e.to_string(), - })?; + let base_post = new_post_data + .map_err(|e| ValidationError { + reason: e.to_string(), + })? + .into(1); + + let inserted_id = post_service + .create_post(&base_post) + .await + .map_err(|e| DatabaseError { + reason: e.to_string(), + })?; - let test = post_service - .post_by_id(&post_id) + let created_post = post_service + .post_by_id(&inserted_id) .await .map_err(|e| DatabaseError { reason: e.to_string(), })? - .ok_or(AlreadyExists)?; + .ok_or(InsertFailed)?; - Ok(test.into()) + Ok(created_post.into()) } pub async fn http_handler( diff --git a/blog-server-api/src/endpoints/create_post/request_content.rs b/blog-server-api/src/endpoints/create_post/request_content.rs index c935d2b..a7ffaab 100644 --- a/blog-server-api/src/endpoints/create_post/request_content.rs +++ b/blog-server-api/src/endpoints/create_post/request_content.rs @@ -1,19 +1,11 @@ -use crate::extensions::Resolve; -use blog_server_services::traits::post_service::*; +use crate::{entities::CreatePost, extensions::Resolve}; +use blog_server_services::traits::post_service::PostService; use screw_api::request::{ApiRequestContent, ApiRequestOriginContent}; use screw_components::dyn_result::DResult; -use serde::Deserialize; use std::sync::Arc; -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct CreatePostRequestData { - pub post_id: i64, - pub post_content: String, -} - pub struct CreatePostRequestContent { - pub(super) new_post_data: DResult, + pub(super) new_post_data: DResult, pub(super) post_service: Arc>, } @@ -21,7 +13,7 @@ impl ApiRequestContent for CreatePostRequestContent where Extensions: Resolve>>, { - type Data = CreatePostRequestData; + type Data = CreatePost; fn create(origin_content: ApiRequestOriginContent) -> Self { Self { diff --git a/blog-server-api/src/endpoints/create_post/response_content_failure.rs b/blog-server-api/src/endpoints/create_post/response_content_failure.rs index 1b7780e..ed65d08 100644 --- a/blog-server-api/src/endpoints/create_post/response_content_failure.rs +++ b/blog-server-api/src/endpoints/create_post/response_content_failure.rs @@ -5,6 +5,7 @@ pub enum CreatePostContentFailure { DatabaseError { reason: String }, ValidationError { reason: String }, AlreadyExists, + InsertFailed, } impl ApiResponseContentBase for CreatePostContentFailure { @@ -15,6 +16,7 @@ impl ApiResponseContentBase for CreatePostContentFailure { } CreatePostContentFailure::AlreadyExists => &StatusCode::BAD_REQUEST, CreatePostContentFailure::ValidationError { reason: _ } => &StatusCode::BAD_REQUEST, + CreatePostContentFailure::InsertFailed => &StatusCode::INTERNAL_SERVER_ERROR, } } } @@ -25,6 +27,7 @@ impl ApiResponseContentFailure for CreatePostContentFailure { CreatePostContentFailure::DatabaseError { reason: _ } => "POST_DATABASE_ERROR", CreatePostContentFailure::ValidationError { reason: _ } => "POST_VALIDATION_ERROR", CreatePostContentFailure::AlreadyExists => "POST_ALREASY_EXISTS", + CreatePostContentFailure::InsertFailed => "COULD_NOT_FIND_CREATED_POST", } } @@ -43,6 +46,7 @@ impl ApiResponseContentFailure for CreatePostContentFailure { CreatePostContentFailure::AlreadyExists => { String::from("post with specified ID already exists") } + CreatePostContentFailure::InsertFailed => String::from("error while creating new post"), }) } } diff --git a/blog-server-api/src/entities/author.rs b/blog-server-api/src/entities/author.rs index ae35ab4..6db996c 100644 --- a/blog-server-api/src/entities/author.rs +++ b/blog-server-api/src/entities/author.rs @@ -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, diff --git a/blog-server-api/src/entities/create_post.rs b/blog-server-api/src/entities/create_post.rs new file mode 100644 index 0000000..406e86e --- /dev/null +++ b/blog-server-api/src/entities/create_post.rs @@ -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, + pub tags: Vec, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ShortTag { + pub title: String, +} + +impl CreatePost { + pub fn into(self, author_id: i64) -> 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, + } + } +} diff --git a/blog-server-api/src/entities/mod.rs b/blog-server-api/src/entities/mod.rs index 43ca4f9..0a91547 100644 --- a/blog-server-api/src/entities/mod.rs +++ b/blog-server-api/src/entities/mod.rs @@ -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::*; diff --git a/blog-server-api/src/entities/post.rs b/blog-server-api/src/entities/post.rs index 2a3ab2d..d81d6a8 100644 --- a/blog-server-api/src/entities/post.rs +++ b/blog-server-api/src/entities/post.rs @@ -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: i64, pub title: String, pub slug: String, pub summary: String, - pub created_at: i64, + pub created_at: u64, pub content: Option, pub short_author: ShortAuthor, pub tags: Vec, @@ -17,6 +18,7 @@ pub struct Post { impl Into for ServicePost { fn into(self) -> Post { Post { + id: self.id, title: self.base.title, slug: self.base.slug, summary: self.base.summary, @@ -31,9 +33,9 @@ impl Into 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(), } diff --git a/blog-server-api/src/entities/tag.rs b/blog-server-api/src/entities/tag.rs index 196876f..13fbd8b 100644 --- a/blog-server-api/src/entities/tag.rs +++ b/blog-server-api/src/entities/tag.rs @@ -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: i64, pub title: String, pub slug: String, } diff --git a/blog-server-api/src/router.rs b/blog-server-api/src/router.rs index ea986df..ff192ce 100644 --- a/blog-server-api/src/router.rs +++ b/blog-server-api/src/router.rs @@ -79,7 +79,7 @@ pub fn make_router( ) .route( route::first::Route::with_method(&hyper::Method::POST) - .and_path("/post/new") + .and_path("/post") .and_handler(create_post::http_handler), ) .scoped("/search", |r| { diff --git a/blog-server-api/src/utils/mod.rs b/blog-server-api/src/utils/mod.rs index 6ec6460..d9584bc 100644 --- a/blog-server-api/src/utils/mod.rs +++ b/blog-server-api/src/utils/mod.rs @@ -1,3 +1,4 @@ pub mod auth; pub mod jwt; pub mod password; +pub mod time_utils; diff --git a/blog-server-api/src/utils/time_utils.rs b/blog-server-api/src/utils/time_utils.rs new file mode 100644 index 0000000..9bc33b6 --- /dev/null +++ b/blog-server-api/src/utils/time_utils.rs @@ -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() +} diff --git a/blog-server-services/src/impls/rbatis_post_service.rs b/blog-server-services/src/impls/rbatis_post_service.rs index 94d4401..b1b8567 100644 --- a/blog-server-services/src/impls/rbatis_post_service.rs +++ b/blog-server-services/src/impls/rbatis_post_service.rs @@ -1,6 +1,6 @@ use crate::traits::post_service::{BasePost, Post, PostService, Tag}; use rbatis::rbatis::RBatis; -use screw_components::dyn_result::{DError, DResult}; +use screw_components::dyn_result::DResult; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -15,6 +15,7 @@ impl_insert!(BasePost {}, "post"); pub struct TagDto { post_id: i64, id: i64, + slug: String, title: String, } @@ -23,6 +24,7 @@ impl Into for TagDto { Tag { id: self.id, title: self.title, + slug: self.slug, } } } @@ -102,6 +104,7 @@ impl Post { SELECT \ tag.id, \ tag.title, \ + tag.slug, \ post_tag.post_id \ FROM post_tag \ JOIN tag ON tag.id = post_tag.tag_id \ @@ -148,6 +151,17 @@ struct RbatisPostService { } impl RbatisPostService { + #[py_sql( + " + insert into post (author_id,title,slug,summary,published,created_at,content) VALUES + (#{post.author_id},#{post.title},#{post.slug},#{post.summary},#{post.published},to_timestamp(#{post.created_at}),#{post.content}) + returning id + " + )] + async fn test_insert(rb: &RBatis, post: &BasePost) -> rbatis::Result { + impled!() + } + async fn saturate_with_tags(&self, post_option: Option) -> DResult> { match post_option { None => Ok(None), @@ -227,11 +241,7 @@ impl PostService for RbatisPostService { } async fn create_post(&self, post: &BasePost) -> DResult { - let insert_result = BasePost::insert(&mut self.rb.clone(), post).await?; - let last_insert_id = insert_result - .last_insert_id - .as_i64() - .ok_or::("wrond last_insert_id".into())?; - Ok(last_insert_id) + let test = RbatisPostService::test_insert(&self.rb, post).await?; + Ok(test) } } diff --git a/blog-server-services/src/traits/post_service.rs b/blog-server-services/src/traits/post_service.rs index eef337f..306268c 100644 --- a/blog-server-services/src/traits/post_service.rs +++ b/blog-server-services/src/traits/post_service.rs @@ -5,6 +5,7 @@ use serde::{Deserialize, Serialize}; #[serde(rename_all = "snake_case")] pub struct Tag { pub id: i64, + pub slug: String, pub title: String, } @@ -16,7 +17,7 @@ pub struct BasePost { pub slug: String, pub summary: String, pub published: u8, - pub created_at: i64, + pub created_at: u64, pub content: Option, } From 880e29aa0b684dbabc41c4cfea299ff585e4f94b Mon Sep 17 00:00:00 2001 From: Brantov Denis Date: Sat, 15 Jul 2023 13:34:33 +0200 Subject: [PATCH 3/8] style: upper sace foe sql commands --- blog-server-services/src/impls/rbatis_post_service.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/blog-server-services/src/impls/rbatis_post_service.rs b/blog-server-services/src/impls/rbatis_post_service.rs index b1b8567..07fdb12 100644 --- a/blog-server-services/src/impls/rbatis_post_service.rs +++ b/blog-server-services/src/impls/rbatis_post_service.rs @@ -153,9 +153,11 @@ struct RbatisPostService { impl RbatisPostService { #[py_sql( " - insert into post (author_id,title,slug,summary,published,created_at,content) VALUES + INSERT INTO post + (author_id,title,slug,summary,published,created_at,content) + VALUES (#{post.author_id},#{post.title},#{post.slug},#{post.summary},#{post.published},to_timestamp(#{post.created_at}),#{post.content}) - returning id + RETURNING id " )] async fn test_insert(rb: &RBatis, post: &BasePost) -> rbatis::Result { From 640e60992058f1860c02418ae4c49e0f8c32fe97 Mon Sep 17 00:00:00 2001 From: Brantov Denis Date: Sun, 16 Jul 2023 00:11:17 +0200 Subject: [PATCH 4/8] chore: rename test method --- blog-server-services/src/impls/rbatis_post_service.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blog-server-services/src/impls/rbatis_post_service.rs b/blog-server-services/src/impls/rbatis_post_service.rs index 07fdb12..6ee3a0b 100644 --- a/blog-server-services/src/impls/rbatis_post_service.rs +++ b/blog-server-services/src/impls/rbatis_post_service.rs @@ -160,7 +160,7 @@ impl RbatisPostService { RETURNING id " )] - async fn test_insert(rb: &RBatis, post: &BasePost) -> rbatis::Result { + async fn insert_new_post(rb: &RBatis, post: &BasePost) -> rbatis::Result { impled!() } @@ -243,7 +243,7 @@ impl PostService for RbatisPostService { } async fn create_post(&self, post: &BasePost) -> DResult { - let test = RbatisPostService::test_insert(&self.rb, post).await?; + let test = RbatisPostService::insert_new_post(&self.rb, post).await?; Ok(test) } } From 6e5f6c90e283a40789fbc6a59e7d3cc39ab3b496 Mon Sep 17 00:00:00 2001 From: Brantov Denis Date: Thu, 20 Jul 2023 00:02:29 +0200 Subject: [PATCH 5/8] ref: entity ids to u64 --- blog-server-api/src/entities/create_post.rs | 2 +- blog-server-api/src/entities/post.rs | 2 +- blog-server-api/src/entities/tag.rs | 2 +- .../src/impls/rbatis_comment_service.rs | 8 ++++---- .../src/impls/rbatis_post_service.rs | 20 +++++++++---------- .../src/traits/comment_service.rs | 4 ++-- .../src/traits/post_service.rs | 10 +++++----- 7 files changed, 24 insertions(+), 24 deletions(-) diff --git a/blog-server-api/src/entities/create_post.rs b/blog-server-api/src/entities/create_post.rs index 406e86e..1f9bb2e 100644 --- a/blog-server-api/src/entities/create_post.rs +++ b/blog-server-api/src/entities/create_post.rs @@ -21,7 +21,7 @@ pub struct ShortTag { } impl CreatePost { - pub fn into(self, author_id: i64) -> BasePost { + pub fn into(self, author_id: u64) -> BasePost { BasePost { author_id, created_at: time_utils::now_as_secs(), diff --git a/blog-server-api/src/entities/post.rs b/blog-server-api/src/entities/post.rs index d81d6a8..9746fc3 100644 --- a/blog-server-api/src/entities/post.rs +++ b/blog-server-api/src/entities/post.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Post { - pub id: i64, + pub id: u64, pub title: String, pub slug: String, pub summary: String, diff --git a/blog-server-api/src/entities/tag.rs b/blog-server-api/src/entities/tag.rs index 13fbd8b..8e36d65 100644 --- a/blog-server-api/src/entities/tag.rs +++ b/blog-server-api/src/entities/tag.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Tag { - pub id: i64, + pub id: u64, pub title: String, pub slug: String, } diff --git a/blog-server-services/src/impls/rbatis_comment_service.rs b/blog-server-services/src/impls/rbatis_comment_service.rs index 762ac02..674b77d 100644 --- a/blog-server-services/src/impls/rbatis_comment_service.rs +++ b/blog-server-services/src/impls/rbatis_comment_service.rs @@ -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 { + async fn count_by_post_id(rb: &RBatis, post_id: &u64) -> rbatis::Result { impled!() } #[py_sql( @@ -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> { @@ -49,12 +49,12 @@ struct RbatisCommentService { #[async_trait] impl CommentService for RbatisCommentService { - async fn comments_count_by_post_id(&self, post_id: &i64) -> DResult { + async fn comments_count_by_post_id(&self, post_id: &u64) -> DResult { 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> { diff --git a/blog-server-services/src/impls/rbatis_post_service.rs b/blog-server-services/src/impls/rbatis_post_service.rs index 6ee3a0b..e927af1 100644 --- a/blog-server-services/src/impls/rbatis_post_service.rs +++ b/blog-server-services/src/impls/rbatis_post_service.rs @@ -13,8 +13,8 @@ impl_insert!(BasePost {}, "post"); #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub struct TagDto { - post_id: i64, - id: i64, + post_id: u64, + id: u64, slug: String, title: String, } @@ -62,7 +62,7 @@ impl Post { LIMIT 1 \ " )] - async fn select_by_id(rb: &RBatis, id: &i64) -> rbatis::Result> { + async fn select_by_id(rb: &RBatis, id: &u64) -> rbatis::Result> { impled!() } #[py_sql( @@ -115,7 +115,7 @@ impl Post { ) \ " )] - async fn select_tags_by_posts(rb: &RBatis, post_ids: Vec) -> rbatis::Result> { + async fn select_tags_by_posts(rb: &RBatis, post_ids: Vec) -> rbatis::Result> { impled!() } #[py_sql( @@ -160,7 +160,7 @@ impl RbatisPostService { RETURNING id " )] - async fn insert_new_post(rb: &RBatis, post: &BasePost) -> rbatis::Result { + async fn insert_new_post(rb: &RBatis, post: &BasePost) -> rbatis::Result { impled!() } @@ -186,7 +186,7 @@ impl RbatisPostService { let post_ids = posts.iter().map(|post| post.id).collect(); - let mut grouped_tags: HashMap> = + let mut grouped_tags: HashMap> = Post::select_tags_by_posts(&self.rb, post_ids) .await? .into_iter() @@ -232,7 +232,7 @@ impl PostService for RbatisPostService { RbatisPostService::saturate_posts_with_tags(&self, posts).await } - async fn post_by_id(&self, id: &i64) -> DResult> { + async fn post_by_id(&self, id: &u64) -> DResult> { let post_option = Post::select_by_id(&self.rb, id).await?; RbatisPostService::saturate_with_tags(&self, post_option).await } @@ -242,8 +242,8 @@ impl PostService for RbatisPostService { RbatisPostService::saturate_with_tags(&self, post_option).await } - async fn create_post(&self, post: &BasePost) -> DResult { - let test = RbatisPostService::insert_new_post(&self.rb, post).await?; - Ok(test) + async fn create_post(&self, post: &BasePost) -> DResult { + let inserted_id = RbatisPostService::insert_new_post(&self.rb, post).await?; + Ok(inserted_id) } } diff --git a/blog-server-services/src/traits/comment_service.rs b/blog-server-services/src/traits/comment_service.rs index 7912411..9d7d024 100644 --- a/blog-server-services/src/traits/comment_service.rs +++ b/blog-server-services/src/traits/comment_service.rs @@ -24,10 +24,10 @@ pub struct Comment { #[async_trait] pub trait CommentService: Send + Sync { - async fn comments_count_by_post_id(&self, post_id: &i64) -> DResult; + async fn comments_count_by_post_id(&self, post_id: &u64) -> DResult; async fn comments_by_post_id( &self, - post_id: &i64, + post_id: &u64, offset: &i64, limit: &i64, ) -> DResult>; diff --git a/blog-server-services/src/traits/post_service.rs b/blog-server-services/src/traits/post_service.rs index 306268c..889c535 100644 --- a/blog-server-services/src/traits/post_service.rs +++ b/blog-server-services/src/traits/post_service.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub struct Tag { - pub id: i64, + pub id: u64, pub slug: String, pub title: String, } @@ -12,7 +12,7 @@ pub struct Tag { #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub struct BasePost { - pub author_id: i64, + pub author_id: u64, pub title: String, pub slug: String, pub summary: String, @@ -24,7 +24,7 @@ pub struct BasePost { #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub struct Post { - pub id: i64, + pub id: u64, pub author_slug: String, pub author_first_name: Option, pub author_last_name: Option, @@ -41,7 +41,7 @@ pub trait PostService: Send + Sync { -> DResult>; async fn posts_count(&self) -> DResult; async fn posts(&self, offset: &i64, limit: &i64) -> DResult>; - async fn post_by_id(&self, id: &i64) -> DResult>; + async fn post_by_id(&self, id: &u64) -> DResult>; async fn post_by_slug(&self, slug: &String) -> DResult>; - async fn create_post(&self, post: &BasePost) -> DResult; + async fn create_post(&self, post: &BasePost) -> DResult; } From 023b38a442fc611d52e8c537051c5351ca27eab0 Mon Sep 17 00:00:00 2001 From: Brantov Denis Date: Thu, 20 Jul 2023 00:02:52 +0200 Subject: [PATCH 6/8] fix: error descriptions --- .../endpoints/create_post/response_content_failure.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/blog-server-api/src/endpoints/create_post/response_content_failure.rs b/blog-server-api/src/endpoints/create_post/response_content_failure.rs index ed65d08..db3e584 100644 --- a/blog-server-api/src/endpoints/create_post/response_content_failure.rs +++ b/blog-server-api/src/endpoints/create_post/response_content_failure.rs @@ -24,10 +24,12 @@ impl ApiResponseContentBase for CreatePostContentFailure { impl ApiResponseContentFailure for CreatePostContentFailure { fn identifier(&self) -> &'static str { match self { - CreatePostContentFailure::DatabaseError { reason: _ } => "POST_DATABASE_ERROR", - CreatePostContentFailure::ValidationError { reason: _ } => "POST_VALIDATION_ERROR", - CreatePostContentFailure::AlreadyExists => "POST_ALREASY_EXISTS", - CreatePostContentFailure::InsertFailed => "COULD_NOT_FIND_CREATED_POST", + 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", } } From 7c6ba8c249c8cea10124178d3476a019c18b546c Mon Sep 17 00:00:00 2001 From: Brantov Denis Date: Thu, 20 Jul 2023 00:37:13 +0200 Subject: [PATCH 7/8] ref: new screw ver syntax, change author obtaining --- .../src/endpoints/create_post/handler.rs | 26 +++++-------------- blog-server-api/src/entities/create_post.rs | 5 ++-- 2 files changed, 9 insertions(+), 22 deletions(-) diff --git a/blog-server-api/src/endpoints/create_post/handler.rs b/blog-server-api/src/endpoints/create_post/handler.rs index f3fdf37..41e79ca 100644 --- a/blog-server-api/src/endpoints/create_post/handler.rs +++ b/blog-server-api/src/endpoints/create_post/handler.rs @@ -2,23 +2,18 @@ use super::request_content::CreatePostRequestContent; use super::response_content_failure::CreatePostContentFailure; use super::response_content_failure::CreatePostContentFailure::*; use super::response_content_success::CreatePostContentSuccess; -use crate::entities::CreatePost; -use crate::extensions::Resolve; -use blog_server_services::traits::post_service::PostService; -use screw_api::request::ApiRequest; -use screw_api::response::ApiResponse; -use screw_components::dyn_result::DResult; -use std::sync::Arc; -async fn handler( - new_post_data: DResult, - post_service: Arc>, +pub async fn http_handler( + (CreatePostRequestContent { + new_post_data, + post_service, + },): (CreatePostRequestContent,), ) -> Result { let base_post = new_post_data .map_err(|e| ValidationError { reason: e.to_string(), })? - .into(1); + .into(); let inserted_id = post_service .create_post(&base_post) @@ -37,12 +32,3 @@ async fn handler( Ok(created_post.into()) } - -pub async fn http_handler( - request: ApiRequest, -) -> ApiResponse -where - Extensions: Resolve>>, -{ - ApiResponse::from(handler(request.content.new_post_data, request.content.post_service).await) -} diff --git a/blog-server-api/src/entities/create_post.rs b/blog-server-api/src/entities/create_post.rs index 1f9bb2e..89ffc97 100644 --- a/blog-server-api/src/entities/create_post.rs +++ b/blog-server-api/src/entities/create_post.rs @@ -12,6 +12,7 @@ pub struct CreatePost { pub published: u8, pub content: Option, pub tags: Vec, + pub author_id: u64, //TODO replace with authorized user ID } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -21,9 +22,9 @@ pub struct ShortTag { } impl CreatePost { - pub fn into(self, author_id: u64) -> BasePost { + pub fn into(self) -> BasePost { BasePost { - author_id, + author_id: self.author_id, created_at: time_utils::now_as_secs(), title: self.title, slug: self.slug, From fe6d03cb9be1c9202c7c2d5b280a7d678afc8a4e Mon Sep 17 00:00:00 2001 From: Brantov Denis Date: Thu, 20 Jul 2023 11:57:03 +0200 Subject: [PATCH 8/8] feat: author id from auth token --- .../src/endpoints/create_post/handler.rs | 7 ++++++- .../src/endpoints/create_post/request_content.rs | 16 ++++++++++++---- .../create_post/response_content_failure.rs | 10 ++++++++++ blog-server-api/src/entities/create_post.rs | 5 ++--- blog-server-api/src/utils/auth.rs | 2 +- .../src/impls/rbatis_author_service.rs | 4 ++-- .../src/traits/author_service.rs | 4 ++-- 7 files changed, 35 insertions(+), 13 deletions(-) diff --git a/blog-server-api/src/endpoints/create_post/handler.rs b/blog-server-api/src/endpoints/create_post/handler.rs index 41e79ca..fd29182 100644 --- a/blog-server-api/src/endpoints/create_post/handler.rs +++ b/blog-server-api/src/endpoints/create_post/handler.rs @@ -7,13 +7,18 @@ pub async fn http_handler( (CreatePostRequestContent { new_post_data, post_service, + auth_author_future, },): (CreatePostRequestContent,), ) -> Result { + 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(); + .into(author.id); let inserted_id = post_service .create_post(&base_post) diff --git a/blog-server-api/src/endpoints/create_post/request_content.rs b/blog-server-api/src/endpoints/create_post/request_content.rs index a7ffaab..1f5e5f8 100644 --- a/blog-server-api/src/endpoints/create_post/request_content.rs +++ b/blog-server-api/src/endpoints/create_post/request_content.rs @@ -1,17 +1,21 @@ -use crate::{entities::CreatePost, extensions::Resolve}; -use blog_server_services::traits::post_service::PostService; +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_result::DResult; +use screw_components::{dyn_fn::DFuture, dyn_result::DResult}; use std::sync::Arc; pub struct CreatePostRequestContent { pub(super) new_post_data: DResult, pub(super) post_service: Arc>, + pub(super) auth_author_future: DFuture>, } impl ApiRequestContent for CreatePostRequestContent where - Extensions: Resolve>>, + Extensions: Resolve>> + Resolve>>, { type Data = CreatePost; @@ -19,6 +23,10 @@ where 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(), + )), } } } diff --git a/blog-server-api/src/endpoints/create_post/response_content_failure.rs b/blog-server-api/src/endpoints/create_post/response_content_failure.rs index db3e584..284ca3b 100644 --- a/blog-server-api/src/endpoints/create_post/response_content_failure.rs +++ b/blog-server-api/src/endpoints/create_post/response_content_failure.rs @@ -6,6 +6,7 @@ pub enum CreatePostContentFailure { ValidationError { reason: String }, AlreadyExists, InsertFailed, + Unauthorized { reason: String }, } impl ApiResponseContentBase for CreatePostContentFailure { @@ -16,6 +17,7 @@ impl ApiResponseContentBase for CreatePostContentFailure { } CreatePostContentFailure::AlreadyExists => &StatusCode::BAD_REQUEST, CreatePostContentFailure::ValidationError { reason: _ } => &StatusCode::BAD_REQUEST, + CreatePostContentFailure::Unauthorized { reason: _ } => &StatusCode::UNAUTHORIZED, CreatePostContentFailure::InsertFailed => &StatusCode::INTERNAL_SERVER_ERROR, } } @@ -30,6 +32,7 @@ impl ApiResponseContentFailure for CreatePostContentFailure { } CreatePostContentFailure::AlreadyExists => "CREATE_POST_ALREASY_EXISTS", CreatePostContentFailure::InsertFailed => "CREATE_POST_COULD_NOT_FIND_CREATED_POST", + CreatePostContentFailure::Unauthorized { reason: _ } => "CREATE_POST_UNAUTHORIZED", } } @@ -42,6 +45,13 @@ impl ApiResponseContentFailure for CreatePostContentFailure { "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) } diff --git a/blog-server-api/src/entities/create_post.rs b/blog-server-api/src/entities/create_post.rs index 89ffc97..1f9bb2e 100644 --- a/blog-server-api/src/entities/create_post.rs +++ b/blog-server-api/src/entities/create_post.rs @@ -12,7 +12,6 @@ pub struct CreatePost { pub published: u8, pub content: Option, pub tags: Vec, - pub author_id: u64, //TODO replace with authorized user ID } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -22,9 +21,9 @@ pub struct ShortTag { } impl CreatePost { - pub fn into(self) -> BasePost { + pub fn into(self, author_id: u64) -> BasePost { BasePost { - author_id: self.author_id, + author_id, created_at: time_utils::now_as_secs(), title: self.title, slug: self.slug, diff --git a/blog-server-api/src/utils/auth.rs b/blog-server-api/src/utils/auth.rs index 018bfc0..3e381a2 100644 --- a/blog-server-api/src/utils/auth.rs +++ b/blog-server-api/src/utils/auth.rs @@ -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, } diff --git a/blog-server-services/src/impls/rbatis_author_service.rs b/blog-server-services/src/impls/rbatis_author_service.rs index ac537c1..31a5b80 100644 --- a/blog-server-services/src/impls/rbatis_author_service.rs +++ b/blog-server-services/src/impls/rbatis_author_service.rs @@ -7,7 +7,7 @@ pub fn create_rbatis_author_service(rb: RBatis) -> Box { } 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`"}); @@ -67,7 +67,7 @@ impl AuthorService for RbatisAuthorService { async fn authors(&self, offset: &i64, limit: &i64) -> DResult> { Ok(Author::select_all_with_offset_and_limit(&mut self.rb.clone(), offset, limit).await?) } - async fn author_by_id(&self, id: &i64) -> DResult> { + async fn author_by_id(&self, id: &u64) -> DResult> { Ok(Author::select_by_id(&mut self.rb.clone(), id).await?) } async fn author_by_slug(&self, slug: &String) -> DResult> { diff --git a/blog-server-services/src/traits/author_service.rs b/blog-server-services/src/traits/author_service.rs index 2acf0f4..9bdda41 100644 --- a/blog-server-services/src/traits/author_service.rs +++ b/blog-server-services/src/traits/author_service.rs @@ -18,7 +18,7 @@ pub struct BaseAuthor { #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub struct Author { - pub id: i64, + pub id: u64, #[serde(flatten)] pub base: BaseAuthor, } @@ -34,7 +34,7 @@ pub trait AuthorService: Send + Sync { ) -> DResult>; async fn authors_count(&self) -> DResult; async fn authors(&self, offset: &i64, limit: &i64) -> DResult>; - async fn author_by_id(&self, id: &i64) -> DResult>; + async fn author_by_id(&self, id: &u64) -> DResult>; async fn author_by_slug(&self, slug: &String) -> DResult>; async fn create_author(&self, author: &BaseAuthor) -> DResult; }