From 8f91d815bc4f4e91676d791e13d3fd42bf2d837c Mon Sep 17 00:00:00 2001 From: SquidSpirit Date: Sun, 12 Oct 2025 16:36:48 +0800 Subject: [PATCH] feat: add validation for semantic ID in post and update error handling --- backend/Cargo.lock | 9 +++++---- backend/Cargo.toml | 2 +- backend/feature/post/Cargo.toml | 1 + .../post/src/application/error/post_error.rs | 5 +++++ .../use_case/create_post_use_case.rs | 5 ++--- .../use_case/update_post_use_case.rs | 1 + .../feature/post/src/domain/entity/post.rs | 9 +++++++++ .../post/src/domain/entity/post_info.rs | 18 ++++++++++++++++++ .../src/framework/web/create_label_handler.rs | 18 +++++++++++------- .../src/framework/web/create_post_handler.rs | 19 ++++++++++++------- .../framework/web/get_all_labels_handler.rs | 17 ++++++++++------- .../web/get_all_post_info_handler.rs | 17 ++++++++++------- .../framework/web/get_post_by_id_handler.rs | 5 +++++ .../src/framework/web/update_label_handler.rs | 5 +++++ .../src/framework/web/update_post_handler.rs | 1 + 15 files changed, 96 insertions(+), 36 deletions(-) diff --git a/backend/Cargo.lock b/backend/Cargo.lock index fc70e43..b9189d6 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -2407,6 +2407,7 @@ dependencies = [ "auth", "chrono", "common", + "regex", "sentry", "serde", "sqlx", @@ -2638,9 +2639,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.1" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "4a52d8d02cacdb176ef4678de6c052efb4b3da14b78e4db683a4252762be5433" dependencies = [ "aho-corasick", "memchr", @@ -2650,9 +2651,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "722166aa0d7438abbaa4d5cc2c649dac844e8c56d82fb3d33e9c34b5cd268fc6" dependencies = [ "aho-corasick", "memchr", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 3b56e47..d3092f2 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -5,7 +5,6 @@ members = [ "feature/common", "feature/image", "feature/post", - "feature/common", ] resolver = "2" @@ -33,6 +32,7 @@ openidconnect = { version = "4.0.1", features = [ "reqwest-blocking", ] } percent-encoding = "2.3.1" +regex = "1.12.1" sentry = { version = "0.42.0", features = ["actix", "anyhow"] } serde = { version = "1.0.219", features = ["derive"] } sqlx = { version = "0.8.5", features = [ diff --git a/backend/feature/post/Cargo.toml b/backend/feature/post/Cargo.toml index afd48c7..aecb5fd 100644 --- a/backend/feature/post/Cargo.toml +++ b/backend/feature/post/Cargo.toml @@ -8,6 +8,7 @@ actix-web.workspace = true anyhow.workspace = true async-trait.workspace = true chrono.workspace = true +regex.workspace = true sentry.workspace = true serde.workspace = true sqlx.workspace = true diff --git a/backend/feature/post/src/application/error/post_error.rs b/backend/feature/post/src/application/error/post_error.rs index 58ce49a..4d13e4e 100644 --- a/backend/feature/post/src/application/error/post_error.rs +++ b/backend/feature/post/src/application/error/post_error.rs @@ -4,6 +4,7 @@ use std::fmt::Display; pub enum PostError { NotFound, Unauthorized, + InvalidSemanticId, Unexpected(anyhow::Error), } @@ -12,6 +13,10 @@ impl Display for PostError { match self { PostError::NotFound => write!(f, "Post not found"), PostError::Unauthorized => write!(f, "Unauthorized access to post"), + PostError::InvalidSemanticId => write!( + f, + "Semantic ID shouldn't be numeric and must conform to `^[0-9a-zA-Z_\\-]+$`" + ), PostError::Unexpected(e) => write!(f, "Unexpected error: {}", e), } } diff --git a/backend/feature/post/src/application/use_case/create_post_use_case.rs b/backend/feature/post/src/application/use_case/create_post_use_case.rs index 892f9a7..2e87793 100644 --- a/backend/feature/post/src/application/use_case/create_post_use_case.rs +++ b/backend/feature/post/src/application/use_case/create_post_use_case.rs @@ -25,8 +25,7 @@ impl CreatePostUseCaseImpl { #[async_trait] impl CreatePostUseCase for CreatePostUseCaseImpl { async fn execute(&self, post: Post, label_ids: &[i32]) -> Result { - self.post_repository - .create_post(post, label_ids) - .await + post.validate()?; + self.post_repository.create_post(post, label_ids).await } } diff --git a/backend/feature/post/src/application/use_case/update_post_use_case.rs b/backend/feature/post/src/application/use_case/update_post_use_case.rs index 295cdba..ce5b6d9 100644 --- a/backend/feature/post/src/application/use_case/update_post_use_case.rs +++ b/backend/feature/post/src/application/use_case/update_post_use_case.rs @@ -25,6 +25,7 @@ impl UpdatePostUseCaseImpl { #[async_trait] impl UpdatePostUseCase for UpdatePostUseCaseImpl { async fn execute(&self, post: Post, label_ids: &[i32]) -> Result<(), PostError> { + post.validate()?; self.post_repository.update_post(post, label_ids).await } } diff --git a/backend/feature/post/src/domain/entity/post.rs b/backend/feature/post/src/domain/entity/post.rs index a968187..47e694a 100644 --- a/backend/feature/post/src/domain/entity/post.rs +++ b/backend/feature/post/src/domain/entity/post.rs @@ -1,3 +1,5 @@ +use crate::application::error::post_error::PostError; + use super::post_info::PostInfo; pub struct Post { @@ -5,3 +7,10 @@ pub struct Post { pub info: PostInfo, pub content: String, } + +impl Post { + pub fn validate(&self) -> Result<(), PostError> { + self.info.validate()?; + Ok(()) + } +} diff --git a/backend/feature/post/src/domain/entity/post_info.rs b/backend/feature/post/src/domain/entity/post_info.rs index bac3c34..922be11 100644 --- a/backend/feature/post/src/domain/entity/post_info.rs +++ b/backend/feature/post/src/domain/entity/post_info.rs @@ -1,4 +1,7 @@ use chrono::{DateTime, Utc}; +use regex::Regex; + +use crate::application::error::post_error::PostError; use super::label::Label; @@ -11,3 +14,18 @@ pub struct PostInfo { pub labels: Vec