• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

doitsu2014 / my-cms / 14187034714

01 Apr 2025 04:12AM UTC coverage: 59.597% (+20.5%) from 39.058%
14187034714

push

github

web-flow
Feature/support multi language (#19)

* Enhance entity structure: add category and post translations, update migration dependencies, and improve README guidelines

* Add support for category translations: update create and modify handlers, requests, and tests

add TODO

* Add category translations support: update create, modify, and read handlers, and adjust request structures

* Refactor category translation requests: remove slug field and update handlers to set category ID for translations

* Add support for post translations: update create and modify requests, handlers, and models

* Update CI configuration and scripts for improved coverage reporting and toolchain management

* Update coverage configuration and scripts for improved reporting and toolchain management

* Update CI and coverage configurations for improved reporting and ignore patterns

* Update CI configuration and coverage scripts for improved reporting and cleanup

* Remove unused coverage step from CI configuration

* Update CI configuration to use fixed lcov report paths for coverage uploads

197 of 396 new or added lines in 19 files covered. (49.75%)

71 existing lines in 11 files now uncovered.

975 of 1636 relevant lines covered (59.6%)

26.2 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

70.37
/application_core/src/commands/post/create/create_handler.rs
1
use sea_orm::{
2
    DatabaseConnection, EntityTrait, IntoActiveModel, TransactionError, TransactionTrait,
3
};
4
use std::sync::Arc;
5
use tracing::instrument;
6
use uuid::Uuid;
7

8
use crate::{
9
    commands::tag::create::create_handler::{TagCreateHandler, TagCreateHandlerTrait},
10
    common::{app_error::AppError, datetime_generator::generate_vietnam_now},
11
    entities::{post_tags, post_translations, posts},
12
    Posts,
13
};
14

15
use super::create_request::CreatePostRequest;
16

17
pub trait PostCreateHandlerTrait {
18
    fn handle_create_post(
19
        &self,
20
        body: CreatePostRequest,
21
        actor_email: Option<String>,
22
    ) -> impl std::future::Future<Output = Result<Uuid, AppError>>;
23
}
24

25
#[derive(Debug)]
26
pub struct PostCreateHandler {
27
    pub db: Arc<DatabaseConnection>,
28
}
29

30
impl PostCreateHandlerTrait for PostCreateHandler {
31
    #[instrument]
32
    async fn handle_create_post(
33
        &self,
34
        body: CreatePostRequest,
35
        actor_email: Option<String>,
36
    ) -> Result<Uuid, AppError> {
37
        let tag_create_handler = TagCreateHandler {
38
            db: self.db.clone(),
39
        };
40
        // Prepare Category
41
        let model: posts::Model = body.into_model();
42
        let model = posts::Model {
43
            created_by: actor_email.clone().unwrap_or("System".to_string()),
44
            created_at: generate_vietnam_now(),
45
            ..model
46
        };
47
        let create_model = posts::ActiveModel {
48
            ..model.into_active_model()
49
        };
50

51
        // Prepare Tags
52
        let tags: Vec<String> = body.tag_names.unwrap_or_default();
53

54
        // Prepare Translations
55
        let translations = body.translations.unwrap_or_default();
56

57
        let result: Result<Uuid, TransactionError<AppError>> = self
58
            .db
59
            .as_ref()
60
            .transaction::<_, Uuid, AppError>(|tx| {
6✔
61
                Box::pin(async move {
6✔
62
                    // Insert New Tags
6✔
63
                    let create_tags_response_task =
6✔
64
                        tag_create_handler.handle_create_tags_in_transaction(tags, actor_email, tx);
6✔
65

66
                    // Insert Category
67
                    let inserted_post = Posts::insert(create_model)
6✔
68
                        .exec(tx)
6✔
69
                        .await
6✔
70
                        .map_err(|e| e.into())?;
6✔
71

72
                    // Combine New Tag Ids and Existing Tag Ids
73
                    let create_tags_response = create_tags_response_task.await?;
6✔
74
                    let all_tag_ids = create_tags_response
6✔
75
                        .existing_tag_ids
6✔
76
                        .into_iter()
6✔
77
                        .chain(create_tags_response.new_tag_ids)
6✔
78
                        .collect::<Vec<Uuid>>();
6✔
79

6✔
80
                    // Insert Category Tags
6✔
81
                    if !all_tag_ids.is_empty() {
6✔
82
                        let post_tags = all_tag_ids
6✔
83
                            .iter()
6✔
84
                            .map(|tag_id| {
30✔
85
                                post_tags::Model {
30✔
86
                                    post_id: inserted_post.last_insert_id,
30✔
87
                                    tag_id: tag_id.to_owned(),
30✔
88
                                }
30✔
89
                                .into_active_model()
30✔
90
                            })
30✔
91
                            .collect::<Vec<post_tags::ActiveModel>>();
6✔
92

6✔
93
                        post_tags::Entity::insert_many(post_tags)
6✔
94
                            .exec(tx)
6✔
95
                            .await
6✔
96
                            .map_err(|e| e.into())?;
6✔
UNCOV
97
                    }
×
98

99
                    // Insert Post Translations
100
                    if !translations.is_empty() {
6✔
NEW
101
                        let post_translations = translations
×
NEW
102
                            .into_iter()
×
NEW
103
                            .map(|translation| {
×
NEW
104
                                post_translations::Model {
×
NEW
105
                                    post_id: inserted_post.last_insert_id,
×
NEW
106
                                    ..translation.into_model()
×
NEW
107
                                }
×
NEW
108
                                .into_active_model()
×
NEW
109
                            })
×
NEW
110
                            .collect::<Vec<post_translations::ActiveModel>>();
×
NEW
111

×
NEW
112
                        post_translations::Entity::insert_many(post_translations)
×
NEW
113
                            .exec(tx)
×
NEW
114
                            .await
×
NEW
115
                            .map_err(|e| e.into())?;
×
116
                    }
6✔
117

118

119
                    Ok(inserted_post.last_insert_id)
6✔
120
                })
6✔
121
            })
6✔
122
            .await;
123

124
        match result {
125
            Ok(inserted_id) => Ok(inserted_id),
126
            Err(e) => Err(e.into()),
127
        }
128
    }
129

130
}
131

132
#[cfg(test)]
133
mod tests {
134
    use std::sync::Arc;
135
    use test_helpers::{setup_test_space, ContainerAsyncPostgresEx};
136

137
    use crate::commands::{
138
        category::{
139
            create::create_handler::{CategoryCreateHandler, CategoryCreateHandlerTrait},
140
            test::fake_create_category_request,
141
        },
142
        post::{
143
            create::create_handler::{PostCreateHandler, PostCreateHandlerTrait},
144
            read::read_handler::{PostReadHandler, PostReadHandlerTrait},
145
            test::fake_create_post_request,
146
        },
147
    };
148

149
    #[async_std::test]
150
    async fn handle_create_post_testcase_successfully() {
151
        let beginning_test_timestamp = chrono::Utc::now();
152
        let test_space = setup_test_space().await;
153
        let database = test_space.postgres.get_database_connection().await;
154

155
        let arc_conn = Arc::new(database);
156
        let category_create_handler = CategoryCreateHandler {
157
            db: arc_conn.clone(),
158
        };
159
        let post_create_handler = PostCreateHandler {
160
            db: arc_conn.clone(),
161
        };
162
        let post_read_handler = PostReadHandler {
163
            db: arc_conn.clone(),
164
        };
165
        let create_category_request = fake_create_category_request(5);
166
        let created_category_id = category_create_handler
167
            .handle_create_category_with_tags(create_category_request, None)
168
            .await
169
            .unwrap();
170
        let create_post_request = fake_create_post_request(created_category_id, 5);
171
        let result = post_create_handler
172
            .handle_create_post(create_post_request, None)
173
            .await
174
            .unwrap();
175

176
        let db_posts = post_read_handler.handle_get_all_posts().await.unwrap();
177
        let first = db_posts.first().unwrap();
178

179
        assert_eq!(result, first.id);
180
        assert!(first.created_by == "System");
181
        assert!(first.created_at >= beginning_test_timestamp);
182
        assert!(first.row_version == 1);
183
        assert!(first.tags.len() == 5);
184
    }
185
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc