]> Untitled Git - lemmy.git/blob - crates/db_views/src/post_report_view.rs
Add both (De)Serialize to all models (#1851)
[lemmy.git] / crates / db_views / src / post_report_view.rs
1 use diesel::{result::Error, *};
2 use lemmy_db_queries::{
3   aggregates::post_aggregates::PostAggregates,
4   limit_and_offset,
5   MaybeOptional,
6   ToSafe,
7   ViewToVec,
8 };
9 use lemmy_db_schema::{
10   schema::{
11     community,
12     community_moderator,
13     community_person_ban,
14     person,
15     person_alias_1,
16     person_alias_2,
17     post,
18     post_aggregates,
19     post_like,
20     post_report,
21   },
22   source::{
23     community::{Community, CommunityPersonBan, CommunitySafe},
24     person::{Person, PersonAlias1, PersonAlias2, PersonSafe, PersonSafeAlias1, PersonSafeAlias2},
25     post::Post,
26     post_report::PostReport,
27   },
28   CommunityId,
29   PersonId,
30   PostReportId,
31 };
32 use serde::{Deserialize, Serialize};
33
34 #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
35 pub struct PostReportView {
36   pub post_report: PostReport,
37   pub post: Post,
38   pub community: CommunitySafe,
39   pub creator: PersonSafe,
40   pub post_creator: PersonSafeAlias1,
41   pub creator_banned_from_community: bool,
42   pub my_vote: Option<i16>,
43   pub counts: PostAggregates,
44   pub resolver: Option<PersonSafeAlias2>,
45 }
46
47 type PostReportViewTuple = (
48   PostReport,
49   Post,
50   CommunitySafe,
51   PersonSafe,
52   PersonSafeAlias1,
53   Option<CommunityPersonBan>,
54   Option<i16>,
55   PostAggregates,
56   Option<PersonSafeAlias2>,
57 );
58
59 impl PostReportView {
60   /// returns the PostReportView for the provided report_id
61   ///
62   /// * `report_id` - the report id to obtain
63   pub fn read(
64     conn: &PgConnection,
65     report_id: PostReportId,
66     my_person_id: PersonId,
67   ) -> Result<Self, Error> {
68     let (
69       post_report,
70       post,
71       community,
72       creator,
73       post_creator,
74       creator_banned_from_community,
75       post_like,
76       counts,
77       resolver,
78     ) = post_report::table
79       .find(report_id)
80       .inner_join(post::table)
81       .inner_join(community::table.on(post::community_id.eq(community::id)))
82       .inner_join(person::table.on(post_report::creator_id.eq(person::id)))
83       .inner_join(person_alias_1::table.on(post::creator_id.eq(person_alias_1::id)))
84       .left_join(
85         community_person_ban::table.on(
86           post::community_id
87             .eq(community_person_ban::community_id)
88             .and(community_person_ban::person_id.eq(post::creator_id)),
89         ),
90       )
91       .left_join(
92         post_like::table.on(
93           post::id
94             .eq(post_like::post_id)
95             .and(post_like::person_id.eq(my_person_id)),
96         ),
97       )
98       .inner_join(post_aggregates::table.on(post_report::post_id.eq(post_aggregates::post_id)))
99       .left_join(
100         person_alias_2::table.on(post_report::resolver_id.eq(person_alias_2::id.nullable())),
101       )
102       .select((
103         post_report::all_columns,
104         post::all_columns,
105         Community::safe_columns_tuple(),
106         Person::safe_columns_tuple(),
107         PersonAlias1::safe_columns_tuple(),
108         community_person_ban::all_columns.nullable(),
109         post_like::score.nullable(),
110         post_aggregates::all_columns,
111         PersonAlias2::safe_columns_tuple().nullable(),
112       ))
113       .first::<PostReportViewTuple>(conn)?;
114
115     let my_vote = if post_like.is_none() { None } else { post_like };
116
117     Ok(Self {
118       post_report,
119       post,
120       community,
121       creator,
122       post_creator,
123       creator_banned_from_community: creator_banned_from_community.is_some(),
124       my_vote,
125       counts,
126       resolver,
127     })
128   }
129
130   /// returns the current unresolved post report count for the communities you mod
131   pub fn get_report_count(
132     conn: &PgConnection,
133     my_person_id: PersonId,
134     admin: bool,
135     community_id: Option<CommunityId>,
136   ) -> Result<i64, Error> {
137     use diesel::dsl::*;
138     let mut query = post_report::table
139       .inner_join(post::table)
140       // Test this join
141       .inner_join(
142         community_moderator::table.on(community_moderator::community_id.eq(post::community_id)),
143       )
144       .filter(post_report::resolved.eq(false))
145       .into_boxed();
146
147     // If its not an admin, get only the ones you mod
148     if !admin {
149       query = query.filter(community_moderator::person_id.eq(my_person_id));
150     }
151
152     if let Some(community_id) = community_id {
153       query = query.filter(post::community_id.eq(community_id))
154     }
155
156     query.select(count(post_report::id)).first::<i64>(conn)
157   }
158 }
159
160 pub struct PostReportQueryBuilder<'a> {
161   conn: &'a PgConnection,
162   my_person_id: PersonId,
163   admin: bool,
164   community_id: Option<CommunityId>,
165   page: Option<i64>,
166   limit: Option<i64>,
167   unresolved_only: Option<bool>,
168 }
169
170 impl<'a> PostReportQueryBuilder<'a> {
171   pub fn create(conn: &'a PgConnection, my_person_id: PersonId, admin: bool) -> Self {
172     PostReportQueryBuilder {
173       conn,
174       my_person_id,
175       admin,
176       community_id: None,
177       page: None,
178       limit: None,
179       unresolved_only: Some(true),
180     }
181   }
182
183   pub fn community_id<T: MaybeOptional<CommunityId>>(mut self, community_id: T) -> Self {
184     self.community_id = community_id.get_optional();
185     self
186   }
187
188   pub fn page<T: MaybeOptional<i64>>(mut self, page: T) -> Self {
189     self.page = page.get_optional();
190     self
191   }
192
193   pub fn limit<T: MaybeOptional<i64>>(mut self, limit: T) -> Self {
194     self.limit = limit.get_optional();
195     self
196   }
197
198   pub fn unresolved_only<T: MaybeOptional<bool>>(mut self, unresolved_only: T) -> Self {
199     self.unresolved_only = unresolved_only.get_optional();
200     self
201   }
202
203   pub fn list(self) -> Result<Vec<PostReportView>, Error> {
204     let mut query = post_report::table
205       .inner_join(post::table)
206       .inner_join(community::table.on(post::community_id.eq(community::id)))
207       .inner_join(person::table.on(post_report::creator_id.eq(person::id)))
208       .inner_join(person_alias_1::table.on(post::creator_id.eq(person_alias_1::id)))
209       .inner_join(
210         community_moderator::table.on(community_moderator::community_id.eq(post::community_id)),
211       )
212       .left_join(
213         community_person_ban::table.on(
214           post::community_id
215             .eq(community_person_ban::community_id)
216             .and(community_person_ban::person_id.eq(post::creator_id)),
217         ),
218       )
219       .left_join(
220         post_like::table.on(
221           post::id
222             .eq(post_like::post_id)
223             .and(post_like::person_id.eq(self.my_person_id)),
224         ),
225       )
226       .inner_join(post_aggregates::table.on(post_report::post_id.eq(post_aggregates::post_id)))
227       .left_join(
228         person_alias_2::table.on(post_report::resolver_id.eq(person_alias_2::id.nullable())),
229       )
230       .select((
231         post_report::all_columns,
232         post::all_columns,
233         Community::safe_columns_tuple(),
234         Person::safe_columns_tuple(),
235         PersonAlias1::safe_columns_tuple(),
236         community_person_ban::all_columns.nullable(),
237         post_like::score.nullable(),
238         post_aggregates::all_columns,
239         PersonAlias2::safe_columns_tuple().nullable(),
240       ))
241       .into_boxed();
242
243     // If its not an admin, get only the ones you mod
244     if !self.admin {
245       query = query.filter(community_moderator::person_id.eq(self.my_person_id));
246     }
247
248     if let Some(community_id) = self.community_id {
249       query = query.filter(post::community_id.eq(community_id));
250     }
251
252     if self.unresolved_only.unwrap_or(false) {
253       query = query.filter(post_report::resolved.eq(false));
254     }
255
256     let (limit, offset) = limit_and_offset(self.page, self.limit);
257
258     let res = query
259       .order_by(post_report::published.asc())
260       .limit(limit)
261       .offset(offset)
262       .load::<PostReportViewTuple>(self.conn)?;
263
264     Ok(PostReportView::from_tuple_to_vec(res))
265   }
266 }
267
268 impl ViewToVec for PostReportView {
269   type DbTuple = PostReportViewTuple;
270   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
271     items
272       .iter()
273       .map(|a| Self {
274         post_report: a.0.to_owned(),
275         post: a.1.to_owned(),
276         community: a.2.to_owned(),
277         creator: a.3.to_owned(),
278         post_creator: a.4.to_owned(),
279         creator_banned_from_community: a.5.is_some(),
280         my_vote: a.6,
281         counts: a.7.to_owned(),
282         resolver: a.8.to_owned(),
283       })
284       .collect::<Vec<Self>>()
285   }
286 }
287
288 #[cfg(test)]
289 mod tests {
290   use crate::post_report_view::{PostReportQueryBuilder, PostReportView};
291   use lemmy_db_queries::{
292     aggregates::post_aggregates::PostAggregates,
293     establish_unpooled_connection,
294     Crud,
295     Joinable,
296     Reportable,
297   };
298   use lemmy_db_schema::source::{
299     community::*,
300     person::*,
301     post::*,
302     post_report::{PostReport, PostReportForm},
303   };
304   use serial_test::serial;
305
306   #[test]
307   #[serial]
308   fn test_crud() {
309     let conn = establish_unpooled_connection();
310
311     let new_person = PersonForm {
312       name: "timmy_prv".into(),
313       ..PersonForm::default()
314     };
315
316     let inserted_timmy = Person::create(&conn, &new_person).unwrap();
317
318     let new_person_2 = PersonForm {
319       name: "sara_prv".into(),
320       ..PersonForm::default()
321     };
322
323     let inserted_sara = Person::create(&conn, &new_person_2).unwrap();
324
325     // Add a third person, since new ppl can only report something once.
326     let new_person_3 = PersonForm {
327       name: "jessica_prv".into(),
328       ..PersonForm::default()
329     };
330
331     let inserted_jessica = Person::create(&conn, &new_person_3).unwrap();
332
333     let new_community = CommunityForm {
334       name: "test community prv".to_string(),
335       title: "nada".to_owned(),
336       ..CommunityForm::default()
337     };
338
339     let inserted_community = Community::create(&conn, &new_community).unwrap();
340
341     // Make timmy a mod
342     let timmy_moderator_form = CommunityModeratorForm {
343       community_id: inserted_community.id,
344       person_id: inserted_timmy.id,
345     };
346
347     let _inserted_moderator = CommunityModerator::join(&conn, &timmy_moderator_form).unwrap();
348
349     let new_post = PostForm {
350       name: "A test post crv".into(),
351       creator_id: inserted_timmy.id,
352       community_id: inserted_community.id,
353       ..PostForm::default()
354     };
355
356     let inserted_post = Post::create(&conn, &new_post).unwrap();
357
358     // sara reports
359     let sara_report_form = PostReportForm {
360       creator_id: inserted_sara.id,
361       post_id: inserted_post.id,
362       original_post_name: "Orig post".into(),
363       original_post_url: None,
364       original_post_body: None,
365       reason: "from sara".into(),
366     };
367
368     let inserted_sara_report = PostReport::report(&conn, &sara_report_form).unwrap();
369
370     // jessica reports
371     let jessica_report_form = PostReportForm {
372       creator_id: inserted_jessica.id,
373       post_id: inserted_post.id,
374       original_post_name: "Orig post".into(),
375       original_post_url: None,
376       original_post_body: None,
377       reason: "from jessica".into(),
378     };
379
380     let inserted_jessica_report = PostReport::report(&conn, &jessica_report_form).unwrap();
381
382     let agg = PostAggregates::read(&conn, inserted_post.id).unwrap();
383
384     let read_jessica_report_view =
385       PostReportView::read(&conn, inserted_jessica_report.id, inserted_timmy.id).unwrap();
386     let expected_jessica_report_view = PostReportView {
387       post_report: inserted_jessica_report.to_owned(),
388       post: inserted_post.to_owned(),
389       community: CommunitySafe {
390         id: inserted_community.id,
391         name: inserted_community.name,
392         icon: None,
393         removed: false,
394         deleted: false,
395         nsfw: false,
396         actor_id: inserted_community.actor_id.to_owned(),
397         local: true,
398         title: inserted_community.title,
399         description: None,
400         updated: None,
401         banner: None,
402         published: inserted_community.published,
403       },
404       creator: PersonSafe {
405         id: inserted_jessica.id,
406         name: inserted_jessica.name,
407         display_name: None,
408         published: inserted_jessica.published,
409         avatar: None,
410         actor_id: inserted_jessica.actor_id.to_owned(),
411         local: true,
412         banned: false,
413         deleted: false,
414         admin: false,
415         bot_account: false,
416         bio: None,
417         banner: None,
418         updated: None,
419         inbox_url: inserted_jessica.inbox_url.to_owned(),
420         shared_inbox_url: None,
421         matrix_user_id: None,
422       },
423       post_creator: PersonSafeAlias1 {
424         id: inserted_timmy.id,
425         name: inserted_timmy.name.to_owned(),
426         display_name: None,
427         published: inserted_timmy.published,
428         avatar: None,
429         actor_id: inserted_timmy.actor_id.to_owned(),
430         local: true,
431         banned: false,
432         deleted: false,
433         admin: false,
434         bot_account: false,
435         bio: None,
436         banner: None,
437         updated: None,
438         inbox_url: inserted_timmy.inbox_url.to_owned(),
439         shared_inbox_url: None,
440         matrix_user_id: None,
441       },
442       creator_banned_from_community: false,
443       my_vote: None,
444       counts: PostAggregates {
445         id: agg.id,
446         post_id: inserted_post.id,
447         comments: 0,
448         score: 0,
449         upvotes: 0,
450         downvotes: 0,
451         stickied: false,
452         published: agg.published,
453         newest_comment_time_necro: inserted_post.published,
454         newest_comment_time: inserted_post.published,
455       },
456       resolver: None,
457     };
458
459     assert_eq!(read_jessica_report_view, expected_jessica_report_view);
460
461     let mut expected_sara_report_view = expected_jessica_report_view.clone();
462     expected_sara_report_view.post_report = inserted_sara_report;
463     expected_sara_report_view.my_vote = None;
464     expected_sara_report_view.creator = PersonSafe {
465       id: inserted_sara.id,
466       name: inserted_sara.name,
467       display_name: None,
468       published: inserted_sara.published,
469       avatar: None,
470       actor_id: inserted_sara.actor_id.to_owned(),
471       local: true,
472       banned: false,
473       deleted: false,
474       admin: false,
475       bot_account: false,
476       bio: None,
477       banner: None,
478       updated: None,
479       inbox_url: inserted_sara.inbox_url.to_owned(),
480       shared_inbox_url: None,
481       matrix_user_id: None,
482     };
483
484     // Do a batch read of timmys reports
485     let reports = PostReportQueryBuilder::create(&conn, inserted_timmy.id, false)
486       .list()
487       .unwrap();
488
489     assert_eq!(
490       reports,
491       [
492         expected_sara_report_view.to_owned(),
493         expected_jessica_report_view.to_owned()
494       ]
495     );
496
497     // Make sure the counts are correct
498     let report_count =
499       PostReportView::get_report_count(&conn, inserted_timmy.id, false, None).unwrap();
500     assert_eq!(2, report_count);
501
502     // Try to resolve the report
503     PostReport::resolve(&conn, inserted_jessica_report.id, inserted_timmy.id).unwrap();
504     let read_jessica_report_view_after_resolve =
505       PostReportView::read(&conn, inserted_jessica_report.id, inserted_timmy.id).unwrap();
506
507     let mut expected_jessica_report_view_after_resolve = expected_jessica_report_view;
508     expected_jessica_report_view_after_resolve
509       .post_report
510       .resolved = true;
511     expected_jessica_report_view_after_resolve
512       .post_report
513       .resolver_id = Some(inserted_timmy.id);
514     expected_jessica_report_view_after_resolve
515       .post_report
516       .updated = read_jessica_report_view_after_resolve.post_report.updated;
517     expected_jessica_report_view_after_resolve.resolver = Some(PersonSafeAlias2 {
518       id: inserted_timmy.id,
519       name: inserted_timmy.name.to_owned(),
520       display_name: None,
521       published: inserted_timmy.published,
522       avatar: None,
523       actor_id: inserted_timmy.actor_id.to_owned(),
524       local: true,
525       banned: false,
526       deleted: false,
527       admin: false,
528       bot_account: false,
529       bio: None,
530       banner: None,
531       updated: None,
532       inbox_url: inserted_timmy.inbox_url.to_owned(),
533       shared_inbox_url: None,
534       matrix_user_id: None,
535     });
536
537     assert_eq!(
538       read_jessica_report_view_after_resolve,
539       expected_jessica_report_view_after_resolve
540     );
541
542     // Do a batch read of timmys reports
543     // It should only show saras, which is unresolved
544     let reports_after_resolve = PostReportQueryBuilder::create(&conn, inserted_timmy.id, false)
545       .list()
546       .unwrap();
547     assert_eq!(reports_after_resolve[0], expected_sara_report_view);
548
549     // Make sure the counts are correct
550     let report_count_after_resolved =
551       PostReportView::get_report_count(&conn, inserted_timmy.id, false, None).unwrap();
552     assert_eq!(1, report_count_after_resolved);
553
554     Person::delete(&conn, inserted_timmy.id).unwrap();
555     Person::delete(&conn, inserted_sara.id).unwrap();
556     Person::delete(&conn, inserted_jessica.id).unwrap();
557     Community::delete(&conn, inserted_community.id).unwrap();
558   }
559 }