refactor(lib): refresh export views and issues crate (features, handler, tests)
zhenyi committed
Modified lib/export/issues/issue.rs lib/export/issues/issue.rs +5 −4 View lib/export/issues/issue.rs
@@ -1,3 +1,4 @@
1use crate::user::basic::UserBasicInfo;
12use db::issue::issue as issue_entity;
23use serde::{Deserialize, Serialize};
34use time::OffsetDateTime;
@@ -24,7 +25,7 @@
2425 pub id: Uuid,
2526 pub collection_id: Uuid,
2627 pub autoincrement_id: i32,
27 pub author_id: Uuid,
28 pub author: Option<UserBasicInfo>,
2829 pub title: String,
2930 pub description: Option<String>,
3031 pub body: String,
@@ -35,8 +36,8 @@
3536 pub closed_at: Option<String>,
3637}
3738
38impl From<issue_entity::Model> for IssueView {
39 fn from(m: issue_entity::Model) -> Self {
39impl IssueView {
40 pub fn from_model(m: issue_entity::Model, author: Option<UserBasicInfo>) -> Self {
4041 let state = if m.closed_at.is_some() {
4142 IssueState::Closed
4243 } else {
@@ -46,7 +47,7 @@
4647 id: m.id,
4748 collection_id: m.collection_id,
4849 autoincrement_id: m.autoincrement_id,
49 author_id: m.author_id,
50 author,
5051 title: m.title,
5152 description: m.description,
5253 body: m.body,
Modified lib/export/issues/issue_attachment.rs lib/export/issues/issue_attachment.rs +5 −4 View lib/export/issues/issue_attachment.rs
@@ -1,3 +1,4 @@
1use crate::user::basic::UserBasicInfo;
12use db::attachment::attachments as att_entity;
23use db::attachment::attachments::OwnerType;
34use serde::{Deserialize, Serialize};
@@ -19,12 +20,12 @@
1920 pub content_type: String,
2021 pub size: i64,
2122 pub url: String,
22 pub uploaded_by: Option<Uuid>,
23 pub uploaded_by: Option<UserBasicInfo>,
2324 pub created_at: String,
2425}
2526
26impl From<att_entity::Model> for AttachmentView {
27 fn from(m: att_entity::Model) -> Self {
27impl AttachmentView {
28 pub fn from_model(m: att_entity::Model, uploaded_by: Option<UserBasicInfo>) -> Self {
2829 Self {
2930 id: m.id,
3031 owner_type: format!("{:?}", m.owner_type),
@@ -33,7 +34,7 @@
3334 content_type: m.content_type,
3435 size: m.size,
3536 url: m.url,
36 uploaded_by: m.uploaded_by,
37 uploaded_by,
3738 created_at: fmt_ts(m.created_at),
3839 }
3940 }
Modified lib/export/issues/issue_event.rs lib/export/issues/issue_event.rs +5 −4 View lib/export/issues/issue_event.rs
@@ -1,3 +1,4 @@
1use crate::user::basic::UserBasicInfo;
12use db::issue::issue_event_line as event_entity;
23use serde::{Deserialize, Serialize};
34use time::OffsetDateTime;
@@ -13,7 +14,7 @@
1314pub struct EventView {
1415 pub id: Uuid,
1516 pub issue_id: Option<Uuid>,
16 pub user_id: Uuid,
17 pub user: Option<UserBasicInfo>,
1718 pub event_type: String,
1819 pub comment_id: Option<Uuid>,
1920 pub commit_sha: Option<String>,
@@ -25,12 +26,12 @@
2526 pub created_at: String,
2627}
2728
28impl From<event_entity::Model> for EventView {
29 fn from(m: event_entity::Model) -> Self {
29impl EventView {
30 pub fn from_model(m: event_entity::Model, user: Option<UserBasicInfo>) -> Self {
3031 Self {
3132 id: m.id,
3233 issue_id: m.issue_id,
33 user_id: m.user_id,
34 user,
3435 event_type: m.event_type,
3536 comment_id: m.comment_id,
3637 commit_sha: m.commit_sha,
Modified lib/export/issues/issue_member.rs lib/export/issues/issue_member.rs +5 −4 View lib/export/issues/issue_member.rs
@@ -1,3 +1,4 @@
1use crate::user::basic::UserBasicInfo;
12use db::issue::issue_member as member_entity;
23use serde::{Deserialize, Serialize};
34use time::OffsetDateTime;
@@ -16,18 +17,18 @@
1617pub struct MemberView {
1718 pub id: Uuid,
1819 pub issue_id: Uuid,
19 pub user_id: Uuid,
20 pub user: Option<UserBasicInfo>,
2021 pub kind: String,
2122 pub created_at: String,
2223 pub updated_at: Option<String>,
2324}
2425
25impl From<member_entity::Model> for MemberView {
26 fn from(m: member_entity::Model) -> Self {
26impl MemberView {
27 pub fn from_model(m: member_entity::Model, user: Option<UserBasicInfo>) -> Self {
2728 Self {
2829 id: m.id,
2930 issue_id: m.issue_id,
30 user_id: m.user_id,
31 user,
3132 kind: format!("{:?}", m.kind),
3233 created_at: fmt_ts(m.created_at),
3334 updated_at: fmt_ts_opt(m.updated_at),
Modified lib/export/issues/issue_reaction.rs lib/export/issues/issue_reaction.rs +5 −4 View lib/export/issues/issue_reaction.rs
@@ -1,3 +1,4 @@
1use crate::user::basic::UserBasicInfo;
12use db::issue::issue_reactions as reaction_entity;
23use serde::{Deserialize, Serialize};
34use time::OffsetDateTime;
@@ -21,13 +22,13 @@
2122 pub id: Uuid,
2223 pub scope: ReactionScope,
2324 pub target_id: Uuid,
24 pub user_id: Uuid,
25 pub user: Option<UserBasicInfo>,
2526 pub emoji: String,
2627 pub created_at: String,
2728}
2829
29impl From<reaction_entity::Model> for ReactionView {
30 fn from(m: reaction_entity::Model) -> Self {
30impl ReactionView {
31 pub fn from_model(m: reaction_entity::Model, user: Option<UserBasicInfo>) -> Self {
3132 let scope = if m.comment_id.is_some() {
3233 ReactionScope::Comment
3334 } else {
@@ -41,7 +42,7 @@
4142 id: m.id,
4243 scope,
4344 target_id,
44 user_id: m.user_id,
45 user,
4546 emoji: m.emoji,
4647 created_at: fmt_ts(m.created_at),
4748 }
Modified lib/export/issues/issue_stopwatch.rs lib/export/issues/issue_stopwatch.rs +5 −4 View lib/export/issues/issue_stopwatch.rs
@@ -1,3 +1,4 @@
1use crate::user::basic::UserBasicInfo;
12use db::issue::stopwatch as sw_entity;
23use serde::{Deserialize, Serialize};
34use time::OffsetDateTime;
@@ -13,17 +14,17 @@
1314pub struct StopwatchView {
1415 pub id: Uuid,
1516 pub issue_id: Uuid,
16 pub user_id: Uuid,
17 pub user: Option<UserBasicInfo>,
1718 pub started_at: String,
1819 pub created_at: String,
1920}
2021
21impl From<sw_entity::Model> for StopwatchView {
22 fn from(m: sw_entity::Model) -> Self {
22impl StopwatchView {
23 pub fn from_model(m: sw_entity::Model, user: Option<UserBasicInfo>) -> Self {
2324 Self {
2425 id: m.id,
2526 issue_id: m.issue_id,
26 user_id: m.user_id,
27 user,
2728 started_at: fmt_ts(m.started_at),
2829 created_at: fmt_ts(m.created_at),
2930 }
Modified lib/export/issues/issue_tracked_time.rs lib/export/issues/issue_tracked_time.rs +5 −4 View lib/export/issues/issue_tracked_time.rs
@@ -1,3 +1,4 @@
1use crate::user::basic::UserBasicInfo;
12use db::issue::tracked_time as tt_entity;
23use serde::{Deserialize, Serialize};
34use time::OffsetDateTime;
@@ -13,17 +14,17 @@
1314pub struct TrackedTimeView {
1415 pub id: Uuid,
1516 pub issue_id: Uuid,
16 pub user_id: Uuid,
17 pub user: Option<UserBasicInfo>,
1718 pub seconds: i64,
1819 pub created_at: String,
1920}
2021
21impl From<tt_entity::Model> for TrackedTimeView {
22 fn from(m: tt_entity::Model) -> Self {
22impl TrackedTimeView {
23 pub fn from_model(m: tt_entity::Model, user: Option<UserBasicInfo>) -> Self {
2324 Self {
2425 id: m.id,
2526 issue_id: m.issue_id,
26 user_id: m.user_id,
27 user,
2728 seconds: m.seconds,
2829 created_at: fmt_ts(m.created_at),
2930 }
Modified lib/export/issues/issue_user.rs lib/export/issues/issue_user.rs +5 −4 View lib/export/issues/issue_user.rs
@@ -1,3 +1,4 @@
1use crate::user::basic::UserBasicInfo;
12use db::issue::issue_user as issue_user_entity;
23use serde::{Deserialize, Serialize};
34use time::OffsetDateTime;
@@ -12,18 +13,18 @@
1213#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1314pub struct IssueUserView {
1415 pub id: Uuid,
15 pub user_id: Uuid,
16 pub user: Option<UserBasicInfo>,
1617 pub issue_id: Uuid,
1718 pub is_read: bool,
1819 pub is_mentioned: bool,
1920 pub created_at: String,
2021}
2122
22impl From<issue_user_entity::Model> for IssueUserView {
23 fn from(m: issue_user_entity::Model) -> Self {
23impl IssueUserView {
24 pub fn from_model(m: issue_user_entity::Model, user: Option<UserBasicInfo>) -> Self {
2425 Self {
2526 id: m.id,
26 user_id: m.uid,
27 user,
2728 issue_id: m.issue_id,
2829 is_read: m.is_read,
2930 is_mentioned: m.is_mentioned,
Modified lib/export/issues/issue_watch.rs lib/export/issues/issue_watch.rs +5 −4 View lib/export/issues/issue_watch.rs
@@ -1,3 +1,4 @@
1use crate::user::basic::UserBasicInfo;
12use db::issue::issue_watch as watch_entity;
23use serde::{Deserialize, Serialize};
34use time::OffsetDateTime;
@@ -16,18 +17,18 @@
1617pub struct WatchView {
1718 pub id: Uuid,
1819 pub issue_id: Option<Uuid>,
19 pub user_id: Uuid,
20 pub user: Option<UserBasicInfo>,
2021 pub mode: String,
2122 pub created_at: String,
2223 pub updated_at: Option<String>,
2324}
2425
25impl From<watch_entity::Model> for WatchView {
26 fn from(m: watch_entity::Model) -> Self {
26impl WatchView {
27 pub fn from_model(m: watch_entity::Model, user: Option<UserBasicInfo>) -> Self {
2728 Self {
2829 id: m.id,
2930 issue_id: m.issue_id,
30 user_id: m.user_id,
31 user,
3132 mode: format!("{:?}", m.mode),
3233 created_at: fmt_ts(m.created_at),
3334 updated_at: fmt_ts_opt(m.updated_at),
Modified lib/export/post/views.rs lib/export/post/views.rs +33 −24 View lib/export/post/views.rs
@@ -1,3 +1,4 @@
1use crate::user::basic::UserBasicInfo;
12use db::post::post as post_entity;
23use db::post::post_labels as label_entity;
34use db::post::post_mentions as mention_entity;
@@ -113,34 +114,38 @@
113114 pub id: Uuid,
114115 pub collection_id: Uuid,
115116 pub number: i64,
116 pub author_id: Uuid,
117 pub author: Option<UserBasicInfo>,
117118 pub title: String,
118119 pub body: String,
119120 pub status: PostStatusView,
120121 pub is_pinned: bool,
121122 pub reply_count: i32,
122123 pub last_reply_at: Option<String>,
123 pub last_reply_by: Option<Uuid>,
124 pub last_reply_by: Option<UserBasicInfo>,
124125 pub created_at: String,
125126 pub updated_at: Option<String>,
126127 pub closed_at: Option<String>,
127128 pub archived_at: Option<String>,
128129}
129130
130impl From<post_entity::Model> for PostView {
131 fn from(m: post_entity::Model) -> Self {
131impl PostView {
132 pub fn from_model(
133 m: post_entity::Model,
134 author: Option<UserBasicInfo>,
135 last_reply_by: Option<UserBasicInfo>,
136 ) -> Self {
132137 Self {
133138 id: m.id,
134139 collection_id: m.collection_id,
135140 number: m.autoincrement_id as i64,
136 author_id: m.author_id,
141 author,
137142 title: m.title,
138143 body: m.body,
139144 status: PostStatusView::from(m.status),
140145 is_pinned: m.is_pinned,
141146 reply_count: m.reply_count,
142147 last_reply_at: fmt_ts_opt(m.last_reply_at),
143 last_reply_by: m.last_reply_by,
148 last_reply_by,
144149 created_at: fmt_ts(m.created_at),
145150 updated_at: fmt_ts_opt(m.updated_at),
146151 closed_at: fmt_ts_opt(m.closed_at),
@@ -182,7 +187,7 @@
182187pub struct PostReplyView {
183188 pub id: Uuid,
184189 pub post_id: Uuid,
185 pub author_id: Uuid,
190 pub author: Option<UserBasicInfo>,
186191 pub body: String,
187192 pub parent_reply_id: Option<Uuid>,
188193 pub is_pinned: bool,
@@ -190,15 +195,19 @@
190195 pub created_at: String,
191196 pub updated_at: Option<String>,
192197 pub deleted_at: Option<String>,
193 pub deleted_by: Option<Uuid>,
198 pub deleted_by: Option<UserBasicInfo>,
194199}
195200
196impl From<reply_entity::Model> for PostReplyView {
197 fn from(m: reply_entity::Model) -> Self {
201impl PostReplyView {
202 pub fn from_model(
203 m: reply_entity::Model,
204 author: Option<UserBasicInfo>,
205 deleted_by: Option<UserBasicInfo>,
206 ) -> Self {
198207 Self {
199208 id: m.id,
200209 post_id: m.post_id,
201 author_id: m.author_id,
210 author,
202211 body: m.body,
203212 parent_reply_id: m.parent_reply_id,
204213 is_pinned: m.is_pinned,
@@ -206,7 +215,7 @@
206215 created_at: fmt_ts(m.created_at),
207216 updated_at: fmt_ts_opt(m.updated_at),
208217 deleted_at: fmt_ts_opt(m.deleted_at),
209 deleted_by: m.deleted_by,
218 deleted_by,
210219 }
211220 }
212221}
@@ -272,18 +281,18 @@
272281 pub id: Uuid,
273282 pub target: PostTargetView,
274283 pub target_id: Uuid,
275 pub user_id: Uuid,
284 pub user: Option<UserBasicInfo>,
276285 pub emoji: String,
277286 pub created_at: String,
278287}
279288
280impl From<reaction_entity::Model> for PostReactionView {
281 fn from(m: reaction_entity::Model) -> Self {
289impl PostReactionView {
290 pub fn from_model(m: reaction_entity::Model, user: Option<UserBasicInfo>) -> Self {
282291 Self {
283292 id: m.id,
284293 target: PostTargetView::from(m.target),
285294 target_id: m.target_id,
286 user_id: m.user_id,
295 user,
287296 emoji: m.emoji,
288297 created_at: fmt_ts(m.created_at),
289298 }
@@ -300,7 +309,7 @@
300309 pub id: Uuid,
301310 pub target: PostTargetView,
302311 pub target_id: Uuid,
303 pub user_id: Option<Uuid>,
312 pub user: Option<UserBasicInfo>,
304313 pub role_id: Option<Uuid>,
305314 pub channel_id: Option<Uuid>,
306315 pub is_everyone: bool,
@@ -308,13 +317,13 @@
308317 pub created_at: String,
309318}
310319
311impl From<mention_entity::Model> for PostMentionView {
312 fn from(m: mention_entity::Model) -> Self {
320impl PostMentionView {
321 pub fn from_model(m: mention_entity::Model, user: Option<UserBasicInfo>) -> Self {
313322 Self {
314323 id: m.id,
315324 target: PostTargetView::from(m.target),
316325 target_id: m.target_id,
317 user_id: m.user_id,
326 user,
318327 role_id: m.role_id,
319328 channel_id: m.channel_id,
320329 is_everyone: m.is_everyone,
@@ -336,18 +345,18 @@
336345pub struct PostWatchView {
337346 pub id: Uuid,
338347 pub post_id: Uuid,
339 pub user_id: Uuid,
348 pub user: Option<UserBasicInfo>,
340349 pub mode: String,
341350 pub created_at: String,
342351 pub updated_at: Option<String>,
343352}
344353
345impl From<watch_entity::Model> for PostWatchView {
346 fn from(m: watch_entity::Model) -> Self {
354impl PostWatchView {
355 pub fn from_model(m: watch_entity::Model, user: Option<UserBasicInfo>) -> Self {
347356 Self {
348357 id: m.id,
349358 post_id: m.post_id,
350 user_id: m.user_id,
359 user,
351360 mode: format!("{:?}", m.mode),
352361 created_at: fmt_ts(m.created_at),
353362 updated_at: fmt_ts_opt(m.updated_at),
Modified lib/export/project/activities.rs lib/export/project/activities.rs +20 −1 View lib/export/project/activities.rs
@@ -1,3 +1,4 @@
1use crate::user::basic::UserBasicInfo;
12use serde::{Deserialize, Serialize};
23use serde_json::Value;
34use time::OffsetDateTime;
@@ -8,7 +9,7 @@
89pub struct ActivityView {
910 pub id: Uuid,
1011 pub project_id: Uuid,
11 pub actor_id: Uuid,
12 pub actor: Option<UserBasicInfo>,
1213 pub verb: String,
1314 pub target_kind: String,
1415 pub target_id: Option<Uuid>,
@@ -16,6 +17,24 @@
1617 pub created_at: OffsetDateTime,
1718}
1819
20impl ActivityView {
21 pub fn from_model(
22 m: db::project::project_activities::Model,
23 actor: Option<UserBasicInfo>,
24 ) -> Self {
25 Self {
26 id: m.id,
27 project_id: m.project_id,
28 actor,
29 verb: m.verb,
30 target_kind: m.target_kind,
31 target_id: m.target_id,
32 payload: m.payload,
33 created_at: m.created_at,
34 }
35 }
36}
37
1938#[derive(Debug, Clone, Deserialize, ToSchema, IntoParams)]
2039#[into_params(parameter_in = Query)]
2140pub struct ListActivityQuery {
Modified lib/export/project/documents.rs lib/export/project/documents.rs +25 −2 View lib/export/project/documents.rs
@@ -1,3 +1,4 @@
1use crate::user::basic::UserBasicInfo;
12use serde::{Deserialize, Serialize};
23use time::OffsetDateTime;
34use utoipa::ToSchema;
@@ -27,8 +28,8 @@
2728 pub id: Uuid,
2829 pub project_id: Uuid,
2930 pub parent_id: Option<Uuid>,
30 pub creator_id: Uuid,
31 pub last_editor_id: Option<Uuid>,
31 pub creator: Option<UserBasicInfo>,
32 pub last_editor: Option<UserBasicInfo>,
3233 pub title: String,
3334 pub slug: String,
3435 pub content: String,
@@ -37,6 +38,28 @@
3738 pub updated_at: Option<OffsetDateTime>,
3839}
3940
41impl DocumentView {
42 pub fn from_model(
43 m: db::project::project_documents::Model,
44 creator: Option<UserBasicInfo>,
45 last_editor: Option<UserBasicInfo>,
46 ) -> Self {
47 Self {
48 id: m.id,
49 project_id: m.project_id,
50 parent_id: m.parent_id,
51 creator,
52 last_editor,
53 title: m.title,
54 slug: m.slug,
55 content: m.content,
56 icon: m.icon,
57 created_at: m.created_at,
58 updated_at: m.updated_at,
59 }
60 }
61}
62
4063#[derive(Debug, Clone, Serialize, ToSchema)]
4164pub struct DocumentPage {
4265 pub total: u64,
Modified lib/export/project/favorites.rs lib/export/project/favorites.rs +17 −1 View lib/export/project/favorites.rs
@@ -1,3 +1,4 @@
1use crate::user::basic::UserBasicInfo;
12use serde::{Deserialize, Serialize};
23use time::OffsetDateTime;
34use utoipa::ToSchema;
@@ -12,11 +13,26 @@
1213pub struct FavoriteView {
1314 pub id: Uuid,
1415 pub project_id: Uuid,
15 pub user_id: Uuid,
16 pub user: Option<UserBasicInfo>,
1617 pub folder: Option<String>,
1718 pub created_at: OffsetDateTime,
1819}
1920
21impl FavoriteView {
22 pub fn from_model(
23 m: db::project::project_favorites::Model,
24 user: Option<UserBasicInfo>,
25 ) -> Self {
26 Self {
27 id: m.id,
28 project_id: m.project_id,
29 user,
30 folder: m.folder,
31 created_at: m.created_at,
32 }
33 }
34}
35
2036#[derive(Debug, Clone, Serialize, ToSchema)]
2137pub struct FavoritePage {
2238 pub total: u64,
Modified lib/export/project/lifecycle.rs lib/export/project/lifecycle.rs +32 −2 View lib/export/project/lifecycle.rs
@@ -1,3 +1,4 @@
1use crate::user::basic::UserBasicInfo;
12use db::project::project_state::ProjectState as DbState;
23use serde::{Deserialize, Serialize};
34use time::OffsetDateTime;
@@ -82,8 +83,8 @@
8283pub struct ProjectView {
8384 pub id: Uuid,
8485 pub space_id: Uuid,
85 pub creator_id: Uuid,
86 pub lead_id: Option<Uuid>,
86 pub creator: Option<UserBasicInfo>,
87 pub lead: Option<UserBasicInfo>,
8788 pub name: String,
8889 pub description: Option<String>,
8990 pub icon: Option<String>,
@@ -100,6 +101,35 @@
100101 pub updated_at: Option<OffsetDateTime>,
101102}
102103
104impl ProjectView {
105 pub fn from_model(
106 m: db::project::project::Model,
107 creator: Option<UserBasicInfo>,
108 lead: Option<UserBasicInfo>,
109 ) -> Self {
110 Self {
111 id: m.id,
112 space_id: m.space_id,
113 creator,
114 lead,
115 name: m.name,
116 description: m.description,
117 icon: m.icon,
118 color: m.color,
119 state: ProjectState::from(m.state),
120 target_date: m.target_date,
121 started_at: m.started_at,
122 completed_at: m.completed_at,
123 cancelled_at: m.cancelled_at,
124 health: ProjectHealth::from(m.health),
125 sort_order: m.sort_order,
126 starred: m.starred,
127 created_at: m.created_at,
128 updated_at: m.updated_at,
129 }
130 }
131}
132
103133#[derive(Debug, Clone, Serialize, ToSchema)]
104134pub struct ProjectPage {
105135 pub total: u64,
Modified lib/export/project/views.rs lib/export/project/views.rs +25 −1 View lib/export/project/views.rs
@@ -1,3 +1,4 @@
1use crate::user::basic::UserBasicInfo;
12use serde::{Deserialize, Serialize};
23use serde_json::Value;
34use time::OffsetDateTime;
@@ -46,7 +47,7 @@
4647pub struct ViewView {
4748 pub id: Uuid,
4849 pub project_id: Uuid,
49 pub creator_id: Uuid,
50 pub creator: Option<UserBasicInfo>,
5051 pub name: String,
5152 pub description: Option<String>,
5253 pub filter: Value,
@@ -59,6 +60,29 @@
5960 pub updated_at: Option<OffsetDateTime>,
6061}
6162
63impl ViewView {
64 pub fn from_model(
65 m: db::project::project_views::Model,
66 creator: Option<UserBasicInfo>,
67 ) -> Self {
68 Self {
69 id: m.id,
70 project_id: m.project_id,
71 creator,
72 name: m.name,
73 description: m.description,
74 filter: m.filter,
75 sort_by: m.sort_by,
76 group_by: m.group_by,
77 layout: m.layout,
78 is_shared: m.is_shared,
79 sort_order: m.sort_order,
80 created_at: m.created_at,
81 updated_at: m.updated_at,
82 }
83 }
84}
85
6286#[derive(Debug, Clone, Serialize, ToSchema)]
6387pub struct ViewPage {
6488 pub total: u64,
Modified lib/export/repo/collab.rs lib/export/repo/collab.rs +6 −13 View lib/export/repo/collab.rs
@@ -1,3 +1,4 @@
1use crate::user::basic::UserBasicInfo;
12use db::repo::repo_collaborations as collab_entity;
23use db::repo::repo_collaborations::CollaborationMode;
34use serde::{Deserialize, Serialize};
@@ -14,18 +15,18 @@
1415pub struct CollaboratorView {
1516 pub id: Uuid,
1617 pub repo_id: Uuid,
17 pub user_id: Uuid,
18 pub user: Option<UserBasicInfo>,
1819 pub mode: String,
1920 pub created_at: String,
2021 pub updated_at: String,
2122}
2223
23impl From<collab_entity::Model> for CollaboratorView {
24 fn from(m: collab_entity::Model) -> Self {
24impl CollaboratorView {
25 pub fn from_model(m: collab_entity::Model, user: Option<UserBasicInfo>) -> Self {
2526 Self {
2627 id: m.id,
2728 repo_id: m.repo_id,
28 user_id: m.user_id,
29 user,
2930 mode: match m.mode {
3031 CollaborationMode::Read => "read".to_string(),
3132 CollaborationMode::Write => "write".to_string(),
@@ -52,15 +53,7 @@
5253pub struct RepoCollaboratorPermission {
5354 pub permission: String,
5455 pub role: String,
55 pub user: UserSummary,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
59pub struct UserSummary {
60 pub id: Uuid,
61 pub name: String,
62 pub login: String,
63 pub email: Option<String>,
56 pub user: Option<UserBasicInfo>,
6457}
6558
6659#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, IntoParams)]
Modified lib/export/repo/extras.rs lib/export/repo/extras.rs +7 −15 View lib/export/repo/extras.rs
@@ -1,3 +1,4 @@
1use crate::user::basic::UserBasicInfo;
12use serde::{Deserialize, Serialize};
23use time::OffsetDateTime;
34use time::format_description::well_known::Rfc3339;
@@ -108,22 +109,13 @@
108109}
109110
110111#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
111pub struct UserMini {
112 pub id: Uuid,
113 pub login: String,
114 pub full_name: Option<String>,
115 pub email: Option<String>,
116 pub avatar_url: Option<String>,
117}
118
119#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
120112pub struct AssigneesResponse {
121 pub items: Vec<UserMini>,
113 pub items: Vec<UserBasicInfo>,
122114}
123115
124116#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
125117pub struct ReviewersResponse {
126 pub items: Vec<UserMini>,
118 pub items: Vec<UserBasicInfo>,
127119}
128120
129121#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
@@ -140,8 +132,8 @@
140132pub struct CommitSummary {
141133 pub sha: String,
142134 pub message: String,
143 pub author: Option<UserMini>,
144 pub committer: Option<UserMini>,
135 pub author: Option<UserBasicInfo>,
136 pub committer: Option<UserBasicInfo>,
145137 pub parent_shas: Vec<String>,
146138 pub tree_sha: String,
147139 pub authored_at: Option<String>,
@@ -240,7 +232,7 @@
240232 pub id: Uuid,
241233 pub name: String,
242234 pub full_name: String,
243 pub owner: UserMini,
235 pub owner: UserBasicInfo,
244236 pub private: bool,
245237 pub fork: bool,
246238 pub created_at: String,
@@ -268,7 +260,7 @@
268260
269261#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
270262pub struct Subscription {
271 pub user_id: Uuid,
263 pub user: Option<UserBasicInfo>,
272264 pub repo_id: Uuid,
273265 pub mode: String,
274266 pub created_at: String,
Modified lib/export/repo/mod.rs lib/export/repo/mod.rs +3 −0 View lib/export/repo/mod.rs
@@ -9,7 +9,10 @@
99pub mod git_ref;
1010pub mod lifecycle;
1111pub mod pull_request;
12pub mod readme;
1213pub mod release;
14pub mod repo_features;
15pub mod repo_merge_strategies;
1316pub mod subscriber;
1417pub mod sync;
1518pub mod tag;
Modified lib/export/repo/pull_request.rs lib/export/repo/pull_request.rs +63 −58 View lib/export/repo/pull_request.rs
@@ -1,3 +1,4 @@
1use crate::user::basic::UserBasicInfo;
12use db::pull_request::pull_request_comment as comment_entity;
23use db::pull_request::pull_request_event_line as event_entity;
34use db::pull_request::pull_request_review as review_entity;
@@ -51,12 +52,12 @@
5152 pub head_sha: Option<String>,
5253 pub base_branch: String,
5354 pub base_sha: Option<String>,
54 pub author_id: Uuid,
55 pub author: Option<UserBasicInfo>,
5556 pub state: PrState,
5657 pub is_locked: bool,
5758 pub has_merged: bool,
5859 pub merged_at: Option<String>,
59 pub merger_id: Option<String>,
60 pub merger: Option<UserBasicInfo>,
6061 pub merged_commit_sha: Option<String>,
6162 pub closed_at: Option<String>,
6263 pub created_at: String,
@@ -67,8 +68,12 @@
6768 pub reviews_count: i64,
6869}
6970
70impl From<pr_entity::Model> for PullRequestView {
71 fn from(m: pr_entity::Model) -> Self {
71impl PullRequestView {
72 pub fn from_model(
73 m: pr_entity::Model,
74 author: Option<UserBasicInfo>,
75 merger: Option<UserBasicInfo>,
76 ) -> Self {
7277 Self {
7378 id: m.id,
7479 repo_id: m.repo_id,
@@ -79,12 +84,12 @@
7984 head_sha: (!m.head_sha.is_empty()).then_some(m.head_sha),
8085 base_branch: m.base_branch,
8186 base_sha: (!m.base_sha.is_empty()).then_some(m.base_sha),
82 author_id: m.author_id,
87 author,
8388 state: PrState::from_model(m.has_merged, m.closed_at),
8489 is_locked: m.is_locked,
8590 has_merged: m.has_merged,
8691 merged_at: fmt_ts_opt(m.merged_at),
87 merger_id: fmt_uuid_opt(m.merger_id),
92 merger,
8893 merged_commit_sha: m.merged_commit_sha,
8994 closed_at: fmt_ts_opt(m.closed_at),
9095 created_at: fmt_ts(m.created_at),
@@ -150,7 +155,7 @@
150155pub struct PullRequestCommentView {
151156 pub id: Uuid,
152157 pub pull_request_id: Uuid,
153 pub user_id: Uuid,
158 pub user: Option<UserBasicInfo>,
154159 pub review_id: Option<String>,
155160 pub commit_sha: Option<String>,
156161 pub path: Option<String>,
@@ -160,6 +165,23 @@
160165 pub updated_at: Option<String>,
161166}
162167
168impl PullRequestCommentView {
169 pub fn from_model(m: comment_entity::Model, user: Option<UserBasicInfo>) -> Self {
170 Self {
171 id: m.id,
172 pull_request_id: m.pull_request_id,
173 user,
174 review_id: fmt_uuid_opt(m.review_id),
175 commit_sha: (!m.commit_sha.is_empty()).then_some(m.commit_sha),
176 path: (!m.path.is_empty()).then_some(m.path),
177 line: m.line,
178 body: m.body,
179 created_at: fmt_ts(m.created_at),
180 updated_at: fmt_ts_opt(m.updated_at),
181 }
182 }
183}
184
163185#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
164186pub struct CreatePullRequestCommentOption {
165187 pub body: String,
@@ -180,7 +202,7 @@
180202pub struct PullRequestReviewView {
181203 pub id: Uuid,
182204 pub pull_request_id: Uuid,
183 pub user_id: Uuid,
205 pub user: Option<UserBasicInfo>,
184206 pub commit_sha: Option<String>,
185207 pub official: bool,
186208 pub content: Option<String>,
@@ -189,52 +211,8 @@
189211 pub updated_at: Option<String>,
190212}
191213
192#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
193pub struct SubmitPullRequestReviewOption {
194 pub state: ReviewState,
195 pub content: Option<String>,
196 pub commit_sha: Option<String>,
197}
198
199#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
200pub struct PullRequestEventView {
201 pub id: Uuid,
202 pub pull_request_id: Uuid,
203 pub user_id: Uuid,
204 pub event_type: String,
205 pub commit_sha: Option<String>,
206 pub content: Option<String>,
207 pub created_at: String,
208}
209
210#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, IntoParams)]
211#[into_params(parameter_in = Query)]
212pub struct ListPullRequestQuery {
213 pub page: Option<u64>,
214 pub limit: Option<u64>,
215 pub state: Option<String>,
216 pub keyword: Option<String>,
217}
218
219impl From<comment_entity::Model> for PullRequestCommentView {
220 fn from(m: comment_entity::Model) -> Self {
221 Self {
222 id: m.id,
223 pull_request_id: m.pull_request_id,
224 user_id: m.user_id,
225 review_id: fmt_uuid_opt(m.review_id),
226 commit_sha: (!m.commit_sha.is_empty()).then_some(m.commit_sha),
227 path: (!m.path.is_empty()).then_some(m.path),
228 line: m.line,
229 body: m.body,
230 created_at: fmt_ts(m.created_at),
231 updated_at: fmt_ts_opt(m.updated_at),
232 }
233 }
234}
235
236impl From<review_entity::Model> for PullRequestReviewView {
237 fn from(m: review_entity::Model) -> Self {
214impl PullRequestReviewView {
215 pub fn from_model(m: review_entity::Model, user: Option<UserBasicInfo>) -> Self {
238216 let state = match m.state {
239217 review_entity::ReviewState::Approved => ReviewState::Approved,
240218 review_entity::ReviewState::ChangesRequested => ReviewState::ChangesRequested,
@@ -243,7 +221,7 @@
243221 Self {
244222 id: m.id,
245223 pull_request_id: m.pull_request_id,
246 user_id: m.user_id,
224 user,
247225 commit_sha: (!m.commit_sha.is_empty()).then_some(m.commit_sha),
248226 official: m.official,
249227 content: m.content,
@@ -254,12 +232,30 @@
254232 }
255233}
256234
257impl From<event_entity::Model> for PullRequestEventView {
258 fn from(m: event_entity::Model) -> Self {
235#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
236pub struct SubmitPullRequestReviewOption {
237 pub state: ReviewState,
238 pub content: Option<String>,
239 pub commit_sha: Option<String>,
240}
241
242#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
243pub struct PullRequestEventView {
244 pub id: Uuid,
245 pub pull_request_id: Uuid,
246 pub user: Option<UserBasicInfo>,
247 pub event_type: String,
248 pub commit_sha: Option<String>,
249 pub content: Option<String>,
250 pub created_at: String,
251}
252
253impl PullRequestEventView {
254 pub fn from_model(m: event_entity::Model, user: Option<UserBasicInfo>) -> Self {
259255 Self {
260256 id: m.id,
261257 pull_request_id: m.pull_request_id,
262 user_id: m.user_id,
258 user,
263259 event_type: m.event_type,
264260 commit_sha: m.commit_sha,
265261 content: m.content,
@@ -267,3 +263,12 @@
267263 }
268264 }
269265}
266
267#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, IntoParams)]
268#[into_params(parameter_in = Query)]
269pub struct ListPullRequestQuery {
270 pub page: Option<u64>,
271 pub limit: Option<u64>,
272 pub state: Option<String>,
273 pub keyword: Option<String>,
274}
Modified lib/export/repo/release.rs lib/export/repo/release.rs +5 −26 View lib/export/repo/release.rs
@@ -1,3 +1,4 @@
1use crate::user::basic::UserBasicInfo;
12use db::repo::repo_release as release_entity;
23use serde::{Deserialize, Serialize};
34use time::OffsetDateTime;
@@ -16,14 +17,6 @@
1617 pub size: i64,
1718 pub download_url: String,
1819 pub created_at: String,
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
22pub struct UserSummary {
23 pub id: Uuid,
24 pub login: String,
25 pub full_name: Option<String>,
26 pub avatar_url: Option<String>,
2720}
2821
2922#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
@@ -44,14 +37,14 @@
4437 pub tarball_url: String,
4538 pub zipball_url: String,
4639 pub upload_url: String,
47 pub publisher: UserSummary,
40 pub publisher: Option<UserBasicInfo>,
4841 pub attachments: Vec<ReleaseAttachment>,
4942 pub created_at: String,
5043 pub updated_at: String,
5144}
5245
53impl From<release_entity::Model> for ReleaseView {
54 fn from(m: release_entity::Model) -> Self {
46impl ReleaseView {
47 pub fn from_model(m: release_entity::Model, publisher: Option<UserBasicInfo>) -> Self {
5548 let repo_id = m.repo_id;
5649 let id = m.id;
5750 let tag_name = m.tag_name.clone();
@@ -72,12 +65,7 @@
7265 tarball_url: format!("/repo/{repo_id}/archive/tarball/{tag_name}"),
7366 zipball_url: format!("/repo/{repo_id}/archive/zipball/{tag_name}"),
7467 upload_url: format!("/repo/{repo_id}/releases/{id}/assets"),
75 publisher: UserSummary {
76 id: m.publisher_id,
77 login: m.publisher_name.clone(),
78 full_name: Some(m.publisher_name),
79 avatar_url: None,
80 },
68 publisher,
8169 attachments: Vec::new(),
8270 created_at: fmt_ts(m.created_at),
8371 updated_at: fmt_ts(m.updated_at),
@@ -110,15 +98,6 @@
11098 pub content: Option<Option<String>>,
11199 pub is_draft: Option<bool>,
112100 pub is_prerelease: Option<bool>,
113}
114
115#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
116pub struct PublisherSummary {
117 pub id: Uuid,
118 pub name: String,
119 pub login: String,
120 pub email: Option<String>,
121 pub avatar_url: Option<String>,
122101}
123102
124103#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, IntoParams)]
Modified lib/export/repo/subscriber.rs lib/export/repo/subscriber.rs +5 −4 View lib/export/repo/subscriber.rs
@@ -1,3 +1,4 @@
1use crate::user::basic::UserBasicInfo;
12use db::common::watch_mode::WatchMode;
23use db::repo::repo_watch as watch_entity;
34use serde::{Deserialize, Serialize};
@@ -12,16 +13,16 @@
1213
1314#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1415pub struct SubscriberView {
15 pub user_id: Uuid,
16 pub user: Option<UserBasicInfo>,
1617 pub repo_id: Uuid,
1718 pub mode: String,
1819 pub created_at: String,
1920}
2021
21impl From<watch_entity::Model> for SubscriberView {
22 fn from(m: watch_entity::Model) -> Self {
22impl SubscriberView {
23 pub fn from_model(m: watch_entity::Model, user: Option<UserBasicInfo>) -> Self {
2324 Self {
24 user_id: m.user_id,
25 user,
2526 repo_id: m.repo_id,
2627 mode: match m.mode {
2728 WatchMode::Normal => "normal".to_string(),
Modified lib/export/space/bans.rs lib/export/space/bans.rs +11 −6 View lib/export/space/bans.rs
@@ -4,6 +4,7 @@
44use utoipa::ToSchema;
55use uuid::Uuid;
66
7use crate::user::basic::UserBasicInfo;
78use db::space::space_bans as ban_entity;
89
910fn fmt_ts(ts: OffsetDateTime) -> String {
@@ -17,9 +18,9 @@
1718#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1819pub struct SpaceBanView {
1920 pub space_id: Uuid,
20 pub user_id: Uuid,
21 pub user: Option<UserBasicInfo>,
2122 pub reason: Option<String>,
22 pub banned_by: Uuid,
23 pub banned_by: Option<UserBasicInfo>,
2324 pub created_at: String,
2425 pub updated_at: String,
2526 pub expires_at: Option<String>,
@@ -29,13 +30,17 @@
2930 pub allow_comment_any: bool,
3031}
3132
32impl From<ban_entity::Model> for SpaceBanView {
33 fn from(b: ban_entity::Model) -> Self {
33impl SpaceBanView {
34 pub fn from_model(
35 b: ban_entity::Model,
36 user: Option<UserBasicInfo>,
37 banned_by: Option<UserBasicInfo>,
38 ) -> Self {
3439 SpaceBanView {
3540 space_id: b.space_id,
36 user_id: b.user_id,
41 user,
3742 reason: b.reason,
38 banned_by: b.banned_by,
43 banned_by,
3944 created_at: fmt_ts(b.created_at),
4045 updated_at: fmt_ts(b.updated_at),
4146 expires_at: fmt_ts_opt(b.expires_at),
Modified lib/export/space/channels.rs lib/export/space/channels.rs +11 −6 View lib/export/space/channels.rs
@@ -1,3 +1,4 @@
1use crate::user::basic::UserBasicInfo;
12use db::channel::channels as channel_entity;
23use db::channel::channels::ChannelKind;
34use serde::{Deserialize, Serialize};
@@ -28,10 +29,10 @@
2829 pub purpose: Option<String>,
2930 pub is_archived: bool,
3031 pub archived_at: Option<String>,
31 pub archived_by: Option<Uuid>,
32 pub archived_by: Option<UserBasicInfo>,
3233 pub created_at: String,
3334 pub updated_at: String,
34 pub created_by: Uuid,
35 pub created_by: Option<UserBasicInfo>,
3536}
3637
3738fn kind_str(k: ChannelKind) -> String {
@@ -41,8 +42,12 @@
4142 }
4243}
4344
44impl From<channel_entity::Model> for ChannelView {
45 fn from(c: channel_entity::Model) -> Self {
45impl ChannelView {
46 pub fn from_model(
47 c: channel_entity::Model,
48 archived_by: Option<UserBasicInfo>,
49 created_by: Option<UserBasicInfo>,
50 ) -> Self {
4651 Self {
4752 id: c.id,
4853 space_id: c.space_id,
@@ -56,10 +61,10 @@
5661 purpose: c.purpose,
5762 is_archived: c.is_archived,
5863 archived_at: fmt_ts_opt(c.archived_at),
59 archived_by: c.archived_by,
64 archived_by,
6065 created_at: fmt_ts(c.created_at),
6166 updated_at: fmt_ts(c.updated_at),
62 created_by: c.created_by,
67 created_by,
6368 }
6469 }
6570}
Modified lib/export/space/invites.rs lib/export/space/invites.rs +5 −4 View lib/export/space/invites.rs
@@ -1,3 +1,4 @@
1use crate::user::basic::UserBasicInfo;
12use db::space::space_invite as invite_entity;
23use serde::{Deserialize, Serialize};
34use time::OffsetDateTime;
@@ -17,7 +18,7 @@
1718pub struct SpaceInviteView {
1819 pub id: Uuid,
1920 pub space_id: Uuid,
20 pub user_id: Uuid,
21 pub user: Option<UserBasicInfo>,
2122 pub email: String,
2223 pub role_ids: Vec<Uuid>,
2324 pub created_at: String,
@@ -26,12 +27,12 @@
2627 pub revoked_at: Option<String>,
2728}
2829
29impl From<invite_entity::Model> for SpaceInviteView {
30 fn from(m: invite_entity::Model) -> Self {
30impl SpaceInviteView {
31 pub fn from_model(m: invite_entity::Model, user: Option<UserBasicInfo>) -> Self {
3132 SpaceInviteView {
3233 id: m.id,
3334 space_id: m.space_id,
34 user_id: m.user_id,
35 user,
3536 email: m.email,
3637 role_ids: m
3738 .role_ids
Modified lib/export/space/members.rs lib/export/space/members.rs +11 −6 View lib/export/space/members.rs
@@ -4,6 +4,7 @@
44use utoipa::ToSchema;
55use uuid::Uuid;
66
7use crate::user::basic::UserBasicInfo;
78use db::space::space_members as member_entity;
89
910fn fmt_ts(ts: OffsetDateTime) -> String {
@@ -78,14 +79,14 @@
7879#[derive(Debug, Clone, Serialize, ToSchema)]
7980pub struct SpaceMemberView {
8081 pub space_id: Uuid,
81 pub user_id: Uuid,
82 pub user: Option<UserBasicInfo>,
8283 pub alias_name: Option<String>,
8384 pub alias_avatar_url: Option<String>,
8485 pub status: SpaceMemberStatus,
8586 pub last_active_at: Option<String>,
8687 pub left_at: Option<String>,
8788 pub invited_at: Option<String>,
88 pub invited_by: Option<Uuid>,
89 pub invited_by: Option<UserBasicInfo>,
8990 pub joined_at: String,
9091 pub created_at: String,
9192 pub updated_at: Option<String>,
@@ -98,18 +99,22 @@
9899 pub hide_muted_channels: bool,
99100}
100101
101impl From<member_entity::Model> for SpaceMemberView {
102 fn from(m: member_entity::Model) -> Self {
102impl SpaceMemberView {
103 pub fn from_model(
104 m: member_entity::Model,
105 user: Option<UserBasicInfo>,
106 invited_by: Option<UserBasicInfo>,
107 ) -> Self {
103108 Self {
104109 space_id: m.space_id,
105 user_id: m.user_id,
110 user,
106111 alias_name: m.alias_name,
107112 alias_avatar_url: m.alias_avatar_url,
108113 status: m.status.into(),
109114 last_active_at: fmt_ts_opt(m.last_active_at),
110115 left_at: fmt_ts_opt(m.left_at),
111116 invited_at: fmt_ts_opt(m.invited_at),
112 invited_by: m.invited_by,
117 invited_by,
113118 joined_at: fmt_ts(m.joined_at),
114119 created_at: fmt_ts(m.created_at),
115120 updated_at: fmt_ts_opt(m.updated_at),
Modified lib/export/space/mod.rs lib/export/space/mod.rs +1 −0 View lib/export/space/mod.rs
@@ -4,4 +4,5 @@
44pub mod members;
55pub mod overwrites;
66pub mod roles;
7pub mod sidebar;
78pub mod spaces;
Modified lib/export/space/overwrites.rs lib/export/space/overwrites.rs +5 −4 View lib/export/space/overwrites.rs
@@ -1,3 +1,4 @@
1use crate::user::basic::UserBasicInfo;
12use db::channel::channel_overwrite_role as role_ow_entity;
23use db::channel::channel_overwrite_users as user_ow_entity;
34use serde::{Deserialize, Serialize};
@@ -37,16 +38,16 @@
3738#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
3839pub struct UserOverwriteView {
3940 pub channel_id: Uuid,
40 pub user_id: Uuid,
41 pub user: Option<UserBasicInfo>,
4142 pub allow_mask: i64,
4243 pub deny_mask: i64,
4344}
4445
45impl From<user_ow_entity::Model> for UserOverwriteView {
46 fn from(m: user_ow_entity::Model) -> Self {
46impl UserOverwriteView {
47 pub fn from_model(m: user_ow_entity::Model, user: Option<UserBasicInfo>) -> Self {
4748 Self {
4849 channel_id: m.channel_id,
49 user_id: m.user_id,
50 user,
5051 allow_mask: m.allow_mask,
5152 deny_mask: m.deny_mask,
5253 }
Modified lib/export/space/roles.rs lib/export/space/roles.rs +5 −4 View lib/export/space/roles.rs
@@ -1,3 +1,4 @@
1use crate::user::basic::UserBasicInfo;
12use db::space::space_roles as role_entity;
23use serde::{Deserialize, Serialize};
34use time::OffsetDateTime;
@@ -25,12 +26,12 @@
2526 pub version: i32,
2627 pub created_at: String,
2728 pub updated_at: Option<String>,
28 pub created_by: Uuid,
29 pub created_by: Option<UserBasicInfo>,
2930 pub permissions: Vec<String>,
3031}
3132
32impl From<role_entity::Model> for RoleView {
33 fn from(r: role_entity::Model) -> Self {
33impl RoleView {
34 pub fn from_model(r: role_entity::Model, created_by: Option<UserBasicInfo>) -> Self {
3435 Self {
3536 id: r.id,
3637 space_id: r.space_id,
@@ -42,7 +43,7 @@
4243 version: r.version,
4344 created_at: fmt_ts(r.created_at),
4445 updated_at: fmt_ts_opt(r.updated_at),
45 created_by: r.created_by,
46 created_by,
4647 permissions: Vec::new(),
4748 }
4849 }
Modified lib/export/space/spaces.rs lib/export/space/spaces.rs +5 −4 View lib/export/space/spaces.rs
@@ -4,6 +4,7 @@
44use utoipa::ToSchema;
55use uuid::Uuid;
66
7use crate::user::basic::UserBasicInfo;
78use db::space::spaces as space_entity;
89
910fn fmt_ts(ts: OffsetDateTime) -> String {
@@ -61,7 +62,7 @@
6162 pub id: Uuid,
6263 pub name: String,
6364 pub description: Option<String>,
64 pub owner_id: Uuid,
65 pub owner: Option<UserBasicInfo>,
6566 pub avatar_url: Option<String>,
6667 pub public: bool,
6768 pub is_active: bool,
@@ -75,13 +76,13 @@
7576 pub updated_at: String,
7677}
7778
78impl From<space_entity::Model> for SpaceView {
79 fn from(m: space_entity::Model) -> Self {
79impl SpaceView {
80 pub fn from_model(m: space_entity::Model, owner: Option<UserBasicInfo>) -> Self {
8081 Self {
8182 id: m.id,
8283 name: m.name,
8384 description: m.description,
84 owner_id: m.owner_id,
85 owner,
8586 avatar_url: m.avatar_url,
8687 public: m.public,
8788 is_active: m.is_active,
Modified lib/export/user/deletion.rs lib/export/user/deletion.rs +8 −4 View lib/export/user/deletion.rs
@@ -1,3 +1,4 @@
1use crate::user::basic::UserBasicInfo;
12use serde::{Deserialize, Serialize};
23use time::format_description::well_known::Rfc3339;
34use utoipa::ToSchema;
@@ -39,7 +40,7 @@
3940#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4041pub struct DeletionRequestView {
4142 pub id: Uuid,
42 pub user_id: Uuid,
43 pub user: Option<UserBasicInfo>,
4344 pub reason: Option<String>,
4445 pub status: DeletionStatus,
4546 pub scheduled_for: Option<String>,
@@ -49,11 +50,14 @@
4950 pub updated_at: String,
5051}
5152
52impl From<db::user::user_deletion_requests::Model> for DeletionRequestView {
53 fn from(m: db::user::user_deletion_requests::Model) -> Self {
53impl DeletionRequestView {
54 pub fn from_model(
55 m: db::user::user_deletion_requests::Model,
56 user: Option<UserBasicInfo>,
57 ) -> Self {
5458 Self {
5559 id: m.id,
56 user_id: m.user_id,
60 user,
5761 reason: m.reason,
5862 status: m.status.into(),
5963 scheduled_for: m
Modified lib/export/user/mod.rs lib/export/user/mod.rs +1 −0 View lib/export/user/mod.rs
@@ -1,3 +1,4 @@
1pub mod basic;
12pub mod deletion;
23pub mod devices;
34pub mod emails;
Modified lib/export/user/notifications.rs lib/export/user/notifications.rs +21 −12 View lib/export/user/notifications.rs
@@ -1,3 +1,4 @@
1use crate::user::basic::UserBasicInfo;
12use serde::{Deserialize, Serialize};
23use time::format_description::well_known::Rfc3339;
34use utoipa::ToSchema;
@@ -97,19 +98,22 @@
9798 pub notification_type: NotificationType,
9899 pub title: String,
99100 pub body: Option<String>,
100 pub actor_uid: Option<Uuid>,
101 pub actor: Option<UserBasicInfo>,
101102 pub read_at: Option<String>,
102103 pub created_at: String,
103104}
104105
105impl From<db::user::user_notifications::Model> for NotificationView {
106 fn from(m: db::user::user_notifications::Model) -> Self {
106impl NotificationView {
107 pub fn from_model(
108 m: db::user::user_notifications::Model,
109 actor: Option<UserBasicInfo>,
110 ) -> Self {
107111 Self {
108112 id: m.id,
109113 notification_type: m.notification_type.into(),
110114 title: m.title,
111115 body: m.body,
112 actor_uid: m.actor_uid,
116 actor,
113117 read_at: m.read_at.map(|t| t.format(&Rfc3339).unwrap_or_default()),
114118 created_at: m.created_at.format(&Rfc3339).unwrap_or_default(),
115119 }
@@ -162,27 +166,32 @@
162166#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
163167pub struct ReportView {
164168 pub id: Uuid,
165 pub reporter_uid: Option<Uuid>,
166 pub reported_uid: Uuid,
169 pub reporter: Option<UserBasicInfo>,
170 pub reported: Option<UserBasicInfo>,
167171 pub reason: String,
168172 pub evidence: Option<String>,
169173 pub status: ReportStatus,
170 pub resolver_uid: Option<Uuid>,
174 pub resolver: Option<UserBasicInfo>,
171175 pub resolution_note: Option<String>,
172176 pub created_at: String,
173177 pub updated_at: String,
174178}
175179
176impl From<db::user::user_reports::Model> for ReportView {
177 fn from(m: db::user::user_reports::Model) -> Self {
180impl ReportView {
181 pub fn from_model(
182 m: db::user::user_reports::Model,
183 reporter: Option<UserBasicInfo>,
184 reported: Option<UserBasicInfo>,
185 resolver: Option<UserBasicInfo>,
186 ) -> Self {
178187 Self {
179188 id: m.id,
180 reporter_uid: m.reporter_uid,
181 reported_uid: m.reported_uid,
189 reporter,
190 reported,
182191 reason: m.reason,
183192 evidence: m.evidence,
184193 status: m.status.into(),
185 resolver_uid: m.resolver_uid,
194 resolver,
186195 resolution_note: m.resolution_note,
187196 created_at: m.created_at.format(&Rfc3339).unwrap_or_default(),
188197 updated_at: m.updated_at.format(&Rfc3339).unwrap_or_default(),
Modified lib/export/user/security.rs lib/export/user/security.rs +21 −0 View lib/export/user/security.rs
@@ -219,3 +219,24 @@
219219 pub platform: PushPlatform,
220220 pub user_agent: Option<String>,
221221}
222
223#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
224pub struct TwoFactorStatusResp {
225 pub enabled: bool,
226}
227
228#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
229pub struct TwoFactorSetupResp {
230 pub secret: String,
231 pub otpauth_uri: String,
232}
233
234#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
235pub struct EnableTwoFactorCmd {
236 pub code: String,
237}
238
239#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
240pub struct DisableTwoFactorCmd {
241 pub code: String,
242}
Modified lib/export/user/suspensions.rs lib/export/user/suspensions.rs +14 −8 View lib/export/user/suspensions.rs
@@ -1,3 +1,4 @@
1use crate::user::basic::UserBasicInfo;
12use serde::{Deserialize, Serialize};
23use time::format_description::well_known::Rfc3339;
34use utoipa::ToSchema;
@@ -31,29 +32,34 @@
3132#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3233pub struct SuspensionView {
3334 pub id: Uuid,
34 pub user_id: Uuid,
35 pub user: Option<UserBasicInfo>,
3536 pub suspension_type: SuspensionType,
3637 pub reason: Option<String>,
37 pub suspended_by: Option<Uuid>,
38 pub suspended_by: Option<UserBasicInfo>,
3839 pub starts_at: String,
3940 pub ends_at: Option<String>,
4041 pub revoked_at: Option<String>,
41 pub revoked_by: Option<Uuid>,
42 pub revoked_by: Option<UserBasicInfo>,
4243 pub created_at: String,
4344}
4445
45impl From<db::user::user_suspensions::Model> for SuspensionView {
46 fn from(m: db::user::user_suspensions::Model) -> Self {
46impl SuspensionView {
47 pub fn from_model(
48 m: db::user::user_suspensions::Model,
49 user: Option<UserBasicInfo>,
50 suspended_by: Option<UserBasicInfo>,
51 revoked_by: Option<UserBasicInfo>,
52 ) -> Self {
4753 Self {
4854 id: m.id,
49 user_id: m.user_id,
55 user,
5056 suspension_type: m.suspension_type.into(),
5157 reason: m.reason,
52 suspended_by: m.suspended_by,
58 suspended_by,
5359 starts_at: m.starts_at.format(&Rfc3339).unwrap_or_default(),
5460 ends_at: m.ends_at.map(|t| t.format(&Rfc3339).unwrap_or_default()),
5561 revoked_at: m.revoked_at.map(|t| t.format(&Rfc3339).unwrap_or_default()),
56 revoked_by: m.revoked_by,
62 revoked_by,
5763 created_at: m.created_at.format(&Rfc3339).unwrap_or_default(),
5864 }
5965 }
Modified lib/issues/features/issue/usecase/close.rs lib/issues/features/issue/usecase/close.rs +7 −2 View lib/issues/features/issue/usecase/close.rs
@@ -1,10 +1,12 @@
11use db::conn::sea_orm::*;
2use db::user::users_loader::load_user;
3use export::user::basic::UserBasicInfo;
24use time::OffsetDateTime;
35use uuid::Uuid;
46
57use crate::error::IssueResult;
6use crate::features::authz;
78use crate::features::IssueService;
9use crate::features::authz;
810use crate::metrics;
911
1012use super::super::{CloseIssueOption, IssueView};
@@ -33,7 +35,10 @@
3335 active.updated_at = Set(Some(OffsetDateTime::now_utc()));
3436
3537 let saved = active.update(&db).await?;
36 Ok(IssueView::from(saved))
38 let author = load_user(&svc.db, saved.author_id)
39 .await?
40 .map(UserBasicInfo::from);
41 Ok(IssueView::from_model(saved, author))
3742 }
3843 .await;
3944 metrics::record_outcome(metrics::FEATURE_ISSUES_CLOSE, &result);
Modified lib/issues/features/issue/usecase/create.rs lib/issues/features/issue/usecase/create.rs +4 −1 View lib/issues/features/issue/usecase/create.rs
@@ -1,6 +1,8 @@
11use db::conn::sea_orm::*;
22use db::issue::issue as issue_entity;
33use db::issue::issues as issues_entity;
4use db::user::users_loader::load_user;
5use export::user::basic::UserBasicInfo;
46use time::OffsetDateTime;
57use uuid::Uuid;
68
@@ -90,7 +92,8 @@
9092 let saved = active.insert(&txn).await?;
9193
9294 txn.commit().await?;
93 Ok(IssueView::from(saved))
95 let author = load_user(&svc.db, actor).await?.map(UserBasicInfo::from);
96 Ok(IssueView::from_model(saved, author))
9497 }
9598 .await;
9699 metrics::record_outcome(metrics::FEATURE_ISSUES_CREATE, &result);
Modified lib/issues/features/issue/usecase/delete.rs lib/issues/features/issue/usecase/delete.rs +1 −1 View lib/issues/features/issue/usecase/delete.rs
@@ -2,8 +2,8 @@
22use uuid::Uuid;
33
44use crate::error::IssueResult;
5use crate::features::IssueService;
56use crate::features::authz;
6use crate::features::IssueService;
77use crate::metrics;
88
99use super::helpers::{load_issue, require_authenticated};
Modified lib/issues/features/issue/usecase/get.rs lib/issues/features/issue/usecase/get.rs +6 −1 View lib/issues/features/issue/usecase/get.rs
@@ -1,3 +1,5 @@
1use db::user::users_loader::load_user;
2use export::user::basic::UserBasicInfo;
13use uuid::Uuid;
24
35use crate::error::IssueResult;
@@ -17,7 +19,10 @@
1719 let result: IssueResult<IssueView> = async {
1820 require_authenticated(actor)?;
1921 let it = load_issue(svc, channel_id, issue_id).await?;
20 Ok(IssueView::from(it))
22 let author = load_user(&svc.db, it.author_id)
23 .await?
24 .map(UserBasicInfo::from);
25 Ok(IssueView::from_model(it, author))
2126 }
2227 .await;
2328 metrics::record_outcome(metrics::FEATURE_ISSUES_GET, &result);
Modified lib/issues/features/issue/usecase/list.rs lib/issues/features/issue/usecase/list.rs +11 −1 View lib/issues/features/issue/usecase/list.rs
@@ -1,5 +1,7 @@
11use db::conn::sea_orm::*;
22use db::issue::issue as issue_entity;
3use db::user::users_loader::load_users;
4use export::user::basic::UserBasicInfo;
35use uuid::Uuid;
46
57use crate::error::{IssueError, IssueResult};
@@ -55,7 +57,15 @@
5557 .all(&db)
5658 .await?;
5759
58 let items = rows.into_iter().map(IssueView::from).collect();
60 let user_ids: Vec<Uuid> = rows.iter().map(|r| r.author_id).collect();
61 let users = load_users(&svc.db, &user_ids).await?;
62 let items = rows
63 .into_iter()
64 .map(|r| {
65 let author_id = r.author_id;
66 IssueView::from_model(r, users.get(&author_id).map(UserBasicInfo::from))
67 })
68 .collect();
5969 Ok(IssuePage { items, total })
6070 }
6171 .await;
Modified lib/issues/features/issue/usecase/update.rs lib/issues/features/issue/usecase/update.rs +7 −2 View lib/issues/features/issue/usecase/update.rs
@@ -1,10 +1,12 @@
11use db::conn::sea_orm::*;
2use db::user::users_loader::load_user;
3use export::user::basic::UserBasicInfo;
24use time::OffsetDateTime;
35use uuid::Uuid;
46
57use crate::error::{IssueError, IssueResult};
6use crate::features::authz;
78use crate::features::IssueService;
9use crate::features::authz;
810use crate::metrics;
911
1012use super::super::{IssueView, UpdateIssueOption};
@@ -58,7 +60,10 @@
5860 active.updated_at = Set(Some(OffsetDateTime::now_utc()));
5961
6062 let saved = active.update(&db).await?;
61 Ok(IssueView::from(saved))
63 let author = load_user(&svc.db, saved.author_id)
64 .await?
65 .map(UserBasicInfo::from);
66 Ok(IssueView::from_model(saved, author))
6267 }
6368 .await;
6469 metrics::record_outcome(metrics::FEATURE_ISSUES_UPDATE, &result);
Modified lib/issues/features/issue_attachment/usecase/create.rs lib/issues/features/issue_attachment/usecase/create.rs +7 −1 View lib/issues/features/issue_attachment/usecase/create.rs
@@ -2,6 +2,8 @@
22use db::attachment::attachments as att_entity;
33use db::attachment::attachments::OwnerType;
44use db::conn::sea_orm::*;
5use db::user::users_loader::load_user;
6use export::user::basic::UserBasicInfo;
57use object_store::path::Path;
68use time::OffsetDateTime;
79use uuid::Uuid;
@@ -61,7 +63,11 @@
6163 .set_created_at(now)
6264 .into();
6365 let saved = active.insert(&db).await?;
64 Ok(AttachmentView::from(saved))
66 let uploaded_by = match saved.uploaded_by {
67 Some(id) => load_user(&svc.db, id).await?.map(UserBasicInfo::from),
68 None => None,
69 };
70 Ok(AttachmentView::from_model(saved, uploaded_by))
6571 }
6672 .await;
6773 metrics::record_outcome(metrics::FEATURE_ISSUE_ATTACHMENTS_CREATE, &result);
Modified lib/issues/features/issue_attachment/usecase/list.rs lib/issues/features/issue_attachment/usecase/list.rs +14 −1 View lib/issues/features/issue_attachment/usecase/list.rs
@@ -1,6 +1,8 @@
11use db::attachment::attachments as att_entity;
22use db::attachment::attachments::OwnerType;
33use db::conn::sea_orm::*;
4use db::user::users_loader::load_users;
5use export::user::basic::UserBasicInfo;
46use uuid::Uuid;
57
68use crate::error::{IssueError, IssueResult};
@@ -41,7 +43,18 @@
4143 .offset(offset)
4244 .all(&db)
4345 .await?;
44 let items = rows.into_iter().map(AttachmentView::from).collect();
46 let user_ids: Vec<Uuid> = rows.iter().filter_map(|r| r.uploaded_by).collect();
47 let users = load_users(&svc.db, &user_ids).await?;
48 let items = rows
49 .into_iter()
50 .map(|r| {
51 let uploaded_by = r.uploaded_by;
52 AttachmentView::from_model(
53 r,
54 uploaded_by.and_then(|id| users.get(&id).map(UserBasicInfo::from)),
55 )
56 })
57 .collect();
4558 Ok(AttachmentPage { items, total })
4659 }
4760 .await;
Modified lib/issues/features/issue_event/usecase/list.rs lib/issues/features/issue_event/usecase/list.rs +11 −1 View lib/issues/features/issue_event/usecase/list.rs
@@ -1,5 +1,7 @@
11use db::conn::sea_orm::*;
22use db::issue::issue_event_line as event_entity;
3use db::user::users_loader::load_users;
4use export::user::basic::UserBasicInfo;
35use uuid::Uuid;
46
57use crate::error::{IssueError, IssueResult};
@@ -38,7 +40,15 @@
3840 .offset(offset)
3941 .all(&db)
4042 .await?;
41 let items = rows.into_iter().map(EventView::from).collect();
43 let user_ids: Vec<Uuid> = rows.iter().map(|r| r.user_id).collect();
44 let users = load_users(&svc.db, &user_ids).await?;
45 let items = rows
46 .into_iter()
47 .map(|r| {
48 let user_id = r.user_id;
49 EventView::from_model(r, users.get(&user_id).map(UserBasicInfo::from))
50 })
51 .collect();
4252 Ok(EventPage { items, total })
4353 }
4454 .await;
Modified lib/issues/features/issue_member/usecase/create.rs lib/issues/features/issue_member/usecase/create.rs +6 −1 View lib/issues/features/issue_member/usecase/create.rs
@@ -1,5 +1,7 @@
11use db::conn::sea_orm::*;
22use db::issue::issue_member as member_entity;
3use db::user::users_loader::load_user;
4use export::user::basic::UserBasicInfo;
35use time::OffsetDateTime;
46use uuid::Uuid;
57
@@ -34,7 +36,10 @@
3436 .set_updated_at(None)
3537 .into();
3638 let saved = active.insert(&db).await?;
37 Ok(MemberView::from(saved))
39 let user = load_user(&svc.db, saved.user_id)
40 .await?
41 .map(UserBasicInfo::from);
42 Ok(MemberView::from_model(saved, user))
3843 }
3944 .await;
4045 metrics::record_outcome(metrics::FEATURE_ISSUE_MEMBERS_CREATE, &result);
Modified lib/issues/features/issue_member/usecase/list.rs lib/issues/features/issue_member/usecase/list.rs +11 −1 View lib/issues/features/issue_member/usecase/list.rs
@@ -1,5 +1,7 @@
11use db::conn::sea_orm::*;
22use db::issue::issue_member as member_entity;
3use db::user::users_loader::load_users;
4use export::user::basic::UserBasicInfo;
35use uuid::Uuid;
46
57use crate::error::{IssueError, IssueResult};
@@ -44,7 +46,15 @@
4446 .offset(offset)
4547 .all(&db)
4648 .await?;
47 let items = rows.into_iter().map(MemberView::from).collect();
49 let user_ids: Vec<Uuid> = rows.iter().map(|r| r.user_id).collect();
50 let users = load_users(&svc.db, &user_ids).await?;
51 let items = rows
52 .into_iter()
53 .map(|r| {
54 let user_id = r.user_id;
55 MemberView::from_model(r, users.get(&user_id).map(UserBasicInfo::from))
56 })
57 .collect();
4858 Ok(MemberPage { items, total })
4959 }
5060 .await;
Modified lib/issues/features/issue_reaction/usecase/create.rs lib/issues/features/issue_reaction/usecase/create.rs +10 −2 View lib/issues/features/issue_reaction/usecase/create.rs
@@ -1,5 +1,7 @@
11use db::conn::sea_orm::*;
22use db::issue::issue_reactions as reaction_entity;
3use db::user::users_loader::load_user;
4use export::user::basic::UserBasicInfo;
35use time::OffsetDateTime;
46use uuid::Uuid;
57
@@ -47,7 +49,10 @@
4749 .one(&db)
4850 .await?
4951 .ok_or_else(|| IssueError::Internal("reaction not found after insert".into()))?;
50 Ok(ReactionView::from(saved))
52 let user = load_user(&svc.db, saved.user_id)
53 .await?
54 .map(UserBasicInfo::from);
55 Ok(ReactionView::from_model(saved, user))
5156 }
5257 .await;
5358 metrics::record_outcome(metrics::FEATURE_ISSUE_REACTIONS_CREATE, &result);
@@ -98,7 +103,10 @@
98103 .one(&db)
99104 .await?
100105 .ok_or_else(|| IssueError::Internal("reaction not found after insert".into()))?;
101 Ok(ReactionView::from(saved))
106 let user = load_user(&svc.db, saved.user_id)
107 .await?
108 .map(UserBasicInfo::from);
109 Ok(ReactionView::from_model(saved, user))
102110 }
103111 .await;
104112 metrics::record_outcome(metrics::FEATURE_ISSUE_REACTIONS_CREATE, &result);
Modified lib/issues/features/issue_reaction/usecase/list.rs lib/issues/features/issue_reaction/usecase/list.rs +1 −7 View lib/issues/features/issue_reaction/usecase/list.rs
@@ -6,7 +6,7 @@
66use crate::features::IssueService;
77use crate::metrics;
88
9use super::super::{ReactionAggView, ReactionView};
9use super::super::ReactionAggView;
1010use super::helpers::ensure_issue_visible;
1111use super::helpers::require_authenticated;
1212
@@ -70,10 +70,4 @@
7070 entry.user_ids.push(r.user_id);
7171 }
7272 map.into_values().collect()
73}
74
75// suppress unused warning when only one path is used
76#[allow(dead_code)]
77fn _views(rows: Vec<reaction_entity::Model>) -> Vec<ReactionView> {
78 rows.into_iter().map(ReactionView::from).collect()
7973}
Modified lib/issues/features/issue_stopwatch/usecase/start.rs lib/issues/features/issue_stopwatch/usecase/start.rs +6 −1 View lib/issues/features/issue_stopwatch/usecase/start.rs
@@ -1,5 +1,7 @@
11use db::conn::sea_orm::*;
22use db::issue::stopwatch as sw_entity;
3use db::user::users_loader::load_user;
4use export::user::basic::UserBasicInfo;
35use time::OffsetDateTime;
46use uuid::Uuid;
57
@@ -42,7 +44,10 @@
4244 .set_created_at(now)
4345 .into();
4446 let saved = active.insert(&db).await?;
45 Ok(StopwatchView::from(saved))
47 let user = load_user(&svc.db, saved.user_id)
48 .await?
49 .map(UserBasicInfo::from);
50 Ok(StopwatchView::from_model(saved, user))
4651 }
4752 .await;
4853 metrics::record_outcome(metrics::FEATURE_ISSUE_STOPWATCH_START, &result);
Modified lib/issues/features/issue_stopwatch/impls.rs lib/issues/features/issue_stopwatch/impls.rs +5 −3 View lib/issues/features/issue_stopwatch/impls.rs
@@ -1,3 +1,5 @@
1use db::user::users_loader::load_user;
2use export::user::basic::UserBasicInfo;
13use uuid::Uuid;
24
35use crate::error::IssueResult;
@@ -13,9 +15,9 @@
1315 _channel_id: Uuid,
1416 issue_id: Uuid,
1517 ) -> IssueResult<StopwatchView> {
16 usecase::load_my_stopwatch(self, issue_id, actor)
17 .await
18 .map(StopwatchView::from)
18 let row = usecase::load_my_stopwatch(self, issue_id, actor).await?;
19 let user = load_user(&self.db, actor).await?.map(UserBasicInfo::from);
20 Ok(StopwatchView::from_model(row, user))
1921 }
2022
2123 pub async fn start_watch(
Modified lib/issues/features/issue_tracked_time/usecase/list.rs lib/issues/features/issue_tracked_time/usecase/list.rs +11 −1 View lib/issues/features/issue_tracked_time/usecase/list.rs
@@ -1,5 +1,7 @@
11use db::conn::sea_orm::*;
22use db::issue::tracked_time as tt_entity;
3use db::user::users_loader::load_users;
4use export::user::basic::UserBasicInfo;
35use uuid::Uuid;
46
57use crate::error::{IssueError, IssueResult};
@@ -41,7 +43,15 @@
4143 .offset(offset)
4244 .all(&db)
4345 .await?;
44 let items = rows.into_iter().map(TrackedTimeView::from).collect();
46 let user_ids: Vec<Uuid> = rows.iter().map(|r| r.user_id).collect();
47 let users = load_users(&svc.db, &user_ids).await?;
48 let items = rows
49 .into_iter()
50 .map(|r| {
51 let user_id = r.user_id;
52 TrackedTimeView::from_model(r, users.get(&user_id).map(UserBasicInfo::from))
53 })
54 .collect();
4555 Ok(TrackedTimePage { items, total })
4656 }
4757 .await;
Modified lib/issues/features/issue_user/usecase/list.rs lib/issues/features/issue_user/usecase/list.rs +12 −1 View lib/issues/features/issue_user/usecase/list.rs
@@ -1,5 +1,7 @@
11use db::conn::sea_orm::*;
22use db::issue::issue_user as issue_user_entity;
3use db::user::users_loader::load_users;
4use export::user::basic::UserBasicInfo;
35use uuid::Uuid;
46
57use crate::error::IssueResult;
@@ -24,7 +26,16 @@
2426 .filter(issue_user_entity::COLUMN.issue_id.eq(issue_id))
2527 .all(&db)
2628 .await?;
27 Ok(rows.into_iter().map(IssueUserView::from).collect())
29 let user_ids: Vec<Uuid> = rows.iter().map(|r| r.uid).collect();
30 let users = load_users(&svc.db, &user_ids).await?;
31 let items = rows
32 .into_iter()
33 .map(|r| {
34 let uid = r.uid;
35 IssueUserView::from_model(r, users.get(&uid).map(UserBasicInfo::from))
36 })
37 .collect();
38 Ok(items)
2839 }
2940 .await;
3041 metrics::record_outcome(metrics::FEATURE_ISSUE_USERS_LIST, &result);
Modified lib/issues/features/issue_user/usecase/update.rs lib/issues/features/issue_user/usecase/update.rs +6 −1 View lib/issues/features/issue_user/usecase/update.rs
@@ -1,4 +1,6 @@
11use db::conn::sea_orm::*;
2use db::user::users_loader::load_user;
3use export::user::basic::UserBasicInfo;
24use time::OffsetDateTime;
35use uuid::Uuid;
46
@@ -32,7 +34,10 @@
3234 }
3335 let _ = OffsetDateTime::now_utc();
3436 let saved = active.update(&db).await?;
35 Ok(IssueUserView::from(saved))
37 let user = load_user(&svc.db, saved.uid)
38 .await?
39 .map(UserBasicInfo::from);
40 Ok(IssueUserView::from_model(saved, user))
3641 }
3742 .await;
3843 metrics::record_outcome(metrics::FEATURE_ISSUE_USERS_UPDATE, &result);
Modified lib/issues/features/issue_watch/usecase/helpers.rs lib/issues/features/issue_watch/usecase/helpers.rs +5 −1 View lib/issues/features/issue_watch/usecase/helpers.rs
@@ -2,11 +2,14 @@
22use db::conn::sea_orm::sea_query::OnConflict;
33use db::conn::sea_orm::*;
44use db::issue::issue_watch as watch_entity;
5use db::user::users_loader::load_user;
6use export::user::basic::UserBasicInfo;
57use time::OffsetDateTime;
68use uuid::Uuid;
79
810use crate::error::{IssueError, IssueResult};
911use crate::features::IssueService;
12use crate::features::issue_watch::WatchView;
1013use crate::metrics;
1114
1215pub fn require_authenticated(actor: Uuid) -> IssueResult<()> {
@@ -76,7 +79,8 @@
7679 )
7780 .exec_with_returning(&db)
7881 .await?;
79 Ok(saved.into())
82 let user = load_user(&svc.db, actor).await?.map(UserBasicInfo::from);
83 Ok(WatchView::from_model(saved, user))
8084 }
8185 .await;
8286 metrics::record_outcome(metrics::FEATURE_ISSUE_WATCHES_UPSERT, &result);
Modified lib/issues/features/issue_watch/impls.rs lib/issues/features/issue_watch/impls.rs +5 −3 View lib/issues/features/issue_watch/impls.rs
@@ -1,3 +1,5 @@
1use db::user::users_loader::load_user;
2use export::user::basic::UserBasicInfo;
13use uuid::Uuid;
24
35use crate::error::IssueResult;
@@ -13,9 +15,9 @@
1315 _channel_id: Uuid,
1416 issue_id: Uuid,
1517 ) -> IssueResult<WatchView> {
16 usecase::load_my_watch(self, issue_id, actor)
17 .await
18 .map(WatchView::from)
18 let row = usecase::load_my_watch(self, issue_id, actor).await?;
19 let user = load_user(&self.db, actor).await?.map(UserBasicInfo::from);
20 Ok(WatchView::from_model(row, user))
1921 }
2022
2123 pub async fn upsert_watch(
Modified lib/issues/features/authz.rs lib/issues/features/authz.rs +2 −6 View lib/issues/features/authz.rs
@@ -40,11 +40,7 @@
4040
4141/// - Site admins: always allowed.
4242/// - Channel owner (top-level / space-less channel): allowed.
43pub async fn authorize_issue(
44 svc: &IssueService,
45 actor: Uuid,
46 channel_id: Uuid,
47) -> IssueResult<()> {
43pub async fn authorize_issue(svc: &IssueService, actor: Uuid, channel_id: Uuid) -> IssueResult<()> {
4844 if actor.is_nil() {
4945 return Err(IssueError::Unauthorized(
5046 "authentication required".to_string(),
@@ -82,4 +78,4 @@
8278 Err(IssueError::Forbidden(format!(
8379 "caller cannot mutate channel {channel_id}"
8480 )))
85}
No newline at end of file
81}
Modified lib/issues/handler/issue/mod.rs lib/issues/handler/issue/mod.rs +1 −0 View lib/issues/handler/issue/mod.rs
@@ -2,4 +2,5 @@
22pub(crate) mod create;
33pub(crate) mod delete;
44pub(crate) mod get;
5pub(crate) mod reopen;
56pub(crate) mod update;
Modified lib/issues/handler/openapi.rs lib/issues/handler/openapi.rs +1 −0 View lib/issues/handler/openapi.rs
@@ -37,6 +37,7 @@
3737 crate::handler::issue::update::handler,
3838 crate::handler::issue::delete::handler,
3939 crate::handler::issue::close::handler,
40 crate::handler::issue::reopen::handler,
4041
4142 crate::handler::comment::create::handler,
4243 crate::handler::comment::list::handler,
Modified lib/issues/handler/routes.rs lib/issues/handler/routes.rs +8 −1 View lib/issues/handler/routes.rs
@@ -11,9 +11,12 @@
1111 .route("/issues", post(super::issues::create::handler))
1212 .route("/issues", get(super::issues::list::handler))
1313 .route("/issues/{space_name}", get(super::issues::get::handler))
14 .route("/issues/{space_name}", patch(super::issues::update::handler))
1514 .route(
1615 "/issues/{space_name}",
16 patch(super::issues::update::handler),
17 )
18 .route(
19 "/issues/{space_name}",
1720 delete(super::issues::delete::handler),
1821 )
1922 .route("/issues/{space_name}", post(super::issue::create::handler))
@@ -28,6 +31,10 @@
2831 post(super::issue::close::handler),
2932 )
3033 .route(
34 "/issues/{space_name}/{issue_id}/reopen",
35 post(super::issue::reopen::handler),
36 )
37 .route(
3138 "/issues/{space_name}/{issue_id}/comments",
3239 get(super::comment::list::handler).post(super::comment::create::handler),
3340 )
Modified lib/issues/tests/issues/get.rs lib/issues/tests/issues/get.rs +1 −5 View lib/issues/tests/issues/get.rs
@@ -2,9 +2,5 @@
22
33#[tokio::test]
44async fn get_issue_requires_auth() {
5 expect_unauthorized(
6 &format!("/issues/{SPACE_NAME}/{ISSUE_ID}"),
7 "GET",
8 )
9 .await;
5 expect_unauthorized(&format!("/issues/{SPACE_NAME}/{ISSUE_ID}"), "GET").await;
106}