]> Untitled Git - lemmy.git/blob - crates/db_views/src/private_message_report_view.rs
Add diesel_async, get rid of blocking function (#2510)
[lemmy.git] / crates / db_views / src / private_message_report_view.rs
1 use crate::structs::PrivateMessageReportView;
2 use diesel::{result::Error, ExpressionMethods, JoinOnDsl, NullableExpressionMethods, QueryDsl};
3 use diesel_async::RunQueryDsl;
4 use lemmy_db_schema::{
5   newtypes::PrivateMessageReportId,
6   schema::{person, private_message, private_message_report},
7   source::{
8     person::{Person, PersonSafe},
9     private_message::PrivateMessage,
10     private_message_report::PrivateMessageReport,
11   },
12   traits::{ToSafe, ViewToVec},
13   utils::{get_conn, limit_and_offset, DbPool},
14 };
15 use typed_builder::TypedBuilder;
16
17 type PrivateMessageReportViewTuple = (
18   PrivateMessageReport,
19   PrivateMessage,
20   PersonSafe,
21   PersonSafe,
22   Option<PersonSafe>,
23 );
24
25 impl PrivateMessageReportView {
26   /// returns the PrivateMessageReportView for the provided report_id
27   ///
28   /// * `report_id` - the report id to obtain
29   pub async fn read(pool: &DbPool, report_id: PrivateMessageReportId) -> Result<Self, Error> {
30     let conn = &mut get_conn(pool).await?;
31     let (person_alias_1, person_alias_2) = diesel::alias!(person as person1, person as person2);
32
33     let (private_message_report, private_message, private_message_creator, creator, resolver) =
34       private_message_report::table
35         .find(report_id)
36         .inner_join(private_message::table)
37         .inner_join(person::table.on(private_message::creator_id.eq(person::id)))
38         .inner_join(
39           person_alias_1
40             .on(private_message_report::creator_id.eq(person_alias_1.field(person::id))),
41         )
42         .left_join(
43           person_alias_2.on(
44             private_message_report::resolver_id.eq(person_alias_2.field(person::id).nullable()),
45           ),
46         )
47         .select((
48           private_message_report::all_columns,
49           private_message::all_columns,
50           Person::safe_columns_tuple(),
51           person_alias_1.fields(Person::safe_columns_tuple()),
52           person_alias_2
53             .fields(Person::safe_columns_tuple())
54             .nullable(),
55         ))
56         .first::<PrivateMessageReportViewTuple>(conn)
57         .await?;
58
59     Ok(Self {
60       private_message_report,
61       private_message,
62       private_message_creator,
63       creator,
64       resolver,
65     })
66   }
67
68   /// Returns the current unresolved post report count for the communities you mod
69   pub async fn get_report_count(pool: &DbPool) -> Result<i64, Error> {
70     use diesel::dsl::*;
71     let conn = &mut get_conn(pool).await?;
72
73     private_message_report::table
74       .inner_join(private_message::table)
75       .filter(private_message_report::resolved.eq(false))
76       .into_boxed()
77       .select(count(private_message_report::id))
78       .first::<i64>(conn)
79       .await
80   }
81 }
82
83 #[derive(TypedBuilder)]
84 #[builder(field_defaults(default))]
85 pub struct PrivateMessageReportQuery<'a> {
86   #[builder(!default)]
87   pool: &'a DbPool,
88   page: Option<i64>,
89   limit: Option<i64>,
90   unresolved_only: Option<bool>,
91 }
92
93 impl<'a> PrivateMessageReportQuery<'a> {
94   pub async fn list(self) -> Result<Vec<PrivateMessageReportView>, Error> {
95     let conn = &mut get_conn(self.pool).await?;
96     let (person_alias_1, person_alias_2) = diesel::alias!(person as person1, person as person2);
97
98     let mut query = private_message_report::table
99       .inner_join(private_message::table)
100       .inner_join(person::table.on(private_message::creator_id.eq(person::id)))
101       .inner_join(
102         person_alias_1.on(private_message_report::creator_id.eq(person_alias_1.field(person::id))),
103       )
104       .left_join(
105         person_alias_2
106           .on(private_message_report::resolver_id.eq(person_alias_2.field(person::id).nullable())),
107       )
108       .select((
109         private_message_report::all_columns,
110         private_message::all_columns,
111         Person::safe_columns_tuple(),
112         person_alias_1.fields(Person::safe_columns_tuple()),
113         person_alias_2
114           .fields(Person::safe_columns_tuple())
115           .nullable(),
116       ))
117       .into_boxed();
118
119     if self.unresolved_only.unwrap_or(true) {
120       query = query.filter(private_message_report::resolved.eq(false));
121     }
122
123     let (limit, offset) = limit_and_offset(self.page, self.limit)?;
124
125     query = query
126       .order_by(private_message::published.desc())
127       .limit(limit)
128       .offset(offset);
129
130     let res = query.load::<PrivateMessageReportViewTuple>(conn).await?;
131
132     Ok(PrivateMessageReportView::from_tuple_to_vec(res))
133   }
134 }
135
136 impl ViewToVec for PrivateMessageReportView {
137   type DbTuple = PrivateMessageReportViewTuple;
138   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
139     items
140       .into_iter()
141       .map(|a| Self {
142         private_message_report: a.0,
143         private_message: a.1,
144         private_message_creator: a.2,
145         creator: a.3,
146         resolver: a.4,
147       })
148       .collect::<Vec<Self>>()
149   }
150 }
151
152 #[cfg(test)]
153 mod tests {
154   use crate::private_message_report_view::PrivateMessageReportQuery;
155   use lemmy_db_schema::{
156     source::{
157       instance::Instance,
158       person::{Person, PersonInsertForm},
159       private_message::{PrivateMessage, PrivateMessageInsertForm},
160       private_message_report::{PrivateMessageReport, PrivateMessageReportForm},
161     },
162     traits::{Crud, Reportable},
163     utils::build_db_pool_for_tests,
164   };
165   use serial_test::serial;
166
167   #[tokio::test]
168   #[serial]
169   async fn test_crud() {
170     let pool = &build_db_pool_for_tests().await;
171
172     let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
173
174     let new_person_1 = PersonInsertForm::builder()
175       .name("timmy_mrv".into())
176       .public_key("pubkey".to_string())
177       .instance_id(inserted_instance.id)
178       .build();
179     let inserted_timmy = Person::create(pool, &new_person_1).await.unwrap();
180
181     let new_person_2 = PersonInsertForm::builder()
182       .name("jessica_mrv".into())
183       .public_key("pubkey".to_string())
184       .instance_id(inserted_instance.id)
185       .build();
186     let inserted_jessica = Person::create(pool, &new_person_2).await.unwrap();
187
188     // timmy sends private message to jessica
189     let pm_form = PrivateMessageInsertForm::builder()
190       .creator_id(inserted_timmy.id)
191       .recipient_id(inserted_jessica.id)
192       .content("something offensive".to_string())
193       .build();
194     let pm = PrivateMessage::create(pool, &pm_form).await.unwrap();
195
196     // jessica reports private message
197     let pm_report_form = PrivateMessageReportForm {
198       creator_id: inserted_jessica.id,
199       original_pm_text: pm.content.clone(),
200       private_message_id: pm.id,
201       reason: "its offensive".to_string(),
202     };
203     let pm_report = PrivateMessageReport::report(pool, &pm_report_form)
204       .await
205       .unwrap();
206
207     let reports = PrivateMessageReportQuery::builder()
208       .pool(pool)
209       .build()
210       .list()
211       .await
212       .unwrap();
213     assert_eq!(1, reports.len());
214     assert!(!reports[0].private_message_report.resolved);
215     assert_eq!(inserted_timmy.name, reports[0].private_message_creator.name);
216     assert_eq!(inserted_jessica.name, reports[0].creator.name);
217     assert_eq!(pm_report.reason, reports[0].private_message_report.reason);
218     assert_eq!(pm.content, reports[0].private_message.content);
219
220     let new_person_3 = PersonInsertForm::builder()
221       .name("admin_mrv".into())
222       .public_key("pubkey".to_string())
223       .instance_id(inserted_instance.id)
224       .build();
225     let inserted_admin = Person::create(pool, &new_person_3).await.unwrap();
226
227     // admin resolves the report (after taking appropriate action)
228     PrivateMessageReport::resolve(pool, pm_report.id, inserted_admin.id)
229       .await
230       .unwrap();
231
232     let reports = PrivateMessageReportQuery::builder()
233       .pool(pool)
234       .unresolved_only(Some(false))
235       .build()
236       .list()
237       .await
238       .unwrap();
239     assert_eq!(1, reports.len());
240     assert!(reports[0].private_message_report.resolved);
241     assert!(reports[0].resolver.is_some());
242     assert_eq!(
243       inserted_admin.name,
244       reports[0].resolver.as_ref().unwrap().name
245     );
246
247     Instance::delete(pool, inserted_instance.id).await.unwrap();
248   }
249 }