]> Untitled Git - lemmy.git/blob - crates/db_views/src/comment_report_view.rs
Various pedantic clippy fixes (#2568)
[lemmy.git] / crates / db_views / src / comment_report_view.rs
1 use crate::structs::CommentReportView;
2 use diesel::{
3   dsl::now,
4   result::Error,
5   BoolExpressionMethods,
6   ExpressionMethods,
7   JoinOnDsl,
8   NullableExpressionMethods,
9   QueryDsl,
10 };
11 use diesel_async::RunQueryDsl;
12 use lemmy_db_schema::{
13   aggregates::structs::CommentAggregates,
14   newtypes::{CommentReportId, CommunityId, PersonId},
15   schema::{
16     comment,
17     comment_aggregates,
18     comment_like,
19     comment_report,
20     community,
21     community_moderator,
22     community_person_ban,
23     person,
24     post,
25   },
26   source::{
27     comment::Comment,
28     comment_report::CommentReport,
29     community::{Community, CommunityPersonBan, CommunitySafe},
30     person::{Person, PersonSafe},
31     post::Post,
32   },
33   traits::{ToSafe, ViewToVec},
34   utils::{get_conn, limit_and_offset, DbPool},
35 };
36 use typed_builder::TypedBuilder;
37
38 type CommentReportViewTuple = (
39   CommentReport,
40   Comment,
41   Post,
42   CommunitySafe,
43   PersonSafe,
44   PersonSafe,
45   CommentAggregates,
46   Option<CommunityPersonBan>,
47   Option<i16>,
48   Option<PersonSafe>,
49 );
50
51 impl CommentReportView {
52   /// returns the CommentReportView for the provided report_id
53   ///
54   /// * `report_id` - the report id to obtain
55   pub async fn read(
56     pool: &DbPool,
57     report_id: CommentReportId,
58     my_person_id: PersonId,
59   ) -> Result<Self, Error> {
60     let conn = &mut get_conn(pool).await?;
61
62     let (person_alias_1, person_alias_2) = diesel::alias!(person as person1, person as person2);
63
64     let (
65       comment_report,
66       comment,
67       post,
68       community,
69       creator,
70       comment_creator,
71       counts,
72       creator_banned_from_community,
73       comment_like,
74       resolver,
75     ) = comment_report::table
76       .find(report_id)
77       .inner_join(comment::table)
78       .inner_join(post::table.on(comment::post_id.eq(post::id)))
79       .inner_join(community::table.on(post::community_id.eq(community::id)))
80       .inner_join(person::table.on(comment_report::creator_id.eq(person::id)))
81       .inner_join(person_alias_1.on(comment::creator_id.eq(person_alias_1.field(person::id))))
82       .inner_join(
83         comment_aggregates::table.on(comment_report::comment_id.eq(comment_aggregates::comment_id)),
84       )
85       .left_join(
86         community_person_ban::table.on(
87           community::id
88             .eq(community_person_ban::community_id)
89             .and(community_person_ban::person_id.eq(comment::creator_id))
90             .and(
91               community_person_ban::expires
92                 .is_null()
93                 .or(community_person_ban::expires.gt(now)),
94             ),
95         ),
96       )
97       .left_join(
98         comment_like::table.on(
99           comment::id
100             .eq(comment_like::comment_id)
101             .and(comment_like::person_id.eq(my_person_id)),
102         ),
103       )
104       .left_join(
105         person_alias_2
106           .on(comment_report::resolver_id.eq(person_alias_2.field(person::id).nullable())),
107       )
108       .select((
109         comment_report::all_columns,
110         comment::all_columns,
111         post::all_columns,
112         Community::safe_columns_tuple(),
113         Person::safe_columns_tuple(),
114         person_alias_1.fields(Person::safe_columns_tuple()),
115         comment_aggregates::all_columns,
116         community_person_ban::all_columns.nullable(),
117         comment_like::score.nullable(),
118         person_alias_2
119           .fields(Person::safe_columns_tuple())
120           .nullable(),
121       ))
122       .first::<CommentReportViewTuple>(conn)
123       .await?;
124
125     let my_vote = comment_like;
126
127     Ok(Self {
128       comment_report,
129       comment,
130       post,
131       community,
132       creator,
133       comment_creator,
134       counts,
135       creator_banned_from_community: creator_banned_from_community.is_some(),
136       my_vote,
137       resolver,
138     })
139   }
140
141   /// Returns the current unresolved post report count for the communities you mod
142   pub async fn get_report_count(
143     pool: &DbPool,
144     my_person_id: PersonId,
145     admin: bool,
146     community_id: Option<CommunityId>,
147   ) -> Result<i64, Error> {
148     use diesel::dsl::count;
149
150     let conn = &mut get_conn(pool).await?;
151
152     let mut query = comment_report::table
153       .inner_join(comment::table)
154       .inner_join(post::table.on(comment::post_id.eq(post::id)))
155       .filter(comment_report::resolved.eq(false))
156       .into_boxed();
157
158     if let Some(community_id) = community_id {
159       query = query.filter(post::community_id.eq(community_id))
160     }
161
162     // If its not an admin, get only the ones you mod
163     if !admin {
164       query
165         .inner_join(
166           community_moderator::table.on(
167             community_moderator::community_id
168               .eq(post::community_id)
169               .and(community_moderator::person_id.eq(my_person_id)),
170           ),
171         )
172         .select(count(comment_report::id))
173         .first::<i64>(conn)
174         .await
175     } else {
176       query
177         .select(count(comment_report::id))
178         .first::<i64>(conn)
179         .await
180     }
181   }
182 }
183
184 #[derive(TypedBuilder)]
185 #[builder(field_defaults(default))]
186 pub struct CommentReportQuery<'a> {
187   #[builder(!default)]
188   pool: &'a DbPool,
189   #[builder(!default)]
190   my_person_id: PersonId,
191   #[builder(!default)]
192   admin: bool,
193   community_id: Option<CommunityId>,
194   page: Option<i64>,
195   limit: Option<i64>,
196   unresolved_only: Option<bool>,
197 }
198
199 impl<'a> CommentReportQuery<'a> {
200   pub async fn list(self) -> Result<Vec<CommentReportView>, Error> {
201     let conn = &mut get_conn(self.pool).await?;
202
203     let (person_alias_1, person_alias_2) = diesel::alias!(person as person1, person as person2);
204
205     let mut query = comment_report::table
206       .inner_join(comment::table)
207       .inner_join(post::table.on(comment::post_id.eq(post::id)))
208       .inner_join(community::table.on(post::community_id.eq(community::id)))
209       .inner_join(person::table.on(comment_report::creator_id.eq(person::id)))
210       .inner_join(person_alias_1.on(comment::creator_id.eq(person_alias_1.field(person::id))))
211       .inner_join(
212         comment_aggregates::table.on(comment_report::comment_id.eq(comment_aggregates::comment_id)),
213       )
214       .left_join(
215         community_person_ban::table.on(
216           community::id
217             .eq(community_person_ban::community_id)
218             .and(community_person_ban::person_id.eq(comment::creator_id))
219             .and(
220               community_person_ban::expires
221                 .is_null()
222                 .or(community_person_ban::expires.gt(now)),
223             ),
224         ),
225       )
226       .left_join(
227         comment_like::table.on(
228           comment::id
229             .eq(comment_like::comment_id)
230             .and(comment_like::person_id.eq(self.my_person_id)),
231         ),
232       )
233       .left_join(
234         person_alias_2
235           .on(comment_report::resolver_id.eq(person_alias_2.field(person::id).nullable())),
236       )
237       .select((
238         comment_report::all_columns,
239         comment::all_columns,
240         post::all_columns,
241         Community::safe_columns_tuple(),
242         Person::safe_columns_tuple(),
243         person_alias_1.fields(Person::safe_columns_tuple()),
244         comment_aggregates::all_columns,
245         community_person_ban::all_columns.nullable(),
246         comment_like::score.nullable(),
247         person_alias_2
248           .fields(Person::safe_columns_tuple())
249           .nullable(),
250       ))
251       .into_boxed();
252
253     if let Some(community_id) = self.community_id {
254       query = query.filter(post::community_id.eq(community_id));
255     }
256
257     if self.unresolved_only.unwrap_or(true) {
258       query = query.filter(comment_report::resolved.eq(false));
259     }
260
261     let (limit, offset) = limit_and_offset(self.page, self.limit)?;
262
263     query = query
264       .order_by(comment_report::published.desc())
265       .limit(limit)
266       .offset(offset);
267
268     // If its not an admin, get only the ones you mod
269     let res = if !self.admin {
270       query
271         .inner_join(
272           community_moderator::table.on(
273             community_moderator::community_id
274               .eq(post::community_id)
275               .and(community_moderator::person_id.eq(self.my_person_id)),
276           ),
277         )
278         .load::<CommentReportViewTuple>(conn)
279         .await?
280     } else {
281       query.load::<CommentReportViewTuple>(conn).await?
282     };
283
284     Ok(CommentReportView::from_tuple_to_vec(res))
285   }
286 }
287
288 impl ViewToVec for CommentReportView {
289   type DbTuple = CommentReportViewTuple;
290   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
291     items
292       .into_iter()
293       .map(|a| Self {
294         comment_report: a.0,
295         comment: a.1,
296         post: a.2,
297         community: a.3,
298         creator: a.4,
299         comment_creator: a.5,
300         counts: a.6,
301         creator_banned_from_community: a.7.is_some(),
302         my_vote: a.8,
303         resolver: a.9,
304       })
305       .collect::<Vec<Self>>()
306   }
307 }
308
309 #[cfg(test)]
310 mod tests {
311   use crate::comment_report_view::{CommentReportQuery, CommentReportView};
312   use lemmy_db_schema::{
313     aggregates::structs::CommentAggregates,
314     source::{
315       comment::{Comment, CommentInsertForm},
316       comment_report::{CommentReport, CommentReportForm},
317       community::{
318         Community,
319         CommunityInsertForm,
320         CommunityModerator,
321         CommunityModeratorForm,
322         CommunitySafe,
323       },
324       instance::Instance,
325       person::{Person, PersonInsertForm, PersonSafe},
326       post::{Post, PostInsertForm},
327     },
328     traits::{Crud, Joinable, Reportable},
329     utils::build_db_pool_for_tests,
330   };
331   use serial_test::serial;
332
333   #[tokio::test]
334   #[serial]
335   async fn test_crud() {
336     let pool = &build_db_pool_for_tests().await;
337
338     let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
339
340     let new_person = PersonInsertForm::builder()
341       .name("timmy_crv".into())
342       .public_key("pubkey".to_string())
343       .instance_id(inserted_instance.id)
344       .build();
345
346     let inserted_timmy = Person::create(pool, &new_person).await.unwrap();
347
348     let new_person_2 = PersonInsertForm::builder()
349       .name("sara_crv".into())
350       .public_key("pubkey".to_string())
351       .instance_id(inserted_instance.id)
352       .build();
353
354     let inserted_sara = Person::create(pool, &new_person_2).await.unwrap();
355
356     // Add a third person, since new ppl can only report something once.
357     let new_person_3 = PersonInsertForm::builder()
358       .name("jessica_crv".into())
359       .public_key("pubkey".to_string())
360       .instance_id(inserted_instance.id)
361       .build();
362
363     let inserted_jessica = Person::create(pool, &new_person_3).await.unwrap();
364
365     let new_community = CommunityInsertForm::builder()
366       .name("test community crv".to_string())
367       .title("nada".to_owned())
368       .public_key("pubkey".to_string())
369       .instance_id(inserted_instance.id)
370       .build();
371
372     let inserted_community = Community::create(pool, &new_community).await.unwrap();
373
374     // Make timmy a mod
375     let timmy_moderator_form = CommunityModeratorForm {
376       community_id: inserted_community.id,
377       person_id: inserted_timmy.id,
378     };
379
380     let _inserted_moderator = CommunityModerator::join(pool, &timmy_moderator_form)
381       .await
382       .unwrap();
383
384     let new_post = PostInsertForm::builder()
385       .name("A test post crv".into())
386       .creator_id(inserted_timmy.id)
387       .community_id(inserted_community.id)
388       .build();
389
390     let inserted_post = Post::create(pool, &new_post).await.unwrap();
391
392     let comment_form = CommentInsertForm::builder()
393       .content("A test comment 32".into())
394       .creator_id(inserted_timmy.id)
395       .post_id(inserted_post.id)
396       .build();
397
398     let inserted_comment = Comment::create(pool, &comment_form, None).await.unwrap();
399
400     // sara reports
401     let sara_report_form = CommentReportForm {
402       creator_id: inserted_sara.id,
403       comment_id: inserted_comment.id,
404       original_comment_text: "this was it at time of creation".into(),
405       reason: "from sara".into(),
406     };
407
408     let inserted_sara_report = CommentReport::report(pool, &sara_report_form)
409       .await
410       .unwrap();
411
412     // jessica reports
413     let jessica_report_form = CommentReportForm {
414       creator_id: inserted_jessica.id,
415       comment_id: inserted_comment.id,
416       original_comment_text: "this was it at time of creation".into(),
417       reason: "from jessica".into(),
418     };
419
420     let inserted_jessica_report = CommentReport::report(pool, &jessica_report_form)
421       .await
422       .unwrap();
423
424     let agg = CommentAggregates::read(pool, inserted_comment.id)
425       .await
426       .unwrap();
427
428     let read_jessica_report_view =
429       CommentReportView::read(pool, inserted_jessica_report.id, inserted_timmy.id)
430         .await
431         .unwrap();
432     let expected_jessica_report_view = CommentReportView {
433       comment_report: inserted_jessica_report.clone(),
434       comment: inserted_comment.clone(),
435       post: inserted_post,
436       community: CommunitySafe {
437         id: inserted_community.id,
438         name: inserted_community.name,
439         icon: None,
440         removed: false,
441         deleted: false,
442         nsfw: false,
443         actor_id: inserted_community.actor_id.clone(),
444         local: true,
445         title: inserted_community.title,
446         description: None,
447         updated: None,
448         banner: None,
449         hidden: false,
450         posting_restricted_to_mods: false,
451         published: inserted_community.published,
452         instance_id: inserted_instance.id,
453       },
454       creator: PersonSafe {
455         id: inserted_jessica.id,
456         name: inserted_jessica.name,
457         display_name: None,
458         published: inserted_jessica.published,
459         avatar: None,
460         actor_id: inserted_jessica.actor_id.clone(),
461         local: true,
462         banned: false,
463         deleted: false,
464         admin: false,
465         bot_account: false,
466         bio: None,
467         banner: None,
468         updated: None,
469         inbox_url: inserted_jessica.inbox_url.clone(),
470         shared_inbox_url: None,
471         matrix_user_id: None,
472         ban_expires: None,
473         instance_id: inserted_instance.id,
474       },
475       comment_creator: PersonSafe {
476         id: inserted_timmy.id,
477         name: inserted_timmy.name.clone(),
478         display_name: None,
479         published: inserted_timmy.published,
480         avatar: None,
481         actor_id: inserted_timmy.actor_id.clone(),
482         local: true,
483         banned: false,
484         deleted: false,
485         admin: false,
486         bot_account: false,
487         bio: None,
488         banner: None,
489         updated: None,
490         inbox_url: inserted_timmy.inbox_url.clone(),
491         shared_inbox_url: None,
492         matrix_user_id: None,
493         ban_expires: None,
494         instance_id: inserted_instance.id,
495       },
496       creator_banned_from_community: false,
497       counts: CommentAggregates {
498         id: agg.id,
499         comment_id: inserted_comment.id,
500         score: 0,
501         upvotes: 0,
502         downvotes: 0,
503         published: agg.published,
504         child_count: 0,
505       },
506       my_vote: None,
507       resolver: None,
508     };
509
510     assert_eq!(read_jessica_report_view, expected_jessica_report_view);
511
512     let mut expected_sara_report_view = expected_jessica_report_view.clone();
513     expected_sara_report_view.comment_report = inserted_sara_report;
514     expected_sara_report_view.creator = PersonSafe {
515       id: inserted_sara.id,
516       name: inserted_sara.name,
517       display_name: None,
518       published: inserted_sara.published,
519       avatar: None,
520       actor_id: inserted_sara.actor_id.clone(),
521       local: true,
522       banned: false,
523       deleted: false,
524       admin: false,
525       bot_account: false,
526       bio: None,
527       banner: None,
528       updated: None,
529       inbox_url: inserted_sara.inbox_url.clone(),
530       shared_inbox_url: None,
531       matrix_user_id: None,
532       ban_expires: None,
533       instance_id: inserted_instance.id,
534     };
535
536     // Do a batch read of timmys reports
537     let reports = CommentReportQuery::builder()
538       .pool(pool)
539       .my_person_id(inserted_timmy.id)
540       .admin(false)
541       .build()
542       .list()
543       .await
544       .unwrap();
545
546     assert_eq!(
547       reports,
548       [
549         expected_jessica_report_view.clone(),
550         expected_sara_report_view.clone()
551       ]
552     );
553
554     // Make sure the counts are correct
555     let report_count = CommentReportView::get_report_count(pool, inserted_timmy.id, false, None)
556       .await
557       .unwrap();
558     assert_eq!(2, report_count);
559
560     // Try to resolve the report
561     CommentReport::resolve(pool, inserted_jessica_report.id, inserted_timmy.id)
562       .await
563       .unwrap();
564     let read_jessica_report_view_after_resolve =
565       CommentReportView::read(pool, inserted_jessica_report.id, inserted_timmy.id)
566         .await
567         .unwrap();
568
569     let mut expected_jessica_report_view_after_resolve = expected_jessica_report_view;
570     expected_jessica_report_view_after_resolve
571       .comment_report
572       .resolved = true;
573     expected_jessica_report_view_after_resolve
574       .comment_report
575       .resolver_id = Some(inserted_timmy.id);
576     expected_jessica_report_view_after_resolve
577       .comment_report
578       .updated = read_jessica_report_view_after_resolve
579       .comment_report
580       .updated;
581     expected_jessica_report_view_after_resolve.resolver = Some(PersonSafe {
582       id: inserted_timmy.id,
583       name: inserted_timmy.name.clone(),
584       display_name: None,
585       published: inserted_timmy.published,
586       avatar: None,
587       actor_id: inserted_timmy.actor_id.clone(),
588       local: true,
589       banned: false,
590       deleted: false,
591       admin: false,
592       bot_account: false,
593       bio: None,
594       banner: None,
595       updated: None,
596       inbox_url: inserted_timmy.inbox_url.clone(),
597       shared_inbox_url: None,
598       matrix_user_id: None,
599       ban_expires: None,
600       instance_id: inserted_instance.id,
601     });
602
603     assert_eq!(
604       read_jessica_report_view_after_resolve,
605       expected_jessica_report_view_after_resolve
606     );
607
608     // Do a batch read of timmys reports
609     // It should only show saras, which is unresolved
610     let reports_after_resolve = CommentReportQuery::builder()
611       .pool(pool)
612       .my_person_id(inserted_timmy.id)
613       .admin(false)
614       .build()
615       .list()
616       .await
617       .unwrap();
618     assert_eq!(reports_after_resolve[0], expected_sara_report_view);
619     assert_eq!(reports_after_resolve.len(), 1);
620
621     // Make sure the counts are correct
622     let report_count_after_resolved =
623       CommentReportView::get_report_count(pool, inserted_timmy.id, false, None)
624         .await
625         .unwrap();
626     assert_eq!(1, report_count_after_resolved);
627
628     Person::delete(pool, inserted_timmy.id).await.unwrap();
629     Person::delete(pool, inserted_sara.id).await.unwrap();
630     Person::delete(pool, inserted_jessica.id).await.unwrap();
631     Community::delete(pool, inserted_community.id)
632       .await
633       .unwrap();
634     Instance::delete(pool, inserted_instance.id).await.unwrap();
635   }
636 }